mirror of https://github.com/aliasrobotics/cai.git
Solve streaming no streaming message list
This commit is contained in:
parent
ff0bea80ca
commit
25faeb59ee
118
src/cai/cli.py
118
src/cai/cli.py
|
|
@ -305,6 +305,67 @@ def run_cai_cli(starting_agent, context_variables=None, max_turns=float('inf')):
|
|||
|
||||
Total = time.time() - START_TIME
|
||||
idle_time += time.time() - idle_start_time
|
||||
|
||||
# NEW: Clean up any pending tool calls before exiting
|
||||
try:
|
||||
# Access the _Converter directly to clean up any pending tool calls
|
||||
from cai.sdk.agents.models.openai_chatcompletions import _Converter
|
||||
|
||||
# Check if any tool calls are pending (have been issued but don't have responses)
|
||||
pending_calls = []
|
||||
if hasattr(_Converter, 'recent_tool_calls'):
|
||||
for call_id, call_info in list(_Converter.recent_tool_calls.items()):
|
||||
# Check if this tool call has a corresponding response in message_history
|
||||
tool_response_exists = False
|
||||
for msg in message_history:
|
||||
if msg.get("role") == "tool" and msg.get("tool_call_id") == call_id:
|
||||
tool_response_exists = True
|
||||
break
|
||||
|
||||
# If no tool response exists, create a synthetic one
|
||||
if not tool_response_exists:
|
||||
# First ensure there's a matching assistant message with this tool call
|
||||
assistant_exists = False
|
||||
for msg in message_history:
|
||||
if (msg.get("role") == "assistant" and
|
||||
msg.get("tool_calls") and
|
||||
any(tc.get("id") == call_id for tc in msg.get("tool_calls", []))):
|
||||
assistant_exists = True
|
||||
break
|
||||
|
||||
# Add assistant message if needed
|
||||
if not assistant_exists:
|
||||
tool_call_msg = {
|
||||
"role": "assistant",
|
||||
"content": None,
|
||||
"tool_calls": [{
|
||||
"id": call_id,
|
||||
"type": "function",
|
||||
"function": {
|
||||
"name": call_info.get('name', 'unknown_function'),
|
||||
"arguments": call_info.get('arguments', '{}')
|
||||
}
|
||||
}]
|
||||
}
|
||||
add_to_message_history(tool_call_msg)
|
||||
|
||||
# Add a synthetic tool response
|
||||
tool_msg = {
|
||||
"role": "tool",
|
||||
"tool_call_id": call_id,
|
||||
"content": "Operation interrupted by user (Keyboard Interrupt during shutdown)"
|
||||
}
|
||||
add_to_message_history(tool_msg)
|
||||
pending_calls.append(call_info.get('name', 'unknown'))
|
||||
|
||||
# Apply message list fixes
|
||||
if pending_calls:
|
||||
from cai.util import fix_message_list
|
||||
message_history[:] = fix_message_list(message_history)
|
||||
print(f"\033[93mCleaned up {len(pending_calls)} pending tool calls before exit\033[0m")
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
try:
|
||||
# Get more accurate active and idle time measurements from the timer functions
|
||||
from cai.util import get_active_time_seconds, get_idle_time_seconds, COST_TRACKER
|
||||
|
|
@ -703,6 +764,63 @@ def run_cai_cli(starting_agent, context_variables=None, max_turns=float('inf')):
|
|||
|
||||
except KeyboardInterrupt:
|
||||
print("\n\033[91mKeyboard interrupt detected\033[0m")
|
||||
|
||||
# NEW: Handle pending tool calls to prevent errors on next iteration
|
||||
try:
|
||||
# Access the _Converter directly to clean up any pending tool calls
|
||||
from cai.sdk.agents.models.openai_chatcompletions import _Converter
|
||||
|
||||
# Check if any tool calls are pending (have been issued but don't have responses)
|
||||
if hasattr(_Converter, 'recent_tool_calls'):
|
||||
for call_id, call_info in list(_Converter.recent_tool_calls.items()):
|
||||
# Check if this tool call has a corresponding response in message_history
|
||||
tool_response_exists = False
|
||||
for msg in message_history:
|
||||
if msg.get("role") == "tool" and msg.get("tool_call_id") == call_id:
|
||||
tool_response_exists = True
|
||||
break
|
||||
|
||||
# If no tool response exists, create a synthetic one
|
||||
if not tool_response_exists:
|
||||
# First ensure there's a matching assistant message with this tool call
|
||||
assistant_exists = False
|
||||
for msg in message_history:
|
||||
if (msg.get("role") == "assistant" and
|
||||
msg.get("tool_calls") and
|
||||
any(tc.get("id") == call_id for tc in msg.get("tool_calls", []))):
|
||||
assistant_exists = True
|
||||
break
|
||||
|
||||
# Add assistant message if needed
|
||||
if not assistant_exists:
|
||||
tool_call_msg = {
|
||||
"role": "assistant",
|
||||
"content": None,
|
||||
"tool_calls": [{
|
||||
"id": call_id,
|
||||
"type": "function",
|
||||
"function": {
|
||||
"name": call_info.get('name', 'unknown_function'),
|
||||
"arguments": call_info.get('arguments', '{}')
|
||||
}
|
||||
}]
|
||||
}
|
||||
add_to_message_history(tool_call_msg)
|
||||
|
||||
# Add a synthetic tool response
|
||||
tool_msg = {
|
||||
"role": "tool",
|
||||
"tool_call_id": call_id,
|
||||
"content": "Operation interrupted by user (Keyboard Interrupt)"
|
||||
}
|
||||
add_to_message_history(tool_msg)
|
||||
|
||||
# Apply message list fixes
|
||||
from cai.util import fix_message_list
|
||||
message_history[:] = fix_message_list(message_history)
|
||||
except Exception as cleanup_error:
|
||||
print(f"\033[91mError cleaning up interrupted tools: {str(cleanup_error)}\033[0m")
|
||||
|
||||
pass
|
||||
except Exception as e:
|
||||
import traceback
|
||||
|
|
|
|||
|
|
@ -348,17 +348,41 @@ class OpenAIChatCompletionsModel(Model):
|
|||
# Get token count estimate before API call for consistent counting
|
||||
estimated_input_tokens, _ = count_tokens_with_tiktoken(converted_messages)
|
||||
|
||||
response = await self._fetch_response(
|
||||
system_instructions,
|
||||
input,
|
||||
model_settings,
|
||||
tools,
|
||||
output_schema,
|
||||
handoffs,
|
||||
span_generation,
|
||||
tracing,
|
||||
stream=False,
|
||||
)
|
||||
try:
|
||||
response = await self._fetch_response(
|
||||
system_instructions,
|
||||
input,
|
||||
model_settings,
|
||||
tools,
|
||||
output_schema,
|
||||
handoffs,
|
||||
span_generation,
|
||||
tracing,
|
||||
stream=False,
|
||||
)
|
||||
except KeyboardInterrupt:
|
||||
# Handle KeyboardInterrupt during API call
|
||||
# Make sure to clean up anything needed for proper state before allowing interrupt to propagate
|
||||
|
||||
# If this call generated any tool calls, they were stored in _Converter.recent_tool_calls but
|
||||
# we couldn't add them to message_history since we didn't get the response.
|
||||
# We should generate synthetic responses to avoid broken message sequences.
|
||||
|
||||
# Add synthetic tool output to prevent errors in next turn
|
||||
if hasattr(_Converter, 'tool_outputs') and hasattr(_Converter, 'recent_tool_calls'):
|
||||
# Add a placeholder response for any tool call generated during this interaction
|
||||
# We don't know the actual tool calls, so we'll use what we know from timing
|
||||
# Any tool call that was generated within the last 5 seconds is likely from this interaction
|
||||
current_time = time.time()
|
||||
for call_id, call_info in list(_Converter.recent_tool_calls.items()):
|
||||
if 'start_time' in call_info and (current_time - call_info['start_time']) < 5.0:
|
||||
# Add a placeholder output for this tool call
|
||||
_Converter.tool_outputs[call_id] = "Operation interrupted by user (KeyboardInterrupt)"
|
||||
|
||||
# Let the interrupt propagate up to end the current operation
|
||||
stop_active_timer()
|
||||
start_idle_timer()
|
||||
raise
|
||||
|
||||
if _debug.DONT_LOG_MODEL_DATA:
|
||||
logger.debug("Received model response")
|
||||
|
|
|
|||
|
|
@ -272,11 +272,6 @@ def function_tool(
|
|||
async def _on_invoke_tool(ctx: RunContextWrapper[Any], input: str) -> Any:
|
||||
try:
|
||||
return await _on_invoke_tool_impl(ctx, input)
|
||||
except KeyboardInterrupt:
|
||||
logger.info(
|
||||
f"Tool {schema.name} execution was interrupted by the user (Ctrl+C)."
|
||||
)
|
||||
return "execution was interrupted by the user (Ctrl+C)"
|
||||
except Exception as e:
|
||||
if failure_error_function is None:
|
||||
raise
|
||||
|
|
|
|||
Loading…
Reference in New Issue