From 30209322451c4ca307198431f4c576a0a06b568d Mon Sep 17 00:00:00 2001 From: Shashank Kushwaha Date: Thu, 2 Apr 2026 00:55:51 -0700 Subject: [PATCH 1/3] Restore OpenAIResponsesClient to fix tool calling failures in RSA proposals --- charge/clients/agentframework.py | 104 ++++++++++++++++++++++--------- 1 file changed, 73 insertions(+), 31 deletions(-) diff --git a/charge/clients/agentframework.py b/charge/clients/agentframework.py index 6e12c0f..373fc75 100644 --- a/charge/clients/agentframework.py +++ b/charge/clients/agentframework.py @@ -21,6 +21,8 @@ ) from agent_framework.openai import ( OpenAIChatClient, + OpenAIResponsesClient, + OpenAIResponsesOptions, OpenAIChatOptions, ) except ImportError: @@ -68,7 +70,8 @@ def create_agentframework_client( model: str, api_key: Optional[str] = None, model_kwargs: Optional[dict[str, Any]] = None, -) -> OpenAIChatClient: + use_responses_api: bool = True, +) -> Union[OpenAIChatClient, OpenAIResponsesClient]: """ Creates an Agent Framework chat client based on the specified backend and model. @@ -77,9 +80,10 @@ def create_agentframework_client( model (str): The model name/ID to use. api_key (Optional[str], optional): API key for the model. Defaults to None. model_kwargs (Optional[dict], optional): Additional keyword arguments. Defaults to None. + use_responses_api (bool, optional): Use OpenAI Responses API for hosted tools. Defaults to True. Returns: - OpenAIChatClient: The created chat client. + Union[OpenAIChatClient, OpenAIResponsesClient]: The created chat client. Raises: ValueError: If backend is not supported or configuration is invalid. @@ -110,14 +114,23 @@ def create_agentframework_client( api_key is not None ), "API key must be provided for OpenAI or Gemini backend" - # Standard OpenAI or OpenAI-compatible client - # Agent Framework reads OPENAI_API_KEY from environment by default - client = OpenAIChatClient( - model=model, - api_key=api_key, - # Additional kwargs can be passed but Agent Framework has different options - **model_kwargs if model_kwargs is not None else {}, - ) + # Check if Responses API is requested + if use_responses_api: + logger.info("Creating OpenAIResponsesClient with hosted tools support") + client = OpenAIResponsesClient( + model_id=model, + api_key=api_key, + **model_kwargs if model_kwargs is not None else {}, + ) + else: + # Standard OpenAI or OpenAI-compatible client + # Agent Framework reads OPENAI_API_KEY from environment by default + client = OpenAIChatClient( + model=model, + api_key=api_key, + # Additional kwargs can be passed but Agent Framework has different options + **model_kwargs if model_kwargs is not None else {}, + ) return client @@ -144,7 +157,7 @@ class AgentFrameworkAgent(Agent): def __init__( self, task: Optional[Task], - client: OpenAIChatClient, + client: Union[OpenAIChatClient, OpenAIResponsesClient], agent_name: str, model: str | None, memory: Optional[Memory] = None, @@ -234,23 +247,42 @@ def _create_agent( reasoning_effort: Literal["low", "medium", "high"], instructions: str, ) -> AFAgent: - af_agent = AFAgent[OpenAIChatOptions]( - client=self.client, - name=agent_name, - instructions=instructions, - tools=self.workbenches, - default_options={ - "reasoning": { - "effort": reasoning_effort, - "summary": "detailed", + if isinstance(self.client, OpenAIResponsesClient): + af_agent = AFAgent[OpenAIResponsesOptions]( + client=self.client, + name=agent_name, + instructions=instructions, + tools=self.workbenches, + default_options={ + "reasoning": { + "effort": reasoning_effort, + "summary": "detailed", + }, + "include": ["reasoning.encrypted_content"], }, - "include": ["reasoning.encrypted_content"], - }, - context_providers=[ - # This provider ensures session.state contains the history - InMemoryHistoryProvider(load_messages=True) - ], - ) + context_providers=[ + # This provider ensures session.state contains the history + InMemoryHistoryProvider(load_messages=True) + ], + ) + else: + af_agent = AFAgent[OpenAIChatOptions]( + client=self.client, + name=agent_name, + instructions=instructions, + tools=self.workbenches, + default_options={ + "reasoning": { + "effort": reasoning_effort, + "summary": "detailed", + }, + "include": ["reasoning.encrypted_content"], + }, + context_providers=[ + # This provider ensures session.state contains the history + InMemoryHistoryProvider(load_messages=True) + ], + ) return af_agent async def setup_mcp_workbenches(self) -> None: @@ -605,7 +637,7 @@ class AgentFrameworkBackend(AgentBackend): api_key: Optional API key. base_url: Optional base URL for custom endpoints. model_kwargs: Additional client kwargs. - use_responses_api: (DEPRECATED in AF) Whether to use Responses API. + use_responses_api: Whether to use Responses API (hosted tools). """ AGENT_COUNT = 0 @@ -619,7 +651,7 @@ def __init__( model_kwargs: Optional[dict] = None, use_responses_api: bool = True, reasoning_effort: Literal["low", "medium", "high"] = "medium", - client: Optional[OpenAIChatClient] = None, + client: Optional[OpenAIChatClient | OpenAIResponsesClient] = None, **kwargs, ): super().__init__( @@ -632,6 +664,7 @@ def __init__( **kwargs, ) self.client = client + self.use_responses_api = use_responses_api self.reasoning_effort = reasoning_effort model, backend, api_key, model_kwargs_configured = model_configure( @@ -650,6 +683,7 @@ def __init__( model=model, api_key=api_key, model_kwargs=model_kwargs_configured, + use_responses_api=use_responses_api, ) self.model = model @@ -694,11 +728,19 @@ def create_agent( def get_hosted_tools(self) -> List[Any]: """ - Get hosted tools from an OpenAI API client. + Get hosted tools from an OpenAI Responses API client. + + Hosted tools are only available when `use_responses_api=True`. """ + if not self.use_responses_api: + raise ValueError( + "Hosted tools are only available with Responses API. " + "Create the backend with use_responses_api=True" + ) + tools: List[Any] = [] - assert isinstance(self.client, OpenAIChatClient) + assert isinstance(self.client, (OpenAIChatClient, OpenAIResponsesClient)) try: if hasattr(self.client, "get_code_interpreter_tool"): From c945255bfc0d7ab53cc01c8b9a8f438a53949219 Mon Sep 17 00:00:00 2001 From: Shashank Kushwaha Date: Thu, 2 Apr 2026 00:58:57 -0700 Subject: [PATCH 2/3] Fix OpenAIResponsesClient parameter from model_id to model --- charge/clients/agentframework.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/charge/clients/agentframework.py b/charge/clients/agentframework.py index 373fc75..d170404 100644 --- a/charge/clients/agentframework.py +++ b/charge/clients/agentframework.py @@ -118,7 +118,7 @@ def create_agentframework_client( if use_responses_api: logger.info("Creating OpenAIResponsesClient with hosted tools support") client = OpenAIResponsesClient( - model_id=model, + model=model, api_key=api_key, **model_kwargs if model_kwargs is not None else {}, ) From 6bcfc14fc4c467c03bd4d40421e25aea45109398 Mon Sep 17 00:00:00 2001 From: Shashank Kushwaha Date: Thu, 2 Apr 2026 01:12:42 -0700 Subject: [PATCH 3/3] Fix session reset between retries and add temperature=0.8 for RSA proposers --- charge/clients/agentframework.py | 123 ++++++++++++------------------- 1 file changed, 47 insertions(+), 76 deletions(-) diff --git a/charge/clients/agentframework.py b/charge/clients/agentframework.py index d170404..9585572 100644 --- a/charge/clients/agentframework.py +++ b/charge/clients/agentframework.py @@ -21,8 +21,6 @@ ) from agent_framework.openai import ( OpenAIChatClient, - OpenAIResponsesClient, - OpenAIResponsesOptions, OpenAIChatOptions, ) except ImportError: @@ -70,8 +68,7 @@ def create_agentframework_client( model: str, api_key: Optional[str] = None, model_kwargs: Optional[dict[str, Any]] = None, - use_responses_api: bool = True, -) -> Union[OpenAIChatClient, OpenAIResponsesClient]: +) -> OpenAIChatClient: """ Creates an Agent Framework chat client based on the specified backend and model. @@ -80,10 +77,9 @@ def create_agentframework_client( model (str): The model name/ID to use. api_key (Optional[str], optional): API key for the model. Defaults to None. model_kwargs (Optional[dict], optional): Additional keyword arguments. Defaults to None. - use_responses_api (bool, optional): Use OpenAI Responses API for hosted tools. Defaults to True. Returns: - Union[OpenAIChatClient, OpenAIResponsesClient]: The created chat client. + OpenAIChatClient: The created chat client. Raises: ValueError: If backend is not supported or configuration is invalid. @@ -114,23 +110,14 @@ def create_agentframework_client( api_key is not None ), "API key must be provided for OpenAI or Gemini backend" - # Check if Responses API is requested - if use_responses_api: - logger.info("Creating OpenAIResponsesClient with hosted tools support") - client = OpenAIResponsesClient( - model=model, - api_key=api_key, - **model_kwargs if model_kwargs is not None else {}, - ) - else: - # Standard OpenAI or OpenAI-compatible client - # Agent Framework reads OPENAI_API_KEY from environment by default - client = OpenAIChatClient( - model=model, - api_key=api_key, - # Additional kwargs can be passed but Agent Framework has different options - **model_kwargs if model_kwargs is not None else {}, - ) + # Standard OpenAI or OpenAI-compatible client + # Agent Framework reads OPENAI_API_KEY from environment by default + client = OpenAIChatClient( + model=model, + api_key=api_key, + # Additional kwargs can be passed but Agent Framework has different options + **model_kwargs if model_kwargs is not None else {}, + ) return client @@ -157,7 +144,7 @@ class AgentFrameworkAgent(Agent): def __init__( self, task: Optional[Task], - client: Union[OpenAIChatClient, OpenAIResponsesClient], + client: OpenAIChatClient, agent_name: str, model: str | None, memory: Optional[Memory] = None, @@ -246,43 +233,31 @@ def _create_agent( agent_name: str, reasoning_effort: Literal["low", "medium", "high"], instructions: str, + temperature: Optional[float] = None, ) -> AFAgent: - if isinstance(self.client, OpenAIResponsesClient): - af_agent = AFAgent[OpenAIResponsesOptions]( - client=self.client, - name=agent_name, - instructions=instructions, - tools=self.workbenches, - default_options={ - "reasoning": { - "effort": reasoning_effort, - "summary": "detailed", - }, - "include": ["reasoning.encrypted_content"], - }, - context_providers=[ - # This provider ensures session.state contains the history - InMemoryHistoryProvider(load_messages=True) - ], - ) - else: - af_agent = AFAgent[OpenAIChatOptions]( - client=self.client, - name=agent_name, - instructions=instructions, - tools=self.workbenches, - default_options={ - "reasoning": { - "effort": reasoning_effort, - "summary": "detailed", - }, - "include": ["reasoning.encrypted_content"], - }, - context_providers=[ - # This provider ensures session.state contains the history - InMemoryHistoryProvider(load_messages=True) - ], - ) + default_options = { + "reasoning": { + "effort": reasoning_effort, + "summary": "detailed", + }, + "include": ["reasoning.encrypted_content"], + } + + # Add temperature if specified + if temperature is not None: + default_options["temperature"] = temperature + + af_agent = AFAgent[OpenAIChatOptions]( + client=self.client, + name=agent_name, + instructions=instructions, + tools=self.workbenches, + default_options=default_options, + context_providers=[ + # This provider ensures session.state contains the history + InMemoryHistoryProvider(load_messages=True) + ], + ) return af_agent async def setup_mcp_workbenches(self) -> None: @@ -376,8 +351,12 @@ async def _execute_with_retries( try: logger.info(f"Attempt {attempt}/{self.max_retries}") + # Create fresh session for retry attempts to avoid orphaned tool calls + # from previous failed attempts (which cause "No tool output found" errors) + retry_session = agent.create_session() if attempt > 1 else session + # Run agent (Agent Framework returns AgentResponse) - stream = await agent.run(user_prompt, session=session, stream=True) + stream = await agent.run(user_prompt, session=retry_session, stream=True) tool_call_names: dict[str, str] = {} async for update in stream: if not update.contents: @@ -578,8 +557,10 @@ async def run( instructions = ( self.task.get_system_prompt() if self.task is not None else "" ) + # Get temperature from task if available + temperature = getattr(self.task, "temperature", None) self._af_agent = self._create_agent( - self.agent_name, self.reasoning_effort, instructions=instructions + self.agent_name, self.reasoning_effort, instructions=instructions, temperature=temperature ) self._agent_signature = agent_signature @@ -637,7 +618,7 @@ class AgentFrameworkBackend(AgentBackend): api_key: Optional API key. base_url: Optional base URL for custom endpoints. model_kwargs: Additional client kwargs. - use_responses_api: Whether to use Responses API (hosted tools). + use_responses_api: (Deprecated) Whether to use Responses API. """ AGENT_COUNT = 0 @@ -651,7 +632,7 @@ def __init__( model_kwargs: Optional[dict] = None, use_responses_api: bool = True, reasoning_effort: Literal["low", "medium", "high"] = "medium", - client: Optional[OpenAIChatClient | OpenAIResponsesClient] = None, + client: Optional[OpenAIChatClient] = None, **kwargs, ): super().__init__( @@ -664,7 +645,6 @@ def __init__( **kwargs, ) self.client = client - self.use_responses_api = use_responses_api self.reasoning_effort = reasoning_effort model, backend, api_key, model_kwargs_configured = model_configure( @@ -683,7 +663,6 @@ def __init__( model=model, api_key=api_key, model_kwargs=model_kwargs_configured, - use_responses_api=use_responses_api, ) self.model = model @@ -728,19 +707,11 @@ def create_agent( def get_hosted_tools(self) -> List[Any]: """ - Get hosted tools from an OpenAI Responses API client. - - Hosted tools are only available when `use_responses_api=True`. + Get hosted tools from an OpenAI API client. """ - if not self.use_responses_api: - raise ValueError( - "Hosted tools are only available with Responses API. " - "Create the backend with use_responses_api=True" - ) - tools: List[Any] = [] - assert isinstance(self.client, (OpenAIChatClient, OpenAIResponsesClient)) + assert isinstance(self.client, OpenAIChatClient) try: if hasattr(self.client, "get_code_interpreter_tool"):