splinter-agent 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.
- splinter/__init__.py +236 -0
- splinter/client.py +351 -0
- splinter/cloud/__init__.py +25 -0
- splinter/cloud/client.py +334 -0
- splinter/cloud/commands.py +203 -0
- splinter/cloud/sync.py +273 -0
- splinter/control/__init__.py +68 -0
- splinter/control/circuit_breaker.py +346 -0
- splinter/control/decisions.py +311 -0
- splinter/control/limits.py +297 -0
- splinter/control/loop_detection.py +370 -0
- splinter/control/memory.py +440 -0
- splinter/control/rate_limit.py +231 -0
- splinter/control/retry.py +282 -0
- splinter/control/rules.py +329 -0
- splinter/control/tool_access.py +432 -0
- splinter/coordination/__init__.py +67 -0
- splinter/coordination/checkpoint.py +550 -0
- splinter/coordination/execution.py +688 -0
- splinter/coordination/ownership.py +358 -0
- splinter/coordination/schema.py +438 -0
- splinter/coordination/state.py +420 -0
- splinter/exceptions.py +261 -0
- splinter/gateway/__init__.py +34 -0
- splinter/gateway/gateway.py +583 -0
- splinter/gateway/providers/__init__.py +34 -0
- splinter/gateway/providers/anthropic.py +268 -0
- splinter/gateway/providers/base.py +241 -0
- splinter/gateway/providers/gemini.py +291 -0
- splinter/gateway/providers/grok.py +226 -0
- splinter/gateway/providers/mock.py +223 -0
- splinter/gateway/providers/openai.py +234 -0
- splinter/schemas.py +344 -0
- splinter/utils/__init__.py +5 -0
- splinter/utils/logging.py +97 -0
- splinter/workflow/__init__.py +18 -0
- splinter/workflow/agent.py +353 -0
- splinter/workflow/workflow.py +577 -0
- splinter_agent-0.1.0.dist-info/METADATA +807 -0
- splinter_agent-0.1.0.dist-info/RECORD +42 -0
- splinter_agent-0.1.0.dist-info/WHEEL +5 -0
- splinter_agent-0.1.0.dist-info/top_level.txt +1 -0
splinter/__init__.py
ADDED
|
@@ -0,0 +1,236 @@
|
|
|
1
|
+
"""Splinter - Multi-agent control and coordination.
|
|
2
|
+
|
|
3
|
+
Simple usage:
|
|
4
|
+
from splinter import Splinter
|
|
5
|
+
|
|
6
|
+
s = Splinter(openai_key="sk-...")
|
|
7
|
+
result = await s.run("helper", "What is 2+2?")
|
|
8
|
+
|
|
9
|
+
Advanced usage:
|
|
10
|
+
from splinter import Gateway, Workflow, AgentConfig
|
|
11
|
+
"""
|
|
12
|
+
|
|
13
|
+
__version__ = "0.1.0"
|
|
14
|
+
|
|
15
|
+
# =============================================================================
|
|
16
|
+
# SIMPLE API (start here)
|
|
17
|
+
# =============================================================================
|
|
18
|
+
|
|
19
|
+
from .client import Splinter, run_sync
|
|
20
|
+
|
|
21
|
+
# =============================================================================
|
|
22
|
+
# ADVANCED API
|
|
23
|
+
# =============================================================================
|
|
24
|
+
|
|
25
|
+
# Types
|
|
26
|
+
from .schemas import (
|
|
27
|
+
AgentConfig,
|
|
28
|
+
AgentStatus,
|
|
29
|
+
Checkpoint,
|
|
30
|
+
EvictionPolicy,
|
|
31
|
+
ExecutionContext,
|
|
32
|
+
ExecutionLimits,
|
|
33
|
+
ExecutionMetrics,
|
|
34
|
+
FieldOwnership,
|
|
35
|
+
HandoffSchema,
|
|
36
|
+
LLMMessage,
|
|
37
|
+
LLMProvider,
|
|
38
|
+
LLMRequest,
|
|
39
|
+
LLMResponse,
|
|
40
|
+
LoopDetectionConfig,
|
|
41
|
+
MemoryConfig,
|
|
42
|
+
MemoryEntry,
|
|
43
|
+
StateSnapshot,
|
|
44
|
+
StateVersion,
|
|
45
|
+
StepSignature,
|
|
46
|
+
ToolCall,
|
|
47
|
+
ToolPermission,
|
|
48
|
+
WorkflowConfig,
|
|
49
|
+
WorkflowStatus,
|
|
50
|
+
WorkflowStep,
|
|
51
|
+
)
|
|
52
|
+
|
|
53
|
+
# Exceptions
|
|
54
|
+
from .exceptions import (
|
|
55
|
+
AgentNotFoundError,
|
|
56
|
+
BudgetExceededError,
|
|
57
|
+
CheckpointError,
|
|
58
|
+
CheckpointNotFoundError,
|
|
59
|
+
ExecutionLimitError,
|
|
60
|
+
GatewayError,
|
|
61
|
+
LoopDetectedError,
|
|
62
|
+
MemoryLimitError,
|
|
63
|
+
ProviderError,
|
|
64
|
+
ProviderNotFoundError,
|
|
65
|
+
SchemaValidationError,
|
|
66
|
+
SplinterError,
|
|
67
|
+
StateError,
|
|
68
|
+
StateOwnershipError,
|
|
69
|
+
StepLimitExceededError,
|
|
70
|
+
TimeLimitExceededError,
|
|
71
|
+
ToolAccessDeniedError,
|
|
72
|
+
WorkflowError,
|
|
73
|
+
WorkflowExecutionError,
|
|
74
|
+
)
|
|
75
|
+
|
|
76
|
+
# Control Layer
|
|
77
|
+
from .control import (
|
|
78
|
+
AgentMemory,
|
|
79
|
+
LimitsEnforcer,
|
|
80
|
+
LoopBreaker,
|
|
81
|
+
LoopDetector,
|
|
82
|
+
MemoryStore,
|
|
83
|
+
PerAgentLimits,
|
|
84
|
+
ToolAccessController,
|
|
85
|
+
ToolRegistry,
|
|
86
|
+
)
|
|
87
|
+
|
|
88
|
+
# Coordination Layer
|
|
89
|
+
from .coordination import (
|
|
90
|
+
CheckpointManager,
|
|
91
|
+
CheckpointStorage,
|
|
92
|
+
FileCheckpointStorage,
|
|
93
|
+
HandoffManager,
|
|
94
|
+
InMemoryCheckpointStorage,
|
|
95
|
+
ProtectedState,
|
|
96
|
+
ResumableWorkflow,
|
|
97
|
+
SchemaValidator,
|
|
98
|
+
SharedState,
|
|
99
|
+
StateOwnershipManager,
|
|
100
|
+
create_schema_from_example,
|
|
101
|
+
)
|
|
102
|
+
|
|
103
|
+
# Gateway Layer
|
|
104
|
+
from .gateway import (
|
|
105
|
+
AnthropicProvider,
|
|
106
|
+
BaseProvider,
|
|
107
|
+
CallRecord,
|
|
108
|
+
Gateway,
|
|
109
|
+
GeminiProvider,
|
|
110
|
+
MockProvider,
|
|
111
|
+
OpenAIProvider,
|
|
112
|
+
ProviderFactory,
|
|
113
|
+
SmartMockProvider,
|
|
114
|
+
)
|
|
115
|
+
|
|
116
|
+
# Workflow Layer
|
|
117
|
+
from .workflow import (
|
|
118
|
+
Agent,
|
|
119
|
+
AgentBuilder,
|
|
120
|
+
Workflow,
|
|
121
|
+
WorkflowResult,
|
|
122
|
+
)
|
|
123
|
+
|
|
124
|
+
# Utils
|
|
125
|
+
from .utils import configure_logging, get_logger
|
|
126
|
+
|
|
127
|
+
# Cloud (Paid Features)
|
|
128
|
+
from .cloud import (
|
|
129
|
+
CloudClient,
|
|
130
|
+
CloudConfig,
|
|
131
|
+
Command,
|
|
132
|
+
CommandHandler,
|
|
133
|
+
CommandType,
|
|
134
|
+
StateSync,
|
|
135
|
+
SyncEvent,
|
|
136
|
+
)
|
|
137
|
+
|
|
138
|
+
__all__ = [
|
|
139
|
+
# Simple API
|
|
140
|
+
"Splinter",
|
|
141
|
+
"run_sync",
|
|
142
|
+
# Version
|
|
143
|
+
"__version__",
|
|
144
|
+
# Types
|
|
145
|
+
"AgentConfig",
|
|
146
|
+
"AgentStatus",
|
|
147
|
+
"Checkpoint",
|
|
148
|
+
"EvictionPolicy",
|
|
149
|
+
"ExecutionContext",
|
|
150
|
+
"ExecutionLimits",
|
|
151
|
+
"ExecutionMetrics",
|
|
152
|
+
"FieldOwnership",
|
|
153
|
+
"HandoffSchema",
|
|
154
|
+
"LLMMessage",
|
|
155
|
+
"LLMProvider",
|
|
156
|
+
"LLMRequest",
|
|
157
|
+
"LLMResponse",
|
|
158
|
+
"LoopDetectionConfig",
|
|
159
|
+
"MemoryConfig",
|
|
160
|
+
"MemoryEntry",
|
|
161
|
+
"StateSnapshot",
|
|
162
|
+
"StateVersion",
|
|
163
|
+
"StepSignature",
|
|
164
|
+
"ToolCall",
|
|
165
|
+
"ToolPermission",
|
|
166
|
+
"WorkflowConfig",
|
|
167
|
+
"WorkflowStatus",
|
|
168
|
+
"WorkflowStep",
|
|
169
|
+
# Exceptions
|
|
170
|
+
"AgentNotFoundError",
|
|
171
|
+
"BudgetExceededError",
|
|
172
|
+
"CheckpointError",
|
|
173
|
+
"CheckpointNotFoundError",
|
|
174
|
+
"ExecutionLimitError",
|
|
175
|
+
"GatewayError",
|
|
176
|
+
"LoopDetectedError",
|
|
177
|
+
"MemoryLimitError",
|
|
178
|
+
"ProviderError",
|
|
179
|
+
"ProviderNotFoundError",
|
|
180
|
+
"SchemaValidationError",
|
|
181
|
+
"SplinterError",
|
|
182
|
+
"StateError",
|
|
183
|
+
"StateOwnershipError",
|
|
184
|
+
"StepLimitExceededError",
|
|
185
|
+
"TimeLimitExceededError",
|
|
186
|
+
"ToolAccessDeniedError",
|
|
187
|
+
"WorkflowError",
|
|
188
|
+
"WorkflowExecutionError",
|
|
189
|
+
# Control
|
|
190
|
+
"AgentMemory",
|
|
191
|
+
"LimitsEnforcer",
|
|
192
|
+
"LoopBreaker",
|
|
193
|
+
"LoopDetector",
|
|
194
|
+
"MemoryStore",
|
|
195
|
+
"PerAgentLimits",
|
|
196
|
+
"ToolAccessController",
|
|
197
|
+
"ToolRegistry",
|
|
198
|
+
# Coordination
|
|
199
|
+
"CheckpointManager",
|
|
200
|
+
"CheckpointStorage",
|
|
201
|
+
"FileCheckpointStorage",
|
|
202
|
+
"HandoffManager",
|
|
203
|
+
"InMemoryCheckpointStorage",
|
|
204
|
+
"ProtectedState",
|
|
205
|
+
"ResumableWorkflow",
|
|
206
|
+
"SchemaValidator",
|
|
207
|
+
"SharedState",
|
|
208
|
+
"StateOwnershipManager",
|
|
209
|
+
"create_schema_from_example",
|
|
210
|
+
# Gateway
|
|
211
|
+
"AnthropicProvider",
|
|
212
|
+
"BaseProvider",
|
|
213
|
+
"CallRecord",
|
|
214
|
+
"Gateway",
|
|
215
|
+
"GeminiProvider",
|
|
216
|
+
"MockProvider",
|
|
217
|
+
"OpenAIProvider",
|
|
218
|
+
"ProviderFactory",
|
|
219
|
+
"SmartMockProvider",
|
|
220
|
+
# Workflow
|
|
221
|
+
"Agent",
|
|
222
|
+
"AgentBuilder",
|
|
223
|
+
"Workflow",
|
|
224
|
+
"WorkflowResult",
|
|
225
|
+
# Utils
|
|
226
|
+
"configure_logging",
|
|
227
|
+
"get_logger",
|
|
228
|
+
# Cloud (Paid Features)
|
|
229
|
+
"CloudClient",
|
|
230
|
+
"CloudConfig",
|
|
231
|
+
"Command",
|
|
232
|
+
"CommandHandler",
|
|
233
|
+
"CommandType",
|
|
234
|
+
"StateSync",
|
|
235
|
+
"SyncEvent",
|
|
236
|
+
]
|
splinter/client.py
ADDED
|
@@ -0,0 +1,351 @@
|
|
|
1
|
+
"""Splinter - Simple API.
|
|
2
|
+
|
|
3
|
+
Usage:
|
|
4
|
+
from splinter import Splinter
|
|
5
|
+
|
|
6
|
+
# Local (free)
|
|
7
|
+
s = Splinter(openai_key="sk-...")
|
|
8
|
+
result = await s.run("researcher", "Find info about AI")
|
|
9
|
+
|
|
10
|
+
# With cloud features (paid)
|
|
11
|
+
s = Splinter(openai_key="sk-...", api_key="sk-splinter-...")
|
|
12
|
+
# Now get live dashboard, remote control, etc.
|
|
13
|
+
"""
|
|
14
|
+
|
|
15
|
+
import asyncio
|
|
16
|
+
import os
|
|
17
|
+
from typing import Any, Optional
|
|
18
|
+
|
|
19
|
+
from .gateway import Gateway
|
|
20
|
+
from .workflow import Agent, AgentBuilder, Workflow, WorkflowResult
|
|
21
|
+
from .schemas import (
|
|
22
|
+
AgentConfig,
|
|
23
|
+
AgentStatus,
|
|
24
|
+
ExecutionLimits,
|
|
25
|
+
LLMProvider,
|
|
26
|
+
LoopDetectionConfig,
|
|
27
|
+
)
|
|
28
|
+
from .cloud import CloudClient
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
class Splinter:
|
|
32
|
+
"""Dead simple multi-agent control.
|
|
33
|
+
|
|
34
|
+
Usage:
|
|
35
|
+
from splinter import Splinter
|
|
36
|
+
|
|
37
|
+
# Setup (one line)
|
|
38
|
+
s = Splinter(openai_key="sk-...")
|
|
39
|
+
|
|
40
|
+
# Single agent (one line)
|
|
41
|
+
result = await s.run("helper", "What is 2+2?")
|
|
42
|
+
|
|
43
|
+
# Or with custom prompt
|
|
44
|
+
result = await s.run(
|
|
45
|
+
"analyst",
|
|
46
|
+
"Analyze this data",
|
|
47
|
+
instructions="You analyze data. Output JSON."
|
|
48
|
+
)
|
|
49
|
+
|
|
50
|
+
# Multi-agent pipeline
|
|
51
|
+
s.add_agent("researcher", "Research topics. Output JSON: {findings: []}")
|
|
52
|
+
s.add_agent("writer", "Write articles. Output JSON: {article: string}")
|
|
53
|
+
|
|
54
|
+
result = await s.pipeline(
|
|
55
|
+
["researcher", "writer"],
|
|
56
|
+
input={"topic": "AI trends"}
|
|
57
|
+
)
|
|
58
|
+
"""
|
|
59
|
+
|
|
60
|
+
def __init__(
|
|
61
|
+
self,
|
|
62
|
+
openai_key: str | None = None,
|
|
63
|
+
anthropic_key: str | None = None,
|
|
64
|
+
gemini_key: str | None = None,
|
|
65
|
+
grok_key: str | None = None,
|
|
66
|
+
max_budget: float = 10.0,
|
|
67
|
+
max_steps: int = 100,
|
|
68
|
+
model: str | None = None,
|
|
69
|
+
api_key: str | None = None,
|
|
70
|
+
):
|
|
71
|
+
"""Create Splinter instance.
|
|
72
|
+
|
|
73
|
+
Args:
|
|
74
|
+
openai_key: OpenAI API key (or set OPENAI_API_KEY env var)
|
|
75
|
+
anthropic_key: Anthropic API key (or set ANTHROPIC_API_KEY env var)
|
|
76
|
+
gemini_key: Gemini API key (or set GEMINI_API_KEY env var)
|
|
77
|
+
grok_key: Grok/xAI API key (or set XAI_API_KEY env var)
|
|
78
|
+
max_budget: Max spend in dollars (default $10)
|
|
79
|
+
max_steps: Max LLM calls (default 100)
|
|
80
|
+
model: Default model (auto-detected based on provider)
|
|
81
|
+
api_key: Splinter Cloud API key for paid features (optional)
|
|
82
|
+
"""
|
|
83
|
+
self._gateway = Gateway(
|
|
84
|
+
limits=ExecutionLimits(max_budget=max_budget, max_steps=max_steps)
|
|
85
|
+
)
|
|
86
|
+
self._default_provider: LLMProvider | None = None
|
|
87
|
+
self._cloud: Optional[CloudClient] = None
|
|
88
|
+
|
|
89
|
+
# Connect to Splinter Cloud if API key provided
|
|
90
|
+
api_key = api_key or os.environ.get("SPLINTER_API_KEY")
|
|
91
|
+
if api_key:
|
|
92
|
+
self._cloud = CloudClient(api_key=api_key)
|
|
93
|
+
self._cloud.register("gateway", self._gateway)
|
|
94
|
+
|
|
95
|
+
# Configure providers (in priority order)
|
|
96
|
+
openai_key = openai_key or os.environ.get("OPENAI_API_KEY")
|
|
97
|
+
if openai_key:
|
|
98
|
+
self._gateway.configure_provider("openai", api_key=openai_key)
|
|
99
|
+
self._default_provider = LLMProvider.OPENAI
|
|
100
|
+
self._model = model or "gpt-4o-mini"
|
|
101
|
+
|
|
102
|
+
anthropic_key = anthropic_key or os.environ.get("ANTHROPIC_API_KEY")
|
|
103
|
+
if anthropic_key:
|
|
104
|
+
self._gateway.configure_provider("anthropic", api_key=anthropic_key)
|
|
105
|
+
if self._default_provider is None:
|
|
106
|
+
self._default_provider = LLMProvider.ANTHROPIC
|
|
107
|
+
self._model = model or "claude-sonnet-4-20250514"
|
|
108
|
+
|
|
109
|
+
gemini_key = gemini_key or os.environ.get("GEMINI_API_KEY")
|
|
110
|
+
if gemini_key:
|
|
111
|
+
self._gateway.configure_provider("gemini", api_key=gemini_key)
|
|
112
|
+
if self._default_provider is None:
|
|
113
|
+
self._default_provider = LLMProvider.GEMINI
|
|
114
|
+
self._model = model or "gemini-1.5-flash"
|
|
115
|
+
|
|
116
|
+
grok_key = grok_key or os.environ.get("XAI_API_KEY")
|
|
117
|
+
if grok_key:
|
|
118
|
+
self._gateway.configure_provider("grok", api_key=grok_key)
|
|
119
|
+
if self._default_provider is None:
|
|
120
|
+
self._default_provider = LLMProvider.GROK
|
|
121
|
+
self._model = model or "grok-3-mini-fast"
|
|
122
|
+
|
|
123
|
+
# Fallback if no provider configured
|
|
124
|
+
if self._default_provider is None:
|
|
125
|
+
self._default_provider = LLMProvider.OPENAI
|
|
126
|
+
self._model = model or "gpt-4o-mini"
|
|
127
|
+
|
|
128
|
+
self._agents: dict[str, AgentConfig] = {}
|
|
129
|
+
|
|
130
|
+
# =========================================================================
|
|
131
|
+
# SIMPLE API
|
|
132
|
+
# =========================================================================
|
|
133
|
+
|
|
134
|
+
async def run(
|
|
135
|
+
self,
|
|
136
|
+
agent_name: str,
|
|
137
|
+
task: str,
|
|
138
|
+
instructions: str | None = None,
|
|
139
|
+
model: str | None = None,
|
|
140
|
+
) -> dict[str, Any]:
|
|
141
|
+
"""Run a single agent.
|
|
142
|
+
|
|
143
|
+
Args:
|
|
144
|
+
agent_name: Name for the agent
|
|
145
|
+
task: What to do
|
|
146
|
+
instructions: System prompt (optional)
|
|
147
|
+
model: Model to use (optional)
|
|
148
|
+
|
|
149
|
+
Returns:
|
|
150
|
+
Agent output as dict
|
|
151
|
+
|
|
152
|
+
Example:
|
|
153
|
+
result = await s.run("helper", "What is 2+2?")
|
|
154
|
+
result = await s.run(
|
|
155
|
+
"analyst",
|
|
156
|
+
"Analyze sales",
|
|
157
|
+
instructions="You analyze data. Output JSON."
|
|
158
|
+
)
|
|
159
|
+
"""
|
|
160
|
+
agent = (
|
|
161
|
+
AgentBuilder(agent_name)
|
|
162
|
+
.with_provider(self._default_provider, model or self._model)
|
|
163
|
+
.with_system_prompt(instructions or f"You are a helpful {agent_name}. Output JSON when possible.")
|
|
164
|
+
.build(self._gateway)
|
|
165
|
+
)
|
|
166
|
+
|
|
167
|
+
return await agent.run(task=task)
|
|
168
|
+
|
|
169
|
+
def add_agent(
|
|
170
|
+
self,
|
|
171
|
+
name: str,
|
|
172
|
+
instructions: str,
|
|
173
|
+
model: str | None = None,
|
|
174
|
+
tools: list[str] | None = None,
|
|
175
|
+
owns: list[str] | None = None,
|
|
176
|
+
) -> "Splinter":
|
|
177
|
+
"""Add an agent for pipelines.
|
|
178
|
+
|
|
179
|
+
Args:
|
|
180
|
+
name: Agent name (used as ID)
|
|
181
|
+
instructions: System prompt
|
|
182
|
+
model: Model to use (optional)
|
|
183
|
+
tools: Tools the agent can use (optional)
|
|
184
|
+
owns: State fields this agent owns (optional)
|
|
185
|
+
|
|
186
|
+
Returns:
|
|
187
|
+
Self for chaining
|
|
188
|
+
|
|
189
|
+
Example:
|
|
190
|
+
s.add_agent("researcher", "Research topics. Output JSON.")
|
|
191
|
+
s.add_agent("writer", "Write articles.", owns=["content.*"])
|
|
192
|
+
"""
|
|
193
|
+
self._agents[name] = AgentConfig(
|
|
194
|
+
agent_id=name,
|
|
195
|
+
provider=self._default_provider,
|
|
196
|
+
model=model or self._model,
|
|
197
|
+
system_prompt=instructions,
|
|
198
|
+
tools=tools or [],
|
|
199
|
+
state_ownership=owns or [],
|
|
200
|
+
)
|
|
201
|
+
return self
|
|
202
|
+
|
|
203
|
+
async def pipeline(
|
|
204
|
+
self,
|
|
205
|
+
agents: list[str],
|
|
206
|
+
input: dict[str, Any] | None = None,
|
|
207
|
+
) -> WorkflowResult:
|
|
208
|
+
"""Run a multi-agent pipeline.
|
|
209
|
+
|
|
210
|
+
Agents run in order. Each waits for the previous.
|
|
211
|
+
|
|
212
|
+
Args:
|
|
213
|
+
agents: List of agent names (in order)
|
|
214
|
+
input: Initial state/input
|
|
215
|
+
|
|
216
|
+
Returns:
|
|
217
|
+
WorkflowResult with outputs from all agents
|
|
218
|
+
|
|
219
|
+
Example:
|
|
220
|
+
s.add_agent("researcher", "Research. Output JSON: {findings: []}")
|
|
221
|
+
s.add_agent("writer", "Write. Output JSON: {article: string}")
|
|
222
|
+
|
|
223
|
+
result = await s.pipeline(
|
|
224
|
+
["researcher", "writer"],
|
|
225
|
+
input={"topic": "AI"}
|
|
226
|
+
)
|
|
227
|
+
"""
|
|
228
|
+
workflow = Workflow(workflow_id="pipeline")
|
|
229
|
+
workflow._gateway = self._gateway
|
|
230
|
+
|
|
231
|
+
# Add agents
|
|
232
|
+
for name in agents:
|
|
233
|
+
if name not in self._agents:
|
|
234
|
+
raise ValueError(f"Agent '{name}' not found. Call add_agent() first.")
|
|
235
|
+
workflow.add_agent(self._agents[name])
|
|
236
|
+
|
|
237
|
+
# Add steps with dependencies
|
|
238
|
+
prev = None
|
|
239
|
+
for name in agents:
|
|
240
|
+
if prev:
|
|
241
|
+
workflow.add_step(name, depends_on=[prev])
|
|
242
|
+
else:
|
|
243
|
+
workflow.add_step(name)
|
|
244
|
+
prev = name
|
|
245
|
+
|
|
246
|
+
return await workflow.run(initial_state=input or {})
|
|
247
|
+
|
|
248
|
+
# =========================================================================
|
|
249
|
+
# ADVANCED API
|
|
250
|
+
# =========================================================================
|
|
251
|
+
|
|
252
|
+
def agent(
|
|
253
|
+
self,
|
|
254
|
+
name: str,
|
|
255
|
+
instructions: str | None = None,
|
|
256
|
+
model: str | None = None,
|
|
257
|
+
) -> Agent:
|
|
258
|
+
"""Create a reusable agent.
|
|
259
|
+
|
|
260
|
+
Args:
|
|
261
|
+
name: Agent name
|
|
262
|
+
instructions: System prompt
|
|
263
|
+
model: Model to use
|
|
264
|
+
|
|
265
|
+
Returns:
|
|
266
|
+
Agent instance
|
|
267
|
+
|
|
268
|
+
Example:
|
|
269
|
+
researcher = s.agent("researcher", "Research topics.")
|
|
270
|
+
result = await researcher.run(task="Find info about AI")
|
|
271
|
+
"""
|
|
272
|
+
return (
|
|
273
|
+
AgentBuilder(name)
|
|
274
|
+
.with_provider(self._default_provider, model or self._model)
|
|
275
|
+
.with_system_prompt(instructions or f"You are a helpful {name}.")
|
|
276
|
+
.build(self._gateway)
|
|
277
|
+
)
|
|
278
|
+
|
|
279
|
+
@property
|
|
280
|
+
def gateway(self) -> Gateway:
|
|
281
|
+
"""Access the underlying gateway."""
|
|
282
|
+
return self._gateway
|
|
283
|
+
|
|
284
|
+
@property
|
|
285
|
+
def cost(self) -> float:
|
|
286
|
+
"""Total cost so far."""
|
|
287
|
+
return self._gateway.get_metrics()["total_cost"]
|
|
288
|
+
|
|
289
|
+
@property
|
|
290
|
+
def steps(self) -> int:
|
|
291
|
+
"""Total steps so far."""
|
|
292
|
+
return self._gateway.get_metrics()["total_steps"]
|
|
293
|
+
|
|
294
|
+
def reset(self) -> None:
|
|
295
|
+
"""Reset metrics and state."""
|
|
296
|
+
self._gateway.reset()
|
|
297
|
+
|
|
298
|
+
# =========================================================================
|
|
299
|
+
# CLOUD (PAID FEATURES)
|
|
300
|
+
# =========================================================================
|
|
301
|
+
|
|
302
|
+
@property
|
|
303
|
+
def cloud(self) -> Optional[CloudClient]:
|
|
304
|
+
"""Access cloud client (None if not connected)."""
|
|
305
|
+
return self._cloud
|
|
306
|
+
|
|
307
|
+
@property
|
|
308
|
+
def is_cloud_connected(self) -> bool:
|
|
309
|
+
"""Check if connected to Splinter Cloud."""
|
|
310
|
+
return self._cloud is not None and self._cloud.is_connected
|
|
311
|
+
|
|
312
|
+
async def connect_cloud(self, api_key: str | None = None) -> None:
|
|
313
|
+
"""Connect to Splinter Cloud for paid features.
|
|
314
|
+
|
|
315
|
+
Args:
|
|
316
|
+
api_key: Splinter Cloud API key
|
|
317
|
+
|
|
318
|
+
Paid features enabled:
|
|
319
|
+
- Live agent dashboard
|
|
320
|
+
- Pause/resume/stop agents remotely
|
|
321
|
+
- Live rule and limit changes
|
|
322
|
+
- Rollback to checkpoint
|
|
323
|
+
- Deadlock detection
|
|
324
|
+
- Bottleneck analysis
|
|
325
|
+
"""
|
|
326
|
+
api_key = api_key or os.environ.get("SPLINTER_API_KEY")
|
|
327
|
+
if not api_key:
|
|
328
|
+
raise ValueError("API key required. Pass api_key or set SPLINTER_API_KEY env var.")
|
|
329
|
+
|
|
330
|
+
self._cloud = CloudClient(api_key=api_key)
|
|
331
|
+
self._cloud.register("gateway", self._gateway)
|
|
332
|
+
await self._cloud.connect_async()
|
|
333
|
+
|
|
334
|
+
async def disconnect_cloud(self) -> None:
|
|
335
|
+
"""Disconnect from Splinter Cloud."""
|
|
336
|
+
if self._cloud:
|
|
337
|
+
await self._cloud.disconnect()
|
|
338
|
+
self._cloud = None
|
|
339
|
+
|
|
340
|
+
|
|
341
|
+
# Convenience function for sync usage
|
|
342
|
+
def run_sync(coro):
|
|
343
|
+
"""Run async code synchronously.
|
|
344
|
+
|
|
345
|
+
Example:
|
|
346
|
+
from splinter import Splinter, run_sync
|
|
347
|
+
|
|
348
|
+
s = Splinter(openai_key="sk-...")
|
|
349
|
+
result = run_sync(s.run("helper", "What is 2+2?"))
|
|
350
|
+
"""
|
|
351
|
+
return asyncio.run(coro)
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Splinter Cloud - Paid features for control and coordination.
|
|
3
|
+
|
|
4
|
+
Connect with an API key to unlock:
|
|
5
|
+
- Live agent dashboard
|
|
6
|
+
- Remote pause/resume/stop
|
|
7
|
+
- Live rule changes
|
|
8
|
+
- Rollback & recovery
|
|
9
|
+
- Deadlock detection
|
|
10
|
+
- Bottleneck analysis
|
|
11
|
+
"""
|
|
12
|
+
|
|
13
|
+
from .client import CloudClient, CloudConfig
|
|
14
|
+
from .sync import StateSync, SyncEvent
|
|
15
|
+
from .commands import CommandHandler, Command, CommandType
|
|
16
|
+
|
|
17
|
+
__all__ = [
|
|
18
|
+
"CloudClient",
|
|
19
|
+
"CloudConfig",
|
|
20
|
+
"StateSync",
|
|
21
|
+
"SyncEvent",
|
|
22
|
+
"CommandHandler",
|
|
23
|
+
"Command",
|
|
24
|
+
"CommandType",
|
|
25
|
+
]
|