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/__init__.py
ADDED
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
"""voxagent - A lightweight, model-agnostic LLM provider abstraction.
|
|
2
|
+
|
|
3
|
+
voxagent provides:
|
|
4
|
+
- Multi-Provider: Unified interface for OpenAI, Anthropic, Google, Groq, Ollama
|
|
5
|
+
- Streaming: Typed StreamChunk union (TextDelta, ToolUse, MessageEnd, Error)
|
|
6
|
+
- Tool System: @tool decorator, typed definitions, abort signal propagation
|
|
7
|
+
- MCP Integration: First-class Model Context Protocol support
|
|
8
|
+
- Sub-Agent Support: Hierarchical agent composition with depth-limited delegation
|
|
9
|
+
- Session Management: File-based sessions with context compaction
|
|
10
|
+
|
|
11
|
+
Quick Start:
|
|
12
|
+
>>> from voxagent import Agent
|
|
13
|
+
>>> agent = Agent(model="openai:gpt-4o")
|
|
14
|
+
>>> result = await agent.run("Hello!")
|
|
15
|
+
|
|
16
|
+
With Tools:
|
|
17
|
+
>>> from voxagent import Agent
|
|
18
|
+
>>> from voxagent.tools import tool
|
|
19
|
+
>>>
|
|
20
|
+
>>> @tool()
|
|
21
|
+
... def get_weather(city: str) -> str:
|
|
22
|
+
... '''Get weather for a city.'''
|
|
23
|
+
... return f"Sunny in {city}"
|
|
24
|
+
>>>
|
|
25
|
+
>>> agent = Agent(model="anthropic:claude-3-5-sonnet", tools=[get_weather])
|
|
26
|
+
>>> result = await agent.run("What's the weather in Paris?")
|
|
27
|
+
|
|
28
|
+
Streaming:
|
|
29
|
+
>>> from voxagent import Agent
|
|
30
|
+
>>> from voxagent.providers import TextDeltaChunk
|
|
31
|
+
>>>
|
|
32
|
+
>>> agent = Agent(model="openai:gpt-4o")
|
|
33
|
+
>>> async for chunk in agent.stream("Tell me a story"):
|
|
34
|
+
... if isinstance(chunk, TextDeltaChunk):
|
|
35
|
+
... print(chunk.delta, end="")
|
|
36
|
+
|
|
37
|
+
For more information, see: https://github.com/voxdomus/voxagent
|
|
38
|
+
"""
|
|
39
|
+
|
|
40
|
+
from ._version import __version__, __version_info__
|
|
41
|
+
|
|
42
|
+
# =============================================================================
|
|
43
|
+
# Lazy imports for top-level convenience
|
|
44
|
+
# =============================================================================
|
|
45
|
+
# We use __getattr__ to avoid importing the full dependency chain on module load.
|
|
46
|
+
# This keeps `import voxagent` fast and allows users to import only what they need.
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
def __getattr__(name: str) -> object:
|
|
50
|
+
"""Lazy import for top-level classes."""
|
|
51
|
+
# Core Agent class
|
|
52
|
+
if name == "Agent":
|
|
53
|
+
from .agent import Agent
|
|
54
|
+
|
|
55
|
+
return Agent
|
|
56
|
+
|
|
57
|
+
# Provider base classes and chunks
|
|
58
|
+
if name in (
|
|
59
|
+
"BaseProvider",
|
|
60
|
+
"StreamChunk",
|
|
61
|
+
"TextDeltaChunk",
|
|
62
|
+
"ToolUseChunk",
|
|
63
|
+
"MessageEndChunk",
|
|
64
|
+
"ErrorChunk",
|
|
65
|
+
"AbortSignal",
|
|
66
|
+
):
|
|
67
|
+
from . import providers
|
|
68
|
+
|
|
69
|
+
return getattr(providers, name)
|
|
70
|
+
|
|
71
|
+
# Tool system
|
|
72
|
+
if name in ("tool", "ToolDefinition", "ToolContext"):
|
|
73
|
+
from . import tools
|
|
74
|
+
|
|
75
|
+
return getattr(tools, name)
|
|
76
|
+
|
|
77
|
+
# Message types
|
|
78
|
+
if name in ("Message", "ToolCall", "ToolResult"):
|
|
79
|
+
from . import types
|
|
80
|
+
|
|
81
|
+
return getattr(types, name)
|
|
82
|
+
|
|
83
|
+
# Sub-agent support
|
|
84
|
+
if name == "SubAgentDefinition":
|
|
85
|
+
from .subagent import SubAgentDefinition
|
|
86
|
+
|
|
87
|
+
return SubAgentDefinition
|
|
88
|
+
if name == "SubAgentContext":
|
|
89
|
+
from .subagent import SubAgentContext
|
|
90
|
+
|
|
91
|
+
return SubAgentContext
|
|
92
|
+
if name == "MaxDepthExceededError":
|
|
93
|
+
from .subagent import MaxDepthExceededError
|
|
94
|
+
|
|
95
|
+
return MaxDepthExceededError
|
|
96
|
+
|
|
97
|
+
# MCP
|
|
98
|
+
if name == "MCPServerManager":
|
|
99
|
+
from .mcp import MCPServerManager
|
|
100
|
+
|
|
101
|
+
return MCPServerManager
|
|
102
|
+
|
|
103
|
+
# Registry
|
|
104
|
+
if name in ("ProviderRegistry", "get_default_registry"):
|
|
105
|
+
from . import providers
|
|
106
|
+
|
|
107
|
+
return getattr(providers, name)
|
|
108
|
+
|
|
109
|
+
raise AttributeError(f"module {__name__!r} has no attribute {name!r}")
|
|
110
|
+
|
|
111
|
+
|
|
112
|
+
__all__ = [
|
|
113
|
+
# Version
|
|
114
|
+
"__version__",
|
|
115
|
+
"__version_info__",
|
|
116
|
+
# Core
|
|
117
|
+
"Agent",
|
|
118
|
+
# Providers
|
|
119
|
+
"BaseProvider",
|
|
120
|
+
"StreamChunk",
|
|
121
|
+
"TextDeltaChunk",
|
|
122
|
+
"ToolUseChunk",
|
|
123
|
+
"MessageEndChunk",
|
|
124
|
+
"ErrorChunk",
|
|
125
|
+
"AbortSignal",
|
|
126
|
+
"ProviderRegistry",
|
|
127
|
+
"get_default_registry",
|
|
128
|
+
# Tools
|
|
129
|
+
"tool",
|
|
130
|
+
"ToolDefinition",
|
|
131
|
+
"ToolContext",
|
|
132
|
+
# Types
|
|
133
|
+
"Message",
|
|
134
|
+
"ToolCall",
|
|
135
|
+
"ToolResult",
|
|
136
|
+
# Sub-agents
|
|
137
|
+
"SubAgentDefinition",
|
|
138
|
+
"SubAgentContext",
|
|
139
|
+
"MaxDepthExceededError",
|
|
140
|
+
# MCP
|
|
141
|
+
"MCPServerManager",
|
|
142
|
+
]
|
|
143
|
+
|
voxagent/_version.py
ADDED
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
"""Agent core module for voxagent.
|
|
2
|
+
|
|
3
|
+
This subpackage provides:
|
|
4
|
+
- Agent class for managing AI agent interactions
|
|
5
|
+
- AbortController for abort signal management
|
|
6
|
+
- TimeoutHandler for timeout-based abort triggering
|
|
7
|
+
- Error recovery and failover handling
|
|
8
|
+
"""
|
|
9
|
+
|
|
10
|
+
from voxagent.agent.abort import (
|
|
11
|
+
AbortController,
|
|
12
|
+
AllProfilesExhausted,
|
|
13
|
+
FailoverError,
|
|
14
|
+
FailoverReason,
|
|
15
|
+
TimeoutHandler,
|
|
16
|
+
handle_context_overflow,
|
|
17
|
+
)
|
|
18
|
+
from voxagent.agent.core import Agent
|
|
19
|
+
|
|
20
|
+
# Import providers to ensure they are registered with the default registry
|
|
21
|
+
import voxagent.providers # noqa: F401
|
|
22
|
+
|
|
23
|
+
__all__ = [
|
|
24
|
+
"AbortController",
|
|
25
|
+
"Agent",
|
|
26
|
+
"AllProfilesExhausted",
|
|
27
|
+
"FailoverError",
|
|
28
|
+
"FailoverReason",
|
|
29
|
+
"TimeoutHandler",
|
|
30
|
+
"handle_context_overflow",
|
|
31
|
+
]
|
|
32
|
+
|
voxagent/agent/abort.py
ADDED
|
@@ -0,0 +1,178 @@
|
|
|
1
|
+
"""Abort and error handling for voxagent agent runs.
|
|
2
|
+
|
|
3
|
+
This module provides:
|
|
4
|
+
- AbortController: Controller for aborting agent runs
|
|
5
|
+
- TimeoutHandler: Handles timeout for agent runs
|
|
6
|
+
- handle_context_overflow: Handle context overflow errors
|
|
7
|
+
- FailoverError: Error for failover scenarios (re-exported from auth)
|
|
8
|
+
- FailoverReason: Enum for failover reasons (re-exported from auth)
|
|
9
|
+
- AllProfilesExhausted: Error when all auth profiles are exhausted
|
|
10
|
+
"""
|
|
11
|
+
|
|
12
|
+
from __future__ import annotations
|
|
13
|
+
|
|
14
|
+
import asyncio
|
|
15
|
+
from typing import TYPE_CHECKING
|
|
16
|
+
|
|
17
|
+
from voxagent.providers.auth import FailoverError, FailoverReason
|
|
18
|
+
from voxagent.providers.base import AbortSignal
|
|
19
|
+
from voxagent.session.compaction import compact_context
|
|
20
|
+
|
|
21
|
+
if TYPE_CHECKING:
|
|
22
|
+
from voxagent.types.messages import Message
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
# =============================================================================
|
|
26
|
+
# Custom Error Types
|
|
27
|
+
# =============================================================================
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
class AllProfilesExhausted(Exception):
|
|
31
|
+
"""Error raised when all auth profiles have been exhausted."""
|
|
32
|
+
|
|
33
|
+
def __init__(self, message: str = "All authentication profiles exhausted") -> None:
|
|
34
|
+
"""Initialize AllProfilesExhausted.
|
|
35
|
+
|
|
36
|
+
Args:
|
|
37
|
+
message: Error message.
|
|
38
|
+
"""
|
|
39
|
+
super().__init__(message)
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
# =============================================================================
|
|
43
|
+
# AbortController
|
|
44
|
+
# =============================================================================
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
class AbortController:
|
|
48
|
+
"""Controller for aborting agent runs.
|
|
49
|
+
|
|
50
|
+
Provides a signal that can be checked by tools and providers to abort
|
|
51
|
+
operations, plus cleanup capabilities.
|
|
52
|
+
"""
|
|
53
|
+
|
|
54
|
+
def __init__(self) -> None:
|
|
55
|
+
"""Initialize the abort controller."""
|
|
56
|
+
self._signal = AbortSignal()
|
|
57
|
+
self._cleaned_up = False
|
|
58
|
+
|
|
59
|
+
@property
|
|
60
|
+
def signal(self) -> AbortSignal:
|
|
61
|
+
"""Get the abort signal."""
|
|
62
|
+
return self._signal
|
|
63
|
+
|
|
64
|
+
def abort(self, reason: str = "Aborted") -> None:
|
|
65
|
+
"""Trigger abort.
|
|
66
|
+
|
|
67
|
+
Args:
|
|
68
|
+
reason: The reason for aborting.
|
|
69
|
+
"""
|
|
70
|
+
self._signal._aborted = True
|
|
71
|
+
self._signal._reason = reason
|
|
72
|
+
|
|
73
|
+
def cleanup(self) -> None:
|
|
74
|
+
"""Cleanup resources. Safe to call multiple times."""
|
|
75
|
+
if not self._cleaned_up:
|
|
76
|
+
self._cleaned_up = True
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
# =============================================================================
|
|
80
|
+
# TimeoutHandler
|
|
81
|
+
# =============================================================================
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
class TimeoutHandler:
|
|
85
|
+
"""Handles timeout for agent runs."""
|
|
86
|
+
|
|
87
|
+
def __init__(self, timeout_ms: int) -> None:
|
|
88
|
+
"""Initialize the timeout handler.
|
|
89
|
+
|
|
90
|
+
Args:
|
|
91
|
+
timeout_ms: Timeout in milliseconds.
|
|
92
|
+
"""
|
|
93
|
+
self.timeout_ms = timeout_ms
|
|
94
|
+
self._task: asyncio.Task | None = None
|
|
95
|
+
self._expired = False
|
|
96
|
+
self._started = False
|
|
97
|
+
|
|
98
|
+
async def start(self, abort_controller: AbortController) -> None:
|
|
99
|
+
"""Start timeout timer.
|
|
100
|
+
|
|
101
|
+
Args:
|
|
102
|
+
abort_controller: The abort controller to trigger on timeout.
|
|
103
|
+
"""
|
|
104
|
+
if self._started:
|
|
105
|
+
return
|
|
106
|
+
self._started = True
|
|
107
|
+
|
|
108
|
+
async def _timeout_task() -> None:
|
|
109
|
+
await asyncio.sleep(self.timeout_ms / 1000.0)
|
|
110
|
+
self._expired = True
|
|
111
|
+
abort_controller.abort(f"Timeout after {self.timeout_ms}ms")
|
|
112
|
+
|
|
113
|
+
self._task = asyncio.create_task(_timeout_task())
|
|
114
|
+
|
|
115
|
+
def cancel(self) -> None:
|
|
116
|
+
"""Cancel timeout timer."""
|
|
117
|
+
if self._task and not self._task.done():
|
|
118
|
+
self._task.cancel()
|
|
119
|
+
|
|
120
|
+
@property
|
|
121
|
+
def expired(self) -> bool:
|
|
122
|
+
"""Check if timeout expired."""
|
|
123
|
+
return self._expired
|
|
124
|
+
|
|
125
|
+
|
|
126
|
+
# =============================================================================
|
|
127
|
+
# Context Overflow Handler
|
|
128
|
+
# =============================================================================
|
|
129
|
+
|
|
130
|
+
|
|
131
|
+
async def handle_context_overflow(
|
|
132
|
+
messages: list[Message],
|
|
133
|
+
error: Exception,
|
|
134
|
+
model: str,
|
|
135
|
+
) -> list[Message]:
|
|
136
|
+
"""Handle context overflow error.
|
|
137
|
+
|
|
138
|
+
Applies aggressive compaction to reduce context size.
|
|
139
|
+
|
|
140
|
+
Args:
|
|
141
|
+
messages: The messages that caused the overflow.
|
|
142
|
+
error: The context overflow error.
|
|
143
|
+
model: The model name.
|
|
144
|
+
|
|
145
|
+
Returns:
|
|
146
|
+
Compacted list of messages.
|
|
147
|
+
"""
|
|
148
|
+
if not messages:
|
|
149
|
+
return []
|
|
150
|
+
|
|
151
|
+
# Determine max tokens based on model
|
|
152
|
+
max_tokens = 4000 # Conservative default
|
|
153
|
+
if "gpt-4" in model:
|
|
154
|
+
max_tokens = 128000
|
|
155
|
+
elif "claude" in model:
|
|
156
|
+
max_tokens = 200000
|
|
157
|
+
elif "gpt-3.5" in model:
|
|
158
|
+
max_tokens = 16000
|
|
159
|
+
|
|
160
|
+
# Compact to 30% of max for safety margin after overflow
|
|
161
|
+
target_tokens = int(max_tokens * 0.3)
|
|
162
|
+
|
|
163
|
+
# Use aggressive compaction with minimal preserve_recent
|
|
164
|
+
return compact_context(messages, target_tokens, preserve_recent=2, model=model, aggressive=True)
|
|
165
|
+
|
|
166
|
+
|
|
167
|
+
# =============================================================================
|
|
168
|
+
# Exports
|
|
169
|
+
# =============================================================================
|
|
170
|
+
|
|
171
|
+
__all__ = [
|
|
172
|
+
"AbortController",
|
|
173
|
+
"TimeoutHandler",
|
|
174
|
+
"handle_context_overflow",
|
|
175
|
+
"FailoverError",
|
|
176
|
+
"FailoverReason",
|
|
177
|
+
"AllProfilesExhausted",
|
|
178
|
+
]
|