You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
[BUG]: FREISA-GPT raises TypeError after every successful tool call #111
ChatSession.process_user_request() raises TypeError whenever a tool call succeeds — i.e. on the main FREISA-GPT path.
LLMClient.get_response() is annotated -> str and returns a JSON string (llm_client.py:86):
returnjson.dumps(data["choices"][0]["message"])
But the caller indexes it as if it were a dict, at mcp_client.py:404:
final_response=awaitself.llm_client.get_response(messages)
...
returnfinal_response["content"] # <-- TypeError: string indices must be integers
Steps To Reproduce
This branch is taken whenever process_llm_response() actually executes a tool (so result != llm_response). Minimal reproduction of the type confusion:
importjsondefget_response() ->str: # mirrors LLMClient.get_responsereturnjson.dumps({"role": "assistant", "content": "Woof!"})
final_response=get_response()
print(final_response["content"]) # mcp_client.py:404# TypeError: string indices must be integers, not 'str'
Confirmed statically by mypy:
src/mcp_client_pupper/mcp_client.py:404: error: Invalid index type "str" for "str";
expected type "SupportsIndex | slice[...]" [index]
Additional Information
The exception is not caught anywhere on the way up: process_user_request() is awaited unguarded in PuppyVoiceAssistant._process_heard_text(), which is itself driven from asyncio.run(...) inside the sounddevice audio callback (puppy_voice_assistant.py:145). So the failure surfaces inside the realtime audio thread rather than as a clean error.
Possible fixes:
Have get_response() return the parsed dict (and update the annotation and the other three call sites), or
json.loads(final_response)["content"] at the call site.
The first is cleaner: llm_response is already json.loads-ed again inside process_llm_response(), so the round-trip through a string serves no purpose.
mypy also flags three Item "None" of "LLMClient | None" has no attribute "get_response" errors in the same file — ChatSession.llm_client is typed Optional[LLMClient] but never narrowed.
Note
Found during an automated code audit; see also the related issues filed at the same time.
Activity