From f26f4985a9093c9378c012c6bbfef6c00a70163f Mon Sep 17 00:00:00 2001 From: ciregenz Date: Sat, 23 May 2026 03:09:35 -0700 Subject: [PATCH] [eric] split: move aux LLM helpers out of agent_manager --- backend/apps/agents/agent_manager.py | 21 +-------------------- backend/apps/agents/aux_llm.py | 18 ++++++++++++++++++ 2 files changed, 19 insertions(+), 20 deletions(-) create mode 100644 backend/apps/agents/aux_llm.py diff --git a/backend/apps/agents/agent_manager.py b/backend/apps/agents/agent_manager.py index 2968573a..4fdc6f71 100644 --- a/backend/apps/agents/agent_manager.py +++ b/backend/apps/agents/agent_manager.py @@ -49,32 +49,13 @@ from backend.apps.agents.tool_catalog import ( _get_denied_tool_names, _is_fully_denied, ) +from backend.apps.agents.aux_llm import _safe_resp_text logger = logging.getLogger(__name__) os.environ.setdefault("CLAUDE_CODE_STREAM_CLOSE_TIMEOUT", "3600000") -def _safe_resp_text(resp) -> str: - """Extract text from an Anthropic-shape response, tolerating Gemini/OpenAI - edge cases. Gemini through 9Router occasionally returns `content=[]` (e.g. - safety stop, function-call-only turn) which makes `resp.content[0].text` - raise `'NoneType' object is not subscriptable` and bubbles up as a - fallback-required path. This walks the content list looking for the first - text block and returns "" if none exists, so callers can decide their own - fallback without a raw IndexError. - """ - try: - blocks = getattr(resp, "content", None) or [] - for b in blocks: - t = getattr(b, "text", None) - if isinstance(t, str) and t: - return t - return "" - except Exception: - return "" - - def _apply_context_window(session, settings=None) -> None: """Set session.context_window from the registry for its (provider, model). diff --git a/backend/apps/agents/aux_llm.py b/backend/apps/agents/aux_llm.py new file mode 100644 index 00000000..83204ea8 --- /dev/null +++ b/backend/apps/agents/aux_llm.py @@ -0,0 +1,18 @@ +def _safe_resp_text(resp) -> str: + """Extract text from an Anthropic-shape response, tolerating Gemini/OpenAI + edge cases. Gemini through 9Router occasionally returns `content=[]` (e.g. + safety stop, function-call-only turn) which makes `resp.content[0].text` + raise `'NoneType' object is not subscriptable` and bubbles up as a + fallback-required path. This walks the content list looking for the first + text block and returns "" if none exists, so callers can decide their own + fallback without a raw IndexError. + """ + try: + blocks = getattr(resp, "content", None) or [] + for b in blocks: + t = getattr(b, "text", None) + if isinstance(t, str) and t: + return t + return "" + except Exception: + return ""