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.
Files changed (42) hide show
  1. splinter/__init__.py +236 -0
  2. splinter/client.py +351 -0
  3. splinter/cloud/__init__.py +25 -0
  4. splinter/cloud/client.py +334 -0
  5. splinter/cloud/commands.py +203 -0
  6. splinter/cloud/sync.py +273 -0
  7. splinter/control/__init__.py +68 -0
  8. splinter/control/circuit_breaker.py +346 -0
  9. splinter/control/decisions.py +311 -0
  10. splinter/control/limits.py +297 -0
  11. splinter/control/loop_detection.py +370 -0
  12. splinter/control/memory.py +440 -0
  13. splinter/control/rate_limit.py +231 -0
  14. splinter/control/retry.py +282 -0
  15. splinter/control/rules.py +329 -0
  16. splinter/control/tool_access.py +432 -0
  17. splinter/coordination/__init__.py +67 -0
  18. splinter/coordination/checkpoint.py +550 -0
  19. splinter/coordination/execution.py +688 -0
  20. splinter/coordination/ownership.py +358 -0
  21. splinter/coordination/schema.py +438 -0
  22. splinter/coordination/state.py +420 -0
  23. splinter/exceptions.py +261 -0
  24. splinter/gateway/__init__.py +34 -0
  25. splinter/gateway/gateway.py +583 -0
  26. splinter/gateway/providers/__init__.py +34 -0
  27. splinter/gateway/providers/anthropic.py +268 -0
  28. splinter/gateway/providers/base.py +241 -0
  29. splinter/gateway/providers/gemini.py +291 -0
  30. splinter/gateway/providers/grok.py +226 -0
  31. splinter/gateway/providers/mock.py +223 -0
  32. splinter/gateway/providers/openai.py +234 -0
  33. splinter/schemas.py +344 -0
  34. splinter/utils/__init__.py +5 -0
  35. splinter/utils/logging.py +97 -0
  36. splinter/workflow/__init__.py +18 -0
  37. splinter/workflow/agent.py +353 -0
  38. splinter/workflow/workflow.py +577 -0
  39. splinter_agent-0.1.0.dist-info/METADATA +807 -0
  40. splinter_agent-0.1.0.dist-info/RECORD +42 -0
  41. splinter_agent-0.1.0.dist-info/WHEEL +5 -0
  42. 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
+ ]