55from time import perf_counter
66from typing import Any
77
8- from anthropic import Anthropic
9- from anthropic import APIConnectionError , APIError , APITimeoutError
8+ from openai import APIConnectionError , APIError , APITimeoutError , OpenAI
109from sqlalchemy .orm import Session
1110
1211from app .agent_tools .schemas import CreateContentIdeaInput , ExecutePlanInput , UpdateContentPlanInput , WebSearchInput
@@ -28,6 +27,13 @@ class ToolValidationError(ToolHandlerError):
2827 pass
2928
3029
30+ def _get_ai_client () -> OpenAI | None :
31+ api_key = Config .NIM_API_KEY .strip ()
32+ if not api_key or api_key == "your_nvidia_nim_api_key" :
33+ return None
34+ return OpenAI (api_key = api_key , base_url = Config .NIM_BASE_URL )
35+
36+
3137def handle_create_content_idea (payload : CreateContentIdeaInput ) -> dict [str , Any ]:
3238 return _run_tool ("create_content_idea" , payload , _create_content_idea )
3339
@@ -260,8 +266,8 @@ def _generate_plan_with_ai(
260266 constraints : dict [str , Any ] | None ,
261267 user_context : dict [str , Any ],
262268) -> dict [str , Any ] | None :
263- api_key = Config . ANTHROPIC_API_KEY . strip ()
264- if not api_key or api_key == "your_anthropic_api_key" :
269+ client = _get_ai_client ()
270+ if client is None :
265271 return None
266272
267273 prompt = (
@@ -277,13 +283,14 @@ def _generate_plan_with_ai(
277283 )
278284
279285 try :
280- client = Anthropic (api_key = api_key )
281- response = client .messages .create (
282- model = Config .ANTHROPIC_MODEL ,
283- max_tokens = Config .ANTHROPIC_MAX_TOKENS ,
284- temperature = Config .ANTHROPIC_TEMPERATURE ,
285- system = "You are a precise content strategist. Follow output format exactly." ,
286- messages = [{"role" : "user" , "content" : prompt }],
286+ response = client .chat .completions .create (
287+ model = Config .NIM_MODEL ,
288+ max_tokens = Config .AI_MAX_TOKENS ,
289+ temperature = Config .AI_TEMPERATURE ,
290+ messages = [
291+ {"role" : "system" , "content" : "You are a precise content strategist. Follow output format exactly." },
292+ {"role" : "user" , "content" : prompt },
293+ ],
287294 )
288295 text = _extract_text_from_response (response )
289296 return _parse_json_from_text (text )
@@ -297,8 +304,8 @@ def _generate_blog_with_ai(
297304 output_format : str ,
298305 user_context : dict [str , Any ],
299306) -> dict [str , Any ] | None :
300- api_key = Config . ANTHROPIC_API_KEY . strip ()
301- if not api_key or api_key == "your_anthropic_api_key" :
307+ client = _get_ai_client ()
308+ if client is None :
302309 return None
303310
304311 prompt = (
@@ -316,13 +323,14 @@ def _generate_blog_with_ai(
316323 )
317324
318325 try :
319- client = Anthropic (api_key = api_key )
320- response = client .messages .create (
321- model = Config .ANTHROPIC_MODEL ,
322- max_tokens = Config .ANTHROPIC_MAX_TOKENS ,
323- temperature = Config .ANTHROPIC_TEMPERATURE ,
324- system = "You are a senior blog writer. Follow output format exactly." ,
325- messages = [{"role" : "user" , "content" : prompt }],
326+ response = client .chat .completions .create (
327+ model = Config .NIM_MODEL ,
328+ max_tokens = Config .AI_MAX_TOKENS ,
329+ temperature = Config .AI_TEMPERATURE ,
330+ messages = [
331+ {"role" : "system" , "content" : "You are a senior blog writer. Follow output format exactly." },
332+ {"role" : "user" , "content" : prompt },
333+ ],
326334 )
327335 text = _extract_text_from_response (response )
328336 parsed = _coerce_blog_payload_from_text (text , plan )
@@ -343,7 +351,7 @@ def _generate_blog_with_ai(
343351
344352
345353def _generate_blog_markdown_retry_with_ai (
346- client : Anthropic ,
354+ client : OpenAI ,
347355 plan : ContentPlan ,
348356 writing_instructions : str | None ,
349357 user_context : dict [str , Any ],
@@ -360,12 +368,14 @@ def _generate_blog_markdown_retry_with_ai(
360368 f"User context (JSON):\n { json .dumps (user_context , ensure_ascii = True )} "
361369 )
362370 try :
363- response = client .messages .create (
364- model = Config .ANTHROPIC_MODEL ,
365- max_tokens = Config .ANTHROPIC_MAX_TOKENS ,
366- temperature = Config .ANTHROPIC_TEMPERATURE ,
367- system = "You are a senior blog writer. Return markdown only." ,
368- messages = [{"role" : "user" , "content" : prompt }],
371+ response = client .chat .completions .create (
372+ model = Config .NIM_MODEL ,
373+ max_tokens = Config .AI_MAX_TOKENS ,
374+ temperature = Config .AI_TEMPERATURE ,
375+ messages = [
376+ {"role" : "system" , "content" : "You are a senior blog writer. Return markdown only." },
377+ {"role" : "user" , "content" : prompt },
378+ ],
369379 )
370380 return _extract_text_from_response (response )
371381 except (APIError , APIConnectionError , APITimeoutError , ToolHandlerError ):
@@ -436,6 +446,13 @@ def _generate_blog_fallback(
436446
437447
438448def _extract_text_from_response (response : Any ) -> str :
449+ choices = getattr (response , "choices" , None )
450+ if isinstance (choices , list ) and choices :
451+ message = getattr (choices [0 ], "message" , None )
452+ text = (getattr (message , "content" , None ) or "" ).strip () if message is not None else ""
453+ if text :
454+ return text
455+
439456 text_parts : list [str ] = []
440457 for block in getattr (response , "content" , []):
441458 if getattr (block , "type" , None ) == "text" :
0 commit comments