Signed-off-by: Víctor Mayoral Vilches <v.mayoralv@gmail.com>
This commit is contained in:
Víctor Mayoral Vilches 2025-04-02 16:48:23 +02:00
parent 0877dfda81
commit 5b29fbe4db
10 changed files with 674 additions and 563 deletions

View File

@ -142,6 +142,6 @@ Supplying a list of tools doesn't always mean the LLM will use a tool. You can f
!!! note
To prevent infinite loops, the framework automatically resets `tool_choice` to "auto" after a tool call. This behavior is configurable via [`agent.reset_tool_choice`][agents.agent.Agent.reset_tool_choice]. The infinite loop is because tool results are sent to the LLM, which then generates another tool call because of `tool_choice`, ad infinitum.
To prevent infinite loops, the framework automatically resets `tool_choice` to "auto" after a tool call. This behavior is configurable via [`agent.reset_tool_choice`][cai.sdk.agents.agent.Agent.reset_tool_choice]. The infinite loop is because tool results are sent to the LLM, which then generates another tool call because of `tool_choice`, ad infinitum.
If you want the Agent to completely stop after a tool call (rather than continuing with auto mode), you can set [`Agent.tool_use_behavior="stop_on_first_tool"`] which will directly use the tool output as the final response without further LLM processing.

View File

@ -13,7 +13,7 @@ Currently, the MCP spec defines two kinds of servers, based on the transport mec
1. **stdio** servers run as a subprocess of your application. You can think of them as running "locally".
2. **HTTP over SSE** servers run remotely. You connect to them via a URL.
You can use the [`MCPServerStdio`][agents.mcp.server.MCPServerStdio] and [`MCPServerSse`][agents.mcp.server.MCPServerSse] classes to connect to these servers.
You can use the [`MCPServerStdio`][cai.sdk.agents.mcp.server.MCPServerStdio] and [`MCPServerSse`][cai.sdk.agents.mcp.server.MCPServerSse] classes to connect to these servers.
For example, this is how you'd use the [official MCP filesystem server](https://www.npmjs.com/package/@modelcontextprotocol/server-filesystem).
@ -42,7 +42,7 @@ agent=Agent(
## Caching
Every time an Agent runs, it calls `list_tools()` on the MCP server. This can be a latency hit, especially if the server is a remote server. To automatically cache the list of tools, you can pass `cache_tools_list=True` to both [`MCPServerStdio`][agents.mcp.server.MCPServerStdio] and [`MCPServerSse`][agents.mcp.server.MCPServerSse]. You should only do this if you're certain the tool list will not change.
Every time an Agent runs, it calls `list_tools()` on the MCP server. This can be a latency hit, especially if the server is a remote server. To automatically cache the list of tools, you can pass `cache_tools_list=True` to both [`MCPServerStdio`][cai.sdk.agents.mcp.server.MCPServerStdio] and [`MCPServerSse`][cai.sdk.agents.mcp.server.MCPServerSse]. You should only do this if you're certain the tool list will not change.
If you want to invalidate the cache, you can call `invalidate_tools_cache()` on the servers.

View File

@ -1,3 +1,3 @@
# `Agents`
::: cai.sdk.agents.agent
::: cai.sdk.agents.agent

View File

@ -1,3 +1,3 @@
# `MCP Servers`
::: agents.mcp.server
::: cai.sdk.agents.mcp.server

View File

@ -1,3 +1,3 @@
# `MCP Util`
::: agents.mcp.util
::: cai.sdk.agents.mcp.util

View File

@ -39,7 +39,7 @@ async def main():
print(f"Using model: {os.getenv('CAI_MODEL', 'default')}")
# Run the agent with a simple test message
result = await Runner.run(agent, "Hello! Can you introduce yourself and explain what you can do?")
result = await Runner.run(agent, "Hello! Can you list the files in the current directory?")
# Print the result
print("\nAgent response:")

View File

@ -45,7 +45,7 @@ async def main():
print("Agent: ", end="", flush=True)
# Run the agent with a simple test message in streaming mode
result = Runner.run_streamed(agent, "Hello! Can you introduce yourself and explain what you can do?")
result = Runner.run_streamed(agent, "Hello! Can you list the files in the current directory?")
# Process the streaming response
async for event in result.stream_events():

View File

@ -21,6 +21,8 @@ dependencies = [
"litellm>=1.63.7",
"mako>=1.3.9",
"mcp; python_version >= '3.10'",
"mkdocs>=1.6.0",
"mkdocs-material>=9.6.0",
]
classifiers = [
"Typing :: Typed",

View File

@ -113,7 +113,8 @@ class OpenAIChatCompletionsModel(Model):
self._client = openai_client
# Check if we're using OLLAMA models
self.is_ollama = os.getenv('OLLAMA') is not None and os.getenv('OLLAMA').lower() != 'false'
self.empty_content_error_shown = False
def _non_null_or_not_given(self, value: Any) -> Any:
return value if value is not None else NOT_GIVEN
@ -741,7 +742,11 @@ class OpenAIChatCompletionsModel(Model):
# Handle Anthropic error for empty text content blocks
elif ("text content blocks must be non-empty" in str(e) or
"cache_control cannot be set for empty text blocks" in str(e)): # noqa
print(f"Error: {str(e)}")
# Print the error message only once
print(f"Error: {str(e)}") if not self.empty_content_error_shown else None
self.empty_content_error_shown = True
# Fix for empty content in messages for Anthropic models
kwargs["messages"] = [
msg if msg.get("content") not in [None, ""] else

1210
uv.lock

File diff suppressed because it is too large Load Diff