voxagent 0.1.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- voxagent/__init__.py +143 -0
- voxagent/_version.py +5 -0
- voxagent/agent/__init__.py +32 -0
- voxagent/agent/abort.py +178 -0
- voxagent/agent/core.py +902 -0
- voxagent/code/__init__.py +9 -0
- voxagent/mcp/__init__.py +16 -0
- voxagent/mcp/manager.py +188 -0
- voxagent/mcp/tool.py +152 -0
- voxagent/providers/__init__.py +110 -0
- voxagent/providers/anthropic.py +498 -0
- voxagent/providers/augment.py +293 -0
- voxagent/providers/auth.py +116 -0
- voxagent/providers/base.py +268 -0
- voxagent/providers/chatgpt.py +415 -0
- voxagent/providers/claudecode.py +162 -0
- voxagent/providers/cli_base.py +265 -0
- voxagent/providers/codex.py +183 -0
- voxagent/providers/failover.py +90 -0
- voxagent/providers/google.py +532 -0
- voxagent/providers/groq.py +96 -0
- voxagent/providers/ollama.py +425 -0
- voxagent/providers/openai.py +435 -0
- voxagent/providers/registry.py +175 -0
- voxagent/py.typed +1 -0
- voxagent/security/__init__.py +14 -0
- voxagent/security/events.py +75 -0
- voxagent/security/filter.py +169 -0
- voxagent/security/registry.py +87 -0
- voxagent/session/__init__.py +39 -0
- voxagent/session/compaction.py +237 -0
- voxagent/session/lock.py +103 -0
- voxagent/session/model.py +109 -0
- voxagent/session/storage.py +184 -0
- voxagent/streaming/__init__.py +52 -0
- voxagent/streaming/emitter.py +286 -0
- voxagent/streaming/events.py +255 -0
- voxagent/subagent/__init__.py +20 -0
- voxagent/subagent/context.py +124 -0
- voxagent/subagent/definition.py +172 -0
- voxagent/tools/__init__.py +32 -0
- voxagent/tools/context.py +50 -0
- voxagent/tools/decorator.py +175 -0
- voxagent/tools/definition.py +131 -0
- voxagent/tools/executor.py +109 -0
- voxagent/tools/policy.py +89 -0
- voxagent/tools/registry.py +89 -0
- voxagent/types/__init__.py +46 -0
- voxagent/types/messages.py +134 -0
- voxagent/types/run.py +176 -0
- voxagent-0.1.0.dist-info/METADATA +186 -0
- voxagent-0.1.0.dist-info/RECORD +53 -0
- voxagent-0.1.0.dist-info/WHEEL +4 -0
voxagent/mcp/__init__.py
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
"""Model Context Protocol (MCP) integration.
|
|
2
|
+
|
|
3
|
+
This subpackage provides:
|
|
4
|
+
- MCPToolDefinition: ToolDefinition subclass for MCP tools
|
|
5
|
+
- MCPServerManager: Manages MCP server lifecycle and tool extraction
|
|
6
|
+
- sanitize_tool_name: Utility to sanitize MCP tool names
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
from voxagent.mcp.manager import MCPServerManager
|
|
10
|
+
from voxagent.mcp.tool import MCPToolDefinition, sanitize_tool_name
|
|
11
|
+
|
|
12
|
+
__all__ = [
|
|
13
|
+
"MCPServerManager",
|
|
14
|
+
"MCPToolDefinition",
|
|
15
|
+
"sanitize_tool_name",
|
|
16
|
+
]
|
voxagent/mcp/manager.py
ADDED
|
@@ -0,0 +1,188 @@
|
|
|
1
|
+
"""MCP Server Manager for voxagent.
|
|
2
|
+
|
|
3
|
+
This module provides MCPServerManager, which handles:
|
|
4
|
+
- Connecting to MCP servers
|
|
5
|
+
- Extracting tools from MCP servers
|
|
6
|
+
- Converting MCP tools to ToolDefinition format
|
|
7
|
+
- Managing server lifecycle (connect/disconnect)
|
|
8
|
+
"""
|
|
9
|
+
|
|
10
|
+
from __future__ import annotations
|
|
11
|
+
|
|
12
|
+
import logging
|
|
13
|
+
from typing import Any
|
|
14
|
+
|
|
15
|
+
from voxagent.mcp.tool import MCPToolDefinition, sanitize_tool_name
|
|
16
|
+
from voxagent.tools.definition import ToolDefinition
|
|
17
|
+
|
|
18
|
+
logger = logging.getLogger(__name__)
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
class MCPServerManager:
|
|
22
|
+
"""Manages MCP server connections and tool extraction.
|
|
23
|
+
|
|
24
|
+
This class handles the lifecycle of MCP servers and provides methods
|
|
25
|
+
to extract and convert their tools to voxagent's ToolDefinition format.
|
|
26
|
+
"""
|
|
27
|
+
|
|
28
|
+
def __init__(self) -> None:
|
|
29
|
+
"""Initialize the MCPServerManager."""
|
|
30
|
+
self._servers: list[Any] = []
|
|
31
|
+
self._connected_servers: list[Any] = []
|
|
32
|
+
self._tools: list[ToolDefinition] = []
|
|
33
|
+
|
|
34
|
+
async def add_servers(self, servers: list[Any]) -> None:
|
|
35
|
+
"""Add MCP servers to manage.
|
|
36
|
+
|
|
37
|
+
Args:
|
|
38
|
+
servers: List of MCP server instances (PydanticAI MCP classes).
|
|
39
|
+
"""
|
|
40
|
+
self._servers.extend(servers)
|
|
41
|
+
|
|
42
|
+
async def connect_all(self) -> list[ToolDefinition]:
|
|
43
|
+
"""Connect to all MCP servers and extract their tools.
|
|
44
|
+
|
|
45
|
+
Returns:
|
|
46
|
+
List of ToolDefinition objects from all connected servers.
|
|
47
|
+
"""
|
|
48
|
+
self._tools = []
|
|
49
|
+
self._connected_servers = []
|
|
50
|
+
|
|
51
|
+
for server in self._servers:
|
|
52
|
+
try:
|
|
53
|
+
# Enter the async context manager
|
|
54
|
+
await server.__aenter__()
|
|
55
|
+
self._connected_servers.append(server)
|
|
56
|
+
|
|
57
|
+
# Extract tools from this server
|
|
58
|
+
tools = await self._extract_tools(server)
|
|
59
|
+
self._tools.extend(tools)
|
|
60
|
+
|
|
61
|
+
server_name = getattr(server, "name", None) or getattr(
|
|
62
|
+
server, "tool_prefix", "unknown"
|
|
63
|
+
)
|
|
64
|
+
logger.info(
|
|
65
|
+
f"Connected to MCP server '{server_name}' with {len(tools)} tools"
|
|
66
|
+
)
|
|
67
|
+
|
|
68
|
+
except Exception as e:
|
|
69
|
+
server_name = getattr(server, "name", None) or getattr(
|
|
70
|
+
server, "tool_prefix", "unknown"
|
|
71
|
+
)
|
|
72
|
+
logger.warning(f"Failed to connect to MCP server '{server_name}': {e}")
|
|
73
|
+
# Continue with other servers
|
|
74
|
+
|
|
75
|
+
return self._tools
|
|
76
|
+
|
|
77
|
+
async def disconnect_all(self) -> None:
|
|
78
|
+
"""Disconnect from all connected MCP servers.
|
|
79
|
+
|
|
80
|
+
Note: PydanticAI MCP servers use anyio cancel scopes, which must be
|
|
81
|
+
entered and exited from the same task. If called from a different task,
|
|
82
|
+
the disconnect will be skipped to avoid RuntimeError.
|
|
83
|
+
"""
|
|
84
|
+
for server in self._connected_servers:
|
|
85
|
+
try:
|
|
86
|
+
await server.__aexit__(None, None, None)
|
|
87
|
+
except RuntimeError as e:
|
|
88
|
+
# anyio cancel scope errors when called from different task
|
|
89
|
+
if "cancel scope" in str(e).lower() or "different task" in str(e).lower():
|
|
90
|
+
server_name = getattr(server, "name", None) or getattr(
|
|
91
|
+
server, "tool_prefix", "unknown"
|
|
92
|
+
)
|
|
93
|
+
logger.debug(
|
|
94
|
+
f"MCP server '{server_name}' disconnect skipped (task mismatch): {e}"
|
|
95
|
+
)
|
|
96
|
+
else:
|
|
97
|
+
raise
|
|
98
|
+
except Exception as e:
|
|
99
|
+
server_name = getattr(server, "name", None) or getattr(
|
|
100
|
+
server, "tool_prefix", "unknown"
|
|
101
|
+
)
|
|
102
|
+
logger.warning(f"Error disconnecting from MCP server '{server_name}': {e}")
|
|
103
|
+
|
|
104
|
+
self._connected_servers = []
|
|
105
|
+
self._tools = []
|
|
106
|
+
|
|
107
|
+
async def _extract_tools(self, server: Any) -> list[ToolDefinition]:
|
|
108
|
+
"""Extract tools from an MCP server and convert to ToolDefinition.
|
|
109
|
+
|
|
110
|
+
Args:
|
|
111
|
+
server: The connected MCP server instance.
|
|
112
|
+
|
|
113
|
+
Returns:
|
|
114
|
+
List of ToolDefinition objects.
|
|
115
|
+
"""
|
|
116
|
+
tools: list[ToolDefinition] = []
|
|
117
|
+
|
|
118
|
+
try:
|
|
119
|
+
# Get the server name/prefix for tool naming
|
|
120
|
+
server_name = getattr(server, "name", None) or getattr(
|
|
121
|
+
server, "tool_prefix", None
|
|
122
|
+
)
|
|
123
|
+
|
|
124
|
+
# List tools from the MCP server
|
|
125
|
+
mcp_tools = await server.list_tools()
|
|
126
|
+
|
|
127
|
+
for mcp_tool in mcp_tools:
|
|
128
|
+
try:
|
|
129
|
+
tool_def = self._convert_mcp_tool(mcp_tool, server, server_name)
|
|
130
|
+
tools.append(tool_def)
|
|
131
|
+
except Exception as e:
|
|
132
|
+
tool_name = getattr(mcp_tool, "name", "unknown")
|
|
133
|
+
logger.warning(f"Failed to convert MCP tool '{tool_name}': {e}")
|
|
134
|
+
|
|
135
|
+
except Exception as e:
|
|
136
|
+
logger.warning(f"Failed to list tools from MCP server: {e}")
|
|
137
|
+
|
|
138
|
+
return tools
|
|
139
|
+
|
|
140
|
+
def _convert_mcp_tool(
|
|
141
|
+
self, mcp_tool: Any, server: Any, server_name: str | None
|
|
142
|
+
) -> MCPToolDefinition:
|
|
143
|
+
"""Convert an MCP tool to MCPToolDefinition.
|
|
144
|
+
|
|
145
|
+
Args:
|
|
146
|
+
mcp_tool: The MCP tool object from list_tools().
|
|
147
|
+
server: The MCP server instance.
|
|
148
|
+
server_name: The server name/prefix.
|
|
149
|
+
|
|
150
|
+
Returns:
|
|
151
|
+
MCPToolDefinition instance.
|
|
152
|
+
"""
|
|
153
|
+
# Get tool attributes
|
|
154
|
+
original_name = mcp_tool.name
|
|
155
|
+
description = mcp_tool.description or f"MCP tool: {original_name}"
|
|
156
|
+
|
|
157
|
+
# Build the full tool name with prefix
|
|
158
|
+
if server_name:
|
|
159
|
+
full_name = f"{server_name}_{sanitize_tool_name(original_name)}"
|
|
160
|
+
else:
|
|
161
|
+
full_name = sanitize_tool_name(original_name)
|
|
162
|
+
|
|
163
|
+
# Get input schema (parameters)
|
|
164
|
+
parameters: dict[str, Any] = {"type": "object", "properties": {}}
|
|
165
|
+
if hasattr(mcp_tool, "inputSchema") and mcp_tool.inputSchema:
|
|
166
|
+
parameters = mcp_tool.inputSchema
|
|
167
|
+
elif hasattr(mcp_tool, "input_schema") and mcp_tool.input_schema:
|
|
168
|
+
parameters = mcp_tool.input_schema
|
|
169
|
+
|
|
170
|
+
return MCPToolDefinition(
|
|
171
|
+
name=full_name,
|
|
172
|
+
description=description,
|
|
173
|
+
parameters=parameters,
|
|
174
|
+
mcp_server=server,
|
|
175
|
+
original_tool_name=original_name,
|
|
176
|
+
server_name=server_name or "unknown",
|
|
177
|
+
)
|
|
178
|
+
|
|
179
|
+
@property
|
|
180
|
+
def tools(self) -> list[ToolDefinition]:
|
|
181
|
+
"""Get all extracted tools."""
|
|
182
|
+
return self._tools
|
|
183
|
+
|
|
184
|
+
@property
|
|
185
|
+
def connected_server_count(self) -> int:
|
|
186
|
+
"""Get the number of connected servers."""
|
|
187
|
+
return len(self._connected_servers)
|
|
188
|
+
|
voxagent/mcp/tool.py
ADDED
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
"""MCP Tool Definition for voxagent.
|
|
2
|
+
|
|
3
|
+
This module provides MCPToolDefinition, a ToolDefinition subclass that wraps
|
|
4
|
+
MCP server tools and forwards execution calls to the MCP server.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from __future__ import annotations
|
|
8
|
+
|
|
9
|
+
import re
|
|
10
|
+
from typing import TYPE_CHECKING, Any
|
|
11
|
+
|
|
12
|
+
from voxagent.tools.definition import ToolContext, ToolDefinition
|
|
13
|
+
|
|
14
|
+
if TYPE_CHECKING:
|
|
15
|
+
pass
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
class MCPToolDefinition(ToolDefinition):
|
|
19
|
+
"""A ToolDefinition that wraps an MCP server tool.
|
|
20
|
+
|
|
21
|
+
This class forwards tool execution to the MCP server that provides the tool.
|
|
22
|
+
It stores a reference to the MCP server and the original tool name.
|
|
23
|
+
"""
|
|
24
|
+
|
|
25
|
+
def __init__(
|
|
26
|
+
self,
|
|
27
|
+
name: str,
|
|
28
|
+
description: str,
|
|
29
|
+
parameters: dict[str, Any],
|
|
30
|
+
mcp_server: Any,
|
|
31
|
+
original_tool_name: str,
|
|
32
|
+
server_name: str,
|
|
33
|
+
) -> None:
|
|
34
|
+
"""Initialize MCPToolDefinition.
|
|
35
|
+
|
|
36
|
+
Args:
|
|
37
|
+
name: The sanitized tool name (alphanumeric and underscores).
|
|
38
|
+
description: Tool description for the LLM.
|
|
39
|
+
parameters: JSON Schema for the tool's parameters.
|
|
40
|
+
mcp_server: The MCP server instance that provides this tool.
|
|
41
|
+
original_tool_name: The original tool name from the MCP server.
|
|
42
|
+
server_name: The name of the MCP server (for logging/debugging).
|
|
43
|
+
"""
|
|
44
|
+
# Don't call parent __init__ with execute - we override run() instead
|
|
45
|
+
# Validate name manually
|
|
46
|
+
if not re.match(r"^[a-zA-Z_][a-zA-Z0-9_]*$", name):
|
|
47
|
+
raise ValueError(
|
|
48
|
+
f"Tool name '{name}' is invalid. "
|
|
49
|
+
"Must contain only alphanumeric characters and underscores, "
|
|
50
|
+
"and cannot start with a digit."
|
|
51
|
+
)
|
|
52
|
+
|
|
53
|
+
self.name = name
|
|
54
|
+
self.description = description
|
|
55
|
+
self.parameters = parameters
|
|
56
|
+
self.is_async = True # MCP calls are always async
|
|
57
|
+
|
|
58
|
+
# MCP-specific attributes
|
|
59
|
+
self._mcp_server = mcp_server
|
|
60
|
+
self._original_tool_name = original_tool_name
|
|
61
|
+
self._server_name = server_name
|
|
62
|
+
|
|
63
|
+
# Set execute to None - we override run() instead
|
|
64
|
+
self.execute = self._execute_mcp_tool
|
|
65
|
+
|
|
66
|
+
async def _execute_mcp_tool(self, **params: Any) -> Any:
|
|
67
|
+
"""Execute the MCP tool by calling the MCP server.
|
|
68
|
+
|
|
69
|
+
This is called by the parent's run() method, but we override run()
|
|
70
|
+
to handle MCP-specific execution.
|
|
71
|
+
"""
|
|
72
|
+
# This shouldn't be called directly - run() handles execution
|
|
73
|
+
raise NotImplementedError("Use run() instead")
|
|
74
|
+
|
|
75
|
+
async def run(self, params: dict[str, Any], context: ToolContext) -> Any:
|
|
76
|
+
"""Execute the tool by forwarding to the MCP server.
|
|
77
|
+
|
|
78
|
+
Args:
|
|
79
|
+
params: Dictionary of parameter values.
|
|
80
|
+
context: The ToolContext for this execution.
|
|
81
|
+
|
|
82
|
+
Returns:
|
|
83
|
+
The result from the MCP server tool execution.
|
|
84
|
+
"""
|
|
85
|
+
try:
|
|
86
|
+
# Call the MCP server's direct_call_tool method
|
|
87
|
+
# This bypasses PydanticAI's context requirements
|
|
88
|
+
result = await self._mcp_server.direct_call_tool(
|
|
89
|
+
self._original_tool_name,
|
|
90
|
+
params,
|
|
91
|
+
)
|
|
92
|
+
|
|
93
|
+
# MCP returns a list of content items, extract text
|
|
94
|
+
if hasattr(result, "content") and result.content:
|
|
95
|
+
# Concatenate all text content
|
|
96
|
+
texts = []
|
|
97
|
+
for item in result.content:
|
|
98
|
+
if hasattr(item, "text"):
|
|
99
|
+
texts.append(item.text)
|
|
100
|
+
elif isinstance(item, dict) and "text" in item:
|
|
101
|
+
texts.append(item["text"])
|
|
102
|
+
else:
|
|
103
|
+
texts.append(str(item))
|
|
104
|
+
return "\n".join(texts) if texts else str(result)
|
|
105
|
+
|
|
106
|
+
return str(result)
|
|
107
|
+
|
|
108
|
+
except Exception as e:
|
|
109
|
+
# Return error as string - the executor will handle it
|
|
110
|
+
return f"MCP tool error ({self._server_name}/{self._original_tool_name}): {e}"
|
|
111
|
+
|
|
112
|
+
@property
|
|
113
|
+
def mcp_server(self) -> Any:
|
|
114
|
+
"""Get the MCP server instance."""
|
|
115
|
+
return self._mcp_server
|
|
116
|
+
|
|
117
|
+
@property
|
|
118
|
+
def original_tool_name(self) -> str:
|
|
119
|
+
"""Get the original MCP tool name."""
|
|
120
|
+
return self._original_tool_name
|
|
121
|
+
|
|
122
|
+
@property
|
|
123
|
+
def server_name(self) -> str:
|
|
124
|
+
"""Get the MCP server name."""
|
|
125
|
+
return self._server_name
|
|
126
|
+
|
|
127
|
+
|
|
128
|
+
def sanitize_tool_name(name: str) -> str:
|
|
129
|
+
"""Sanitize a tool name to be valid for voxagent.
|
|
130
|
+
|
|
131
|
+
MCP tool names may contain dashes or other characters that are not
|
|
132
|
+
valid in Python identifiers. This function converts them to underscores.
|
|
133
|
+
|
|
134
|
+
Args:
|
|
135
|
+
name: The original tool name.
|
|
136
|
+
|
|
137
|
+
Returns:
|
|
138
|
+
A sanitized tool name with only alphanumeric characters and underscores.
|
|
139
|
+
"""
|
|
140
|
+
# Replace dashes and other invalid characters with underscores
|
|
141
|
+
sanitized = re.sub(r"[^a-zA-Z0-9_]", "_", name)
|
|
142
|
+
|
|
143
|
+
# Ensure it doesn't start with a digit
|
|
144
|
+
if sanitized and sanitized[0].isdigit():
|
|
145
|
+
sanitized = "_" + sanitized
|
|
146
|
+
|
|
147
|
+
# Ensure it's not empty
|
|
148
|
+
if not sanitized:
|
|
149
|
+
sanitized = "unnamed_tool"
|
|
150
|
+
|
|
151
|
+
return sanitized
|
|
152
|
+
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
"""LLM provider implementations.
|
|
2
|
+
|
|
3
|
+
This subpackage contains unified interfaces for various LLM providers:
|
|
4
|
+
- OpenAI (GPT-4, GPT-4o)
|
|
5
|
+
- Anthropic (Claude)
|
|
6
|
+
- Google (Gemini)
|
|
7
|
+
- Groq (Llama, Mixtral)
|
|
8
|
+
- Ollama (local models)
|
|
9
|
+
- ChatGPT (ChatGPT Plus backend)
|
|
10
|
+
- Augment (Auggie CLI)
|
|
11
|
+
- Codex (OpenAI Codex CLI)
|
|
12
|
+
- ClaudeCode (Claude Code CLI)
|
|
13
|
+
"""
|
|
14
|
+
|
|
15
|
+
from voxagent.providers.anthropic import AnthropicProvider
|
|
16
|
+
from voxagent.providers.augment import AugmentProvider
|
|
17
|
+
from voxagent.providers.auth import (
|
|
18
|
+
AuthProfile,
|
|
19
|
+
AuthProfileManager,
|
|
20
|
+
FailoverError,
|
|
21
|
+
FailoverReason,
|
|
22
|
+
)
|
|
23
|
+
from voxagent.providers.base import (
|
|
24
|
+
AbortSignal,
|
|
25
|
+
BaseProvider,
|
|
26
|
+
ErrorChunk,
|
|
27
|
+
MessageEndChunk,
|
|
28
|
+
StreamChunk,
|
|
29
|
+
TextDeltaChunk,
|
|
30
|
+
ToolUseChunk,
|
|
31
|
+
)
|
|
32
|
+
from voxagent.providers.chatgpt import ChatGPTProvider
|
|
33
|
+
from voxagent.providers.claudecode import ClaudeCodeProvider
|
|
34
|
+
from voxagent.providers.cli_base import CLINotFoundError, CLIProvider
|
|
35
|
+
from voxagent.providers.codex import CodexProvider
|
|
36
|
+
from voxagent.providers.failover import (
|
|
37
|
+
FailoverExhaustedError,
|
|
38
|
+
NoProfilesAvailableError,
|
|
39
|
+
run_with_failover,
|
|
40
|
+
)
|
|
41
|
+
from voxagent.providers.google import GoogleProvider
|
|
42
|
+
from voxagent.providers.groq import GroqProvider
|
|
43
|
+
from voxagent.providers.ollama import OllamaProvider
|
|
44
|
+
from voxagent.providers.openai import OpenAIProvider
|
|
45
|
+
from voxagent.providers.registry import (
|
|
46
|
+
InvalidModelStringError,
|
|
47
|
+
ProviderNotFoundError,
|
|
48
|
+
ProviderRegistry,
|
|
49
|
+
get_default_registry,
|
|
50
|
+
)
|
|
51
|
+
|
|
52
|
+
__all__ = [
|
|
53
|
+
"AbortSignal",
|
|
54
|
+
"AnthropicProvider",
|
|
55
|
+
"AugmentProvider",
|
|
56
|
+
"AuthProfile",
|
|
57
|
+
"AuthProfileManager",
|
|
58
|
+
"BaseProvider",
|
|
59
|
+
"ChatGPTProvider",
|
|
60
|
+
"CLINotFoundError",
|
|
61
|
+
"CLIProvider",
|
|
62
|
+
"ClaudeCodeProvider",
|
|
63
|
+
"CodexProvider",
|
|
64
|
+
"ErrorChunk",
|
|
65
|
+
"FailoverError",
|
|
66
|
+
"FailoverExhaustedError",
|
|
67
|
+
"FailoverReason",
|
|
68
|
+
"GoogleProvider",
|
|
69
|
+
"GroqProvider",
|
|
70
|
+
"InvalidModelStringError",
|
|
71
|
+
"MessageEndChunk",
|
|
72
|
+
"NoProfilesAvailableError",
|
|
73
|
+
"OllamaProvider",
|
|
74
|
+
"OpenAIProvider",
|
|
75
|
+
"ProviderNotFoundError",
|
|
76
|
+
"ProviderRegistry",
|
|
77
|
+
"StreamChunk",
|
|
78
|
+
"TextDeltaChunk",
|
|
79
|
+
"ToolUseChunk",
|
|
80
|
+
"get_default_registry",
|
|
81
|
+
"run_with_failover",
|
|
82
|
+
]
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
# =============================================================================
|
|
86
|
+
# Register default providers
|
|
87
|
+
# =============================================================================
|
|
88
|
+
|
|
89
|
+
def _register_default_providers() -> None:
|
|
90
|
+
"""Register all built-in providers with the default registry."""
|
|
91
|
+
registry = get_default_registry()
|
|
92
|
+
|
|
93
|
+
# Only register if not already registered (idempotent)
|
|
94
|
+
if not registry.list_providers():
|
|
95
|
+
# HTTP API providers
|
|
96
|
+
registry.register("openai", OpenAIProvider)
|
|
97
|
+
registry.register("anthropic", AnthropicProvider)
|
|
98
|
+
registry.register("google", GoogleProvider)
|
|
99
|
+
registry.register("groq", GroqProvider)
|
|
100
|
+
registry.register("ollama", OllamaProvider)
|
|
101
|
+
registry.register("chatgpt", ChatGPTProvider)
|
|
102
|
+
# CLI-wrapped providers
|
|
103
|
+
registry.register("augment", AugmentProvider)
|
|
104
|
+
registry.register("codex", CodexProvider)
|
|
105
|
+
registry.register("claudecode", ClaudeCodeProvider)
|
|
106
|
+
|
|
107
|
+
|
|
108
|
+
# Auto-register providers on module import
|
|
109
|
+
_register_default_providers()
|
|
110
|
+
|