Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 5 additions & 2 deletions run_agent.py
Original file line number Diff line number Diff line change
Expand Up @@ -5796,7 +5796,10 @@ def chat(self, message: str, stream_callback: Optional[callable] = None) -> str:
str: Final assistant response
"""
result = self.run_conversation(message, stream_callback=stream_callback)
return result["final_response"]
# run_conversation's error/failure return paths (API error after retries,
# billing/credits exhaustion, policy halt, etc.) omit "final_response";
# surface the error message instead of raising KeyError here.
return result.get("final_response") or result.get("error") or ""

def _run_codex_app_server_turn(
self,
Expand Down Expand Up @@ -5989,7 +5992,7 @@ def main(
print(f"πŸ“ž API Calls: {result['api_calls']}")
print(f"πŸ’¬ Messages: {len(result['messages'])}")

if result['final_response']:
if result.get('final_response'):
print("\n🎯 FINAL RESPONSE:")
print("-" * 30)
print(result['final_response'])
Expand Down
41 changes: 41 additions & 0 deletions tests/test_chat_final_response_resilience.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
"""Regression: AIAgent.chat() must not crash when run_conversation returns an
error/failure result that omits the 'final_response' key.

Several error/failure return paths in agent.conversation_loop.run_conversation
(API error after max retries, billing/credits exhaustion, policy halt, etc.)
return a result dict with keys like {messages, completed, api_calls, error,
failed} but *no* 'final_response'. chat() previously did
``return result["final_response"]`` and raised ``KeyError: 'final_response'``
on those paths instead of surfacing the error.
"""


def _agent_with(result):
# Bypass the heavy __init__; chat() only depends on self.run_conversation.
from run_agent import AIAgent

agent = AIAgent.__new__(AIAgent)
agent.run_conversation = lambda *a, **k: result
return agent


def test_chat_surfaces_error_when_final_response_missing():
agent = _agent_with({
"messages": [],
"completed": False,
"api_calls": 1,
"error": "API call failed after 3 retries",
"failed": True,
# no "final_response" key β€” the regression case
})
assert agent.chat("hi") == "API call failed after 3 retries"


def test_chat_returns_final_response_when_present():
agent = _agent_with({"final_response": "hello world", "error": None})
assert agent.chat("hi") == "hello world"


def test_chat_empty_string_when_neither_final_response_nor_error():
agent = _agent_with({"completed": True, "messages": [], "api_calls": 0})
assert agent.chat("hi") == ""
Loading