diff --git a/src/cai/repl/commands/memory.py b/src/cai/repl/commands/memory.py index 0e1af02b..e0523a36 100644 --- a/src/cai/repl/commands/memory.py +++ b/src/cai/repl/commands/memory.py @@ -4,6 +4,7 @@ Manages memory storage in .cai/memory for persistent context. """ from typing import List, Optional, Dict, Any +import inspect import os import asyncio import json @@ -1256,15 +1257,31 @@ This session is being continued from a previous conversation that ran out of con input=f"Please summarize the following conversation:\n\n{conversation_text}", max_turns=1 ) - + if result.final_output: return str(result.final_output) else: return None - + except Exception as e: console.print(f"[red]Error generating summary: {e}[/red]") return None + finally: + # Best-effort: explicitly cleanup the temporary summary/support model + try: + model_inst = getattr(summary_agent, "model", None) + # Some Agent constructions put the Model object directly on `agent.model` + # and some providers expose a cleanup coroutine. + if model_inst is not None and hasattr(model_inst, "cleanup"): + try: + coro = model_inst.cleanup() + if inspect.isawaitable(coro): + await coro + except Exception: + # best-effort cleanup — swallow any errors + pass + except Exception: + pass def _format_history_for_summary(self, history: List[Dict[str, Any]]) -> str: """Format message history for summarization. diff --git a/src/cai/sdk/agents/models/openai_chatcompletions.py b/src/cai/sdk/agents/models/openai_chatcompletions.py index 4390f82d..324b0fda 100644 --- a/src/cai/sdk/agents/models/openai_chatcompletions.py +++ b/src/cai/sdk/agents/models/openai_chatcompletions.py @@ -470,6 +470,49 @@ class OpenAIChatCompletionsModel(Model): # Ignore any errors during cleanup pass + async def cleanup(self) -> None: + """Explicitly cleanup underlying clients and free instance registry. + + This is intended to be called when a temporary model instance (for + example the summary/support model) is no longer needed. It will try + to close the HTTP/async client if available, remove the instance + from the legacy `ACTIVE_MODEL_INSTANCES` registry and clear the + in-memory message history so any backing LLM server can free slots + or context. + """ + try: + client = getattr(self, "_client", None) + if client is not None: + aclose = getattr(client, "aclose", None) + if aclose: + try: + res = aclose() + # Await if it's awaitable + if inspect.isawaitable(res): + await res + except Exception: + # Best-effort close + pass + try: + delattr(self, "_client") + except Exception: + pass + except Exception: + pass + + try: + key = (getattr(self, '_display_name', None), getattr(self, 'agent_id', None)) + if key in ACTIVE_MODEL_INSTANCES: + del ACTIVE_MODEL_INSTANCES[key] + except Exception: + pass + + try: + if hasattr(self, 'message_history') and isinstance(self.message_history, list): + self.message_history.clear() + except Exception: + pass + def add_to_message_history(self, msg): """Add a message to this instance's history if it's not a duplicate.