agentspan 0.0.3__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.
agentspan/__init__.py ADDED
@@ -0,0 +1,3 @@
1
+ # Copyright (c) 2025 AgentSpan
2
+ # Licensed under the MIT License. See LICENSE file in the project root for details.
3
+
@@ -0,0 +1,198 @@
1
+ # Copyright (c) 2025 AgentSpan
2
+ # Licensed under the MIT License. See LICENSE file in the project root for details.
3
+
4
+ """Conductor Agents SDK — durable, scalable, observable AI agents.
5
+
6
+ This is the public API surface. Import everything you need from here::
7
+
8
+ from agentspan.agents import Agent, AgentRuntime, tool
9
+
10
+ Quick start::
11
+
12
+ from agentspan.agents import Agent, AgentRuntime, tool
13
+
14
+ @tool
15
+ def get_weather(city: str) -> str:
16
+ \"\"\"Get current weather for a city.\"\"\"
17
+ return f"72F and sunny in {city}"
18
+
19
+ agent = Agent(name="weatherbot", model="openai/gpt-4o", tools=[get_weather])
20
+
21
+ with AgentRuntime() as runtime:
22
+ result = runtime.run(agent, "What's the weather in NYC?")
23
+ print(result.output)
24
+ """
25
+
26
+ # Core primitive
27
+ from agentspan.agents.agent import Agent, AgentDef, PromptTemplate, Strategy, agent
28
+
29
+ # Tool decorator and constructors
30
+ from agentspan.agents.tool import (
31
+ ToolContext,
32
+ ToolDef,
33
+ agent_tool,
34
+ audio_tool,
35
+ http_tool,
36
+ image_tool,
37
+ mcp_tool,
38
+ pdf_tool,
39
+ tool,
40
+ video_tool,
41
+ )
42
+
43
+ # MCP discovery utilities
44
+ from agentspan.agents.runtime.mcp_discovery import clear_discovery_cache
45
+
46
+ # Execution API
47
+ from agentspan.agents.run import (
48
+ plan,
49
+ run,
50
+ run_async,
51
+ shutdown,
52
+ start,
53
+ start_async,
54
+ stream,
55
+ stream_async,
56
+ )
57
+
58
+ # Runtime (for context manager and advanced usage)
59
+ from agentspan.agents.runtime.config import AgentConfig
60
+ from agentspan.agents.runtime.runtime import AgentRuntime
61
+
62
+ # Result types
63
+ from agentspan.agents.result import (
64
+ AgentEvent,
65
+ AgentHandle,
66
+ AgentResult,
67
+ AgentStatus,
68
+ AgentStream,
69
+ AsyncAgentStream,
70
+ EventType,
71
+ TokenUsage,
72
+ )
73
+
74
+ # Guardrails
75
+ from agentspan.agents.guardrail import (
76
+ Guardrail,
77
+ GuardrailDef,
78
+ GuardrailResult,
79
+ LLMGuardrail,
80
+ OnFail,
81
+ Position,
82
+ RegexGuardrail,
83
+ guardrail,
84
+ )
85
+
86
+ # Termination conditions
87
+ from agentspan.agents.termination import (
88
+ MaxMessageTermination,
89
+ StopMessageTermination,
90
+ TerminationCondition,
91
+ TerminationResult,
92
+ TextMentionTermination,
93
+ TokenUsageTermination,
94
+ )
95
+
96
+ # Memory
97
+ from agentspan.agents.memory import ConversationMemory
98
+ from agentspan.agents.semantic_memory import MemoryEntry, MemoryStore, SemanticMemory
99
+
100
+ # Code execution
101
+ from agentspan.agents.code_execution_config import CodeExecutionConfig
102
+ from agentspan.agents.code_executor import (
103
+ CodeExecutor,
104
+ DockerCodeExecutor,
105
+ ExecutionResult,
106
+ JupyterCodeExecutor,
107
+ LocalCodeExecutor,
108
+ ServerlessCodeExecutor,
109
+ )
110
+
111
+ # Handoff conditions (for swarm strategy)
112
+ from agentspan.agents.handoff import HandoffCondition, OnCondition, OnTextMention, OnToolResult
113
+
114
+ # Extended agent types
115
+ from agentspan.agents.ext import GPTAssistantAgent, UserProxyAgent
116
+
117
+ # Tracing (optional — only activates if opentelemetry is installed)
118
+ from agentspan.agents.tracing import is_tracing_enabled
119
+
120
+ __all__ = [
121
+ # Core
122
+ "Agent",
123
+ "AgentDef",
124
+ "PromptTemplate",
125
+ "Strategy",
126
+ "agent",
127
+ "AgentRuntime",
128
+ "AgentConfig",
129
+ # Extended agent types
130
+ "UserProxyAgent",
131
+ "GPTAssistantAgent",
132
+ # Tools
133
+ "tool",
134
+ "ToolDef",
135
+ "ToolContext",
136
+ "agent_tool",
137
+ "http_tool",
138
+ "mcp_tool",
139
+ "image_tool",
140
+ "audio_tool",
141
+ "video_tool",
142
+ "pdf_tool",
143
+ "clear_discovery_cache",
144
+ # Convenience execution (uses a singleton AgentRuntime)
145
+ "plan",
146
+ "run",
147
+ "run_async",
148
+ "shutdown",
149
+ "start",
150
+ "start_async",
151
+ "stream",
152
+ "stream_async",
153
+ # Results
154
+ "AgentResult",
155
+ "AgentHandle",
156
+ "AgentStatus",
157
+ "AgentStream",
158
+ "AsyncAgentStream",
159
+ "AgentEvent",
160
+ "EventType",
161
+ "TokenUsage",
162
+ # Guardrails
163
+ "guardrail",
164
+ "Guardrail",
165
+ "GuardrailDef",
166
+ "GuardrailResult",
167
+ "OnFail",
168
+ "Position",
169
+ "RegexGuardrail",
170
+ "LLMGuardrail",
171
+ # Termination conditions
172
+ "TerminationCondition",
173
+ "TerminationResult",
174
+ "TextMentionTermination",
175
+ "StopMessageTermination",
176
+ "MaxMessageTermination",
177
+ "TokenUsageTermination",
178
+ # Memory
179
+ "ConversationMemory",
180
+ "SemanticMemory",
181
+ "MemoryStore",
182
+ "MemoryEntry",
183
+ # Code execution
184
+ "CodeExecutionConfig",
185
+ "CodeExecutor",
186
+ "LocalCodeExecutor",
187
+ "DockerCodeExecutor",
188
+ "JupyterCodeExecutor",
189
+ "ServerlessCodeExecutor",
190
+ "ExecutionResult",
191
+ # Handoff conditions
192
+ "HandoffCondition",
193
+ "OnToolResult",
194
+ "OnTextMention",
195
+ "OnCondition",
196
+ # Tracing
197
+ "is_tracing_enabled",
198
+ ]
@@ -0,0 +1,4 @@
1
+ # Copyright (c) 2025 AgentSpan
2
+ # Licensed under the MIT License. See LICENSE file in the project root for details.
3
+
4
+ # Private utilities — not part of the public API.
@@ -0,0 +1,85 @@
1
+ # Copyright (c) 2025 AgentSpan
2
+ # Licensed under the MIT License. See LICENSE file in the project root for details.
3
+
4
+ """Parse ``"provider/model"`` strings into (provider, model) tuples.
5
+
6
+ Conductor's :class:`LlmChatComplete` requires separate ``llm_provider`` and
7
+ ``model`` arguments. This module handles the unified format used by the
8
+ agents SDK.
9
+ """
10
+
11
+ from __future__ import annotations
12
+
13
+ from dataclasses import dataclass
14
+ from typing import Optional
15
+
16
+ # Known Conductor LLM provider names.
17
+ KNOWN_PROVIDERS = frozenset({
18
+ "openai",
19
+ "azure_openai",
20
+ "anthropic",
21
+ "google_gemini",
22
+ "google_vertex_ai",
23
+ "aws_bedrock",
24
+ "cohere",
25
+ "mistral",
26
+ "groq",
27
+ "perplexity",
28
+ "hugging_face",
29
+ "deepseek",
30
+ })
31
+
32
+
33
+ @dataclass(frozen=True)
34
+ class ParsedModel:
35
+ """Result of parsing a ``"provider/model"`` string.
36
+
37
+ Attributes:
38
+ provider: The Conductor integration name (e.g. ``"openai"``).
39
+ model: The model identifier (e.g. ``"gpt-4o"``).
40
+ """
41
+
42
+ provider: str
43
+ model: str
44
+
45
+
46
+ def parse_model(model_string: str) -> ParsedModel:
47
+ """Parse a ``"provider/model"`` string.
48
+
49
+ Args:
50
+ model_string: A string in ``"provider/model"`` format, e.g.
51
+ ``"openai/gpt-4o"`` or ``"anthropic/claude-sonnet-4-20250514"``.
52
+
53
+ Returns:
54
+ A :class:`ParsedModel` with separate ``provider`` and ``model`` fields.
55
+
56
+ Raises:
57
+ ValueError: If the string is not in the expected format.
58
+
59
+ Examples::
60
+
61
+ >>> parse_model("openai/gpt-4o")
62
+ ParsedModel(provider='openai', model='gpt-4o')
63
+
64
+ >>> parse_model("anthropic/claude-sonnet-4-20250514")
65
+ ParsedModel(provider='anthropic', model='claude-sonnet-4-20250514')
66
+
67
+ >>> parse_model("azure_openai/gpt-4o")
68
+ ParsedModel(provider='azure_openai', model='gpt-4o')
69
+ """
70
+ if "/" not in model_string:
71
+ raise ValueError(
72
+ f"Invalid model format {model_string!r}. "
73
+ "Expected 'provider/model' (e.g. 'openai/gpt-4o')"
74
+ )
75
+
76
+ parts = model_string.split("/", 1)
77
+ provider = parts[0].strip()
78
+ model = parts[1].strip()
79
+
80
+ if not provider:
81
+ raise ValueError(f"Empty provider in model string {model_string!r}")
82
+ if not model:
83
+ raise ValueError(f"Empty model name in model string {model_string!r}")
84
+
85
+ return ParsedModel(provider=provider, model=model)
@@ -0,0 +1,60 @@
1
+ # Copyright (c) 2025 AgentSpan
2
+ # Licensed under the MIT License. See LICENSE file in the project root for details.
3
+
4
+ """Provider registry — metadata for auto-registering LLM integrations.
5
+
6
+ Maps known LLM provider names (as used in ``"provider/model"`` strings) to the
7
+ configuration required to create integrations on the Conductor server.
8
+ """
9
+
10
+ from __future__ import annotations
11
+
12
+ from dataclasses import dataclass
13
+ from typing import Dict, Optional
14
+
15
+
16
+ @dataclass(frozen=True)
17
+ class ProviderSpec:
18
+ """Metadata for an LLM provider integration.
19
+
20
+ Attributes:
21
+ name: SDK provider name (matches :data:`model_parser.KNOWN_PROVIDERS`).
22
+ integration_type: Conductor server ``type`` field for the integration.
23
+ display_name: Human-readable provider name.
24
+ api_key_env: Environment variable that holds the provider's API key.
25
+ """
26
+
27
+ name: str
28
+ integration_type: str
29
+ display_name: str
30
+ api_key_env: str
31
+
32
+
33
+ PROVIDER_REGISTRY: Dict[str, ProviderSpec] = {
34
+ "openai": ProviderSpec(
35
+ name="openai",
36
+ integration_type="openai",
37
+ display_name="OpenAI",
38
+ api_key_env="OPENAI_API_KEY",
39
+ ),
40
+ "anthropic": ProviderSpec(
41
+ name="anthropic",
42
+ integration_type="anthropic",
43
+ display_name="Anthropic",
44
+ api_key_env="ANTHROPIC_API_KEY",
45
+ ),
46
+ "google_gemini": ProviderSpec(
47
+ name="google_gemini",
48
+ integration_type="google_gemini",
49
+ display_name="Google Gemini",
50
+ api_key_env="GOOGLE_GEMINI_API_KEY",
51
+ ),
52
+ }
53
+
54
+
55
+ def get_provider_spec(provider_name: str) -> Optional[ProviderSpec]:
56
+ """Look up a provider spec by name.
57
+
58
+ Returns ``None`` if the provider is not in the registry.
59
+ """
60
+ return PROVIDER_REGISTRY.get(provider_name)
@@ -0,0 +1,141 @@
1
+ # Copyright (c) 2025 AgentSpan
2
+ # Licensed under the MIT License. See LICENSE file in the project root for details.
3
+
4
+ """JSON Schema generation from Python type hints, Pydantic models, and dataclasses.
5
+
6
+ Wraps ``conductor.client.automator.json_schema_generator`` and adds support
7
+ for Pydantic ``BaseModel`` classes.
8
+ """
9
+
10
+ from __future__ import annotations
11
+
12
+ import inspect
13
+ from typing import Any, Callable, Dict, Optional, get_type_hints
14
+
15
+
16
+ # ── Type-hint → JSON Schema mapping ────────────────────────────────────
17
+
18
+ _PYTHON_TYPE_TO_JSON = {
19
+ str: {"type": "string"},
20
+ int: {"type": "integer"},
21
+ float: {"type": "number"},
22
+ bool: {"type": "boolean"},
23
+ list: {"type": "array"},
24
+ dict: {"type": "object"},
25
+ type(None): {"type": "null"},
26
+ }
27
+
28
+
29
+ def _type_to_json_schema(annotation: Any) -> Dict[str, Any]:
30
+ """Convert a Python type annotation to a JSON Schema fragment."""
31
+ if annotation is inspect.Parameter.empty or annotation is Any:
32
+ return {}
33
+
34
+ # Direct mapping
35
+ if annotation in _PYTHON_TYPE_TO_JSON:
36
+ return dict(_PYTHON_TYPE_TO_JSON[annotation])
37
+
38
+ # Handle Optional[X] (Union[X, None])
39
+ origin = getattr(annotation, "__origin__", None)
40
+ args = getattr(annotation, "__args__", ())
41
+
42
+ if origin is type(None):
43
+ return {"type": "null"}
44
+
45
+ # Union types (including Optional)
46
+ import typing
47
+ if origin is getattr(typing, "Union", None):
48
+ non_none = [a for a in args if a is not type(None)]
49
+ if len(non_none) == 1:
50
+ return _type_to_json_schema(non_none[0])
51
+ return {}
52
+
53
+ # List[X]
54
+ if origin is list:
55
+ schema: Dict[str, Any] = {"type": "array"}
56
+ if args:
57
+ schema["items"] = _type_to_json_schema(args[0])
58
+ return schema
59
+
60
+ # Dict[str, X]
61
+ if origin is dict:
62
+ schema = {"type": "object"}
63
+ if len(args) >= 2:
64
+ schema["additionalProperties"] = _type_to_json_schema(args[1])
65
+ return schema
66
+
67
+ return {}
68
+
69
+
70
+ # ── Function → JSON Schema ─────────────────────────────────────────────
71
+
72
+ def schema_from_function(func: Callable[..., Any]) -> Dict[str, Any]:
73
+ """Generate input/output JSON Schemas from a Python function's signature.
74
+
75
+ Uses type hints and docstring to produce schemas compatible with
76
+ Conductor's ``ToolSpec.input_schema``.
77
+
78
+ Args:
79
+ func: The function to analyse.
80
+
81
+ Returns:
82
+ A dict with ``"input"`` and ``"output"`` keys, each containing a
83
+ JSON Schema dict.
84
+ """
85
+ sig = inspect.signature(func)
86
+ try:
87
+ hints = get_type_hints(func)
88
+ except Exception:
89
+ hints = {}
90
+
91
+ # Build input schema
92
+ properties: Dict[str, Any] = {}
93
+ required: list[str] = []
94
+
95
+ for name, param in sig.parameters.items():
96
+ if name in ("self", "cls", "context"):
97
+ continue
98
+
99
+ prop = _type_to_json_schema(hints.get(name, param.annotation))
100
+ if not prop:
101
+ prop = {}
102
+
103
+ # Use docstring for parameter descriptions (simple extraction)
104
+ properties[name] = prop
105
+
106
+ if param.default is inspect.Parameter.empty:
107
+ required.append(name)
108
+
109
+ input_schema: Dict[str, Any] = {
110
+ "type": "object",
111
+ "properties": properties,
112
+ }
113
+ if required:
114
+ input_schema["required"] = required
115
+
116
+ # Build output schema
117
+ return_type = hints.get("return", sig.return_annotation)
118
+ output_schema = _type_to_json_schema(return_type) if return_type is not inspect.Parameter.empty else {}
119
+
120
+ return {"input": input_schema, "output": output_schema}
121
+
122
+
123
+ def schema_from_pydantic(model_class: type) -> Dict[str, Any]:
124
+ """Generate a JSON Schema from a Pydantic ``BaseModel`` class.
125
+
126
+ Args:
127
+ model_class: A Pydantic ``BaseModel`` subclass.
128
+
129
+ Returns:
130
+ The JSON Schema dict produced by Pydantic's ``model_json_schema()``.
131
+
132
+ Raises:
133
+ TypeError: If *model_class* is not a Pydantic ``BaseModel``.
134
+ """
135
+ if hasattr(model_class, "model_json_schema"):
136
+ # Pydantic v2
137
+ return model_class.model_json_schema()
138
+ elif hasattr(model_class, "schema"):
139
+ # Pydantic v1
140
+ return model_class.schema()
141
+ raise TypeError(f"{model_class} is not a Pydantic BaseModel")