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.
Files changed (53) hide show
  1. voxagent/__init__.py +143 -0
  2. voxagent/_version.py +5 -0
  3. voxagent/agent/__init__.py +32 -0
  4. voxagent/agent/abort.py +178 -0
  5. voxagent/agent/core.py +902 -0
  6. voxagent/code/__init__.py +9 -0
  7. voxagent/mcp/__init__.py +16 -0
  8. voxagent/mcp/manager.py +188 -0
  9. voxagent/mcp/tool.py +152 -0
  10. voxagent/providers/__init__.py +110 -0
  11. voxagent/providers/anthropic.py +498 -0
  12. voxagent/providers/augment.py +293 -0
  13. voxagent/providers/auth.py +116 -0
  14. voxagent/providers/base.py +268 -0
  15. voxagent/providers/chatgpt.py +415 -0
  16. voxagent/providers/claudecode.py +162 -0
  17. voxagent/providers/cli_base.py +265 -0
  18. voxagent/providers/codex.py +183 -0
  19. voxagent/providers/failover.py +90 -0
  20. voxagent/providers/google.py +532 -0
  21. voxagent/providers/groq.py +96 -0
  22. voxagent/providers/ollama.py +425 -0
  23. voxagent/providers/openai.py +435 -0
  24. voxagent/providers/registry.py +175 -0
  25. voxagent/py.typed +1 -0
  26. voxagent/security/__init__.py +14 -0
  27. voxagent/security/events.py +75 -0
  28. voxagent/security/filter.py +169 -0
  29. voxagent/security/registry.py +87 -0
  30. voxagent/session/__init__.py +39 -0
  31. voxagent/session/compaction.py +237 -0
  32. voxagent/session/lock.py +103 -0
  33. voxagent/session/model.py +109 -0
  34. voxagent/session/storage.py +184 -0
  35. voxagent/streaming/__init__.py +52 -0
  36. voxagent/streaming/emitter.py +286 -0
  37. voxagent/streaming/events.py +255 -0
  38. voxagent/subagent/__init__.py +20 -0
  39. voxagent/subagent/context.py +124 -0
  40. voxagent/subagent/definition.py +172 -0
  41. voxagent/tools/__init__.py +32 -0
  42. voxagent/tools/context.py +50 -0
  43. voxagent/tools/decorator.py +175 -0
  44. voxagent/tools/definition.py +131 -0
  45. voxagent/tools/executor.py +109 -0
  46. voxagent/tools/policy.py +89 -0
  47. voxagent/tools/registry.py +89 -0
  48. voxagent/types/__init__.py +46 -0
  49. voxagent/types/messages.py +134 -0
  50. voxagent/types/run.py +176 -0
  51. voxagent-0.1.0.dist-info/METADATA +186 -0
  52. voxagent-0.1.0.dist-info/RECORD +53 -0
  53. 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,5 @@
1
+ """Version information for voxagent."""
2
+
3
+ __version__ = "0.1.0"
4
+ __version_info__ = tuple(int(x) for x in __version__.split("."))
5
+
@@ -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
+
@@ -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
+ ]