polos-sdk 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.
- polos/__init__.py +105 -0
- polos/agents/__init__.py +7 -0
- polos/agents/agent.py +746 -0
- polos/agents/conversation_history.py +121 -0
- polos/agents/stop_conditions.py +280 -0
- polos/agents/stream.py +635 -0
- polos/core/__init__.py +0 -0
- polos/core/context.py +143 -0
- polos/core/state.py +26 -0
- polos/core/step.py +1380 -0
- polos/core/workflow.py +1192 -0
- polos/features/__init__.py +0 -0
- polos/features/events.py +456 -0
- polos/features/schedules.py +110 -0
- polos/features/tracing.py +605 -0
- polos/features/wait.py +82 -0
- polos/llm/__init__.py +9 -0
- polos/llm/generate.py +152 -0
- polos/llm/providers/__init__.py +5 -0
- polos/llm/providers/anthropic.py +615 -0
- polos/llm/providers/azure.py +42 -0
- polos/llm/providers/base.py +196 -0
- polos/llm/providers/fireworks.py +41 -0
- polos/llm/providers/gemini.py +40 -0
- polos/llm/providers/groq.py +40 -0
- polos/llm/providers/openai.py +1021 -0
- polos/llm/providers/together.py +40 -0
- polos/llm/stream.py +183 -0
- polos/middleware/__init__.py +0 -0
- polos/middleware/guardrail.py +148 -0
- polos/middleware/guardrail_executor.py +253 -0
- polos/middleware/hook.py +164 -0
- polos/middleware/hook_executor.py +104 -0
- polos/runtime/__init__.py +0 -0
- polos/runtime/batch.py +87 -0
- polos/runtime/client.py +841 -0
- polos/runtime/queue.py +42 -0
- polos/runtime/worker.py +1365 -0
- polos/runtime/worker_server.py +249 -0
- polos/tools/__init__.py +0 -0
- polos/tools/tool.py +587 -0
- polos/types/__init__.py +23 -0
- polos/types/types.py +116 -0
- polos/utils/__init__.py +27 -0
- polos/utils/agent.py +27 -0
- polos/utils/client_context.py +41 -0
- polos/utils/config.py +12 -0
- polos/utils/output_schema.py +311 -0
- polos/utils/retry.py +47 -0
- polos/utils/serializer.py +167 -0
- polos/utils/tracing.py +27 -0
- polos/utils/worker_singleton.py +40 -0
- polos_sdk-0.1.0.dist-info/METADATA +650 -0
- polos_sdk-0.1.0.dist-info/RECORD +55 -0
- polos_sdk-0.1.0.dist-info/WHEEL +4 -0
|
@@ -0,0 +1,650 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: polos-sdk
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Polos SDK for Python - Durable Agent Execution
|
|
5
|
+
Project-URL: Homepage, https://github.com/polos-dev/polos
|
|
6
|
+
Project-URL: Repository, https://github.com/polos-dev/polos
|
|
7
|
+
Project-URL: Documentation, https://docs.polos.dev
|
|
8
|
+
Project-URL: Issues, https://github.com/polos-dev/polos/issues
|
|
9
|
+
Author: Polos Team
|
|
10
|
+
License: Apache-2.0
|
|
11
|
+
Keywords: agents,ai,async,durable-execution,llm,orchestration,workflow
|
|
12
|
+
Classifier: Development Status :: 4 - Beta
|
|
13
|
+
Classifier: Intended Audience :: Developers
|
|
14
|
+
Classifier: License :: OSI Approved :: Apache Software License
|
|
15
|
+
Classifier: Programming Language :: Python :: 3
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
19
|
+
Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
|
|
20
|
+
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
21
|
+
Requires-Python: >=3.10
|
|
22
|
+
Requires-Dist: anyio>=4.0.0
|
|
23
|
+
Requires-Dist: fastapi>=0.104.0
|
|
24
|
+
Requires-Dist: httpx>=0.24.0
|
|
25
|
+
Requires-Dist: opentelemetry-api>=1.20.0
|
|
26
|
+
Requires-Dist: opentelemetry-exporter-otlp-proto-grpc>=1.20.0
|
|
27
|
+
Requires-Dist: opentelemetry-sdk>=1.20.0
|
|
28
|
+
Requires-Dist: pydantic>=2.0.0
|
|
29
|
+
Requires-Dist: python-dotenv>=1.0.0
|
|
30
|
+
Requires-Dist: uvicorn>=0.24.0
|
|
31
|
+
Provides-Extra: anthropic
|
|
32
|
+
Requires-Dist: anthropic>=0.39.0; extra == 'anthropic'
|
|
33
|
+
Provides-Extra: dev
|
|
34
|
+
Requires-Dist: pre-commit>=3.0.0; extra == 'dev'
|
|
35
|
+
Requires-Dist: pytest-asyncio>=0.21.0; extra == 'dev'
|
|
36
|
+
Requires-Dist: pytest-cov>=4.0.0; extra == 'dev'
|
|
37
|
+
Requires-Dist: pytest-mock>=3.10.0; extra == 'dev'
|
|
38
|
+
Requires-Dist: pytest>=7.0.0; extra == 'dev'
|
|
39
|
+
Requires-Dist: ruff>=0.1.0; extra == 'dev'
|
|
40
|
+
Provides-Extra: fireworks
|
|
41
|
+
Requires-Dist: openai>=1.0.0; extra == 'fireworks'
|
|
42
|
+
Provides-Extra: gemini
|
|
43
|
+
Requires-Dist: openai>=1.0.0; extra == 'gemini'
|
|
44
|
+
Provides-Extra: groq
|
|
45
|
+
Requires-Dist: openai>=1.0.0; extra == 'groq'
|
|
46
|
+
Provides-Extra: openai
|
|
47
|
+
Requires-Dist: openai>=1.0.0; extra == 'openai'
|
|
48
|
+
Provides-Extra: together
|
|
49
|
+
Requires-Dist: openai>=1.0.0; extra == 'together'
|
|
50
|
+
Description-Content-Type: text/markdown
|
|
51
|
+
|
|
52
|
+
# Polos Python SDK
|
|
53
|
+
|
|
54
|
+
Durable execution engine for Python. Build reliable AI agents and workflows that can survive failures, handle long-running tasks, and coordinate complex processes.
|
|
55
|
+
|
|
56
|
+
## Features
|
|
57
|
+
|
|
58
|
+
- 🤖 **AI Agents** - Build LLM-powered agents with tool calling, streaming, and conversation history
|
|
59
|
+
- 🔄 **Durable Workflows** - Workflows survive failures and resume from checkpoints
|
|
60
|
+
- ⏰ **Long-Running** - Execute workflows that run for hours or days
|
|
61
|
+
- 🔗 **Workflow Orchestration** - Chain workflows together and build complex processes
|
|
62
|
+
- 🛠️ **Tools** - Define reusable tools that agents can call
|
|
63
|
+
- 🐍 **Native Python** - Async/await support, type hints, and Pythonic APIs
|
|
64
|
+
- 📊 **Observability** - Built-in tracing, events, and monitoring
|
|
65
|
+
- 🔒 **Isolated Execution** - Each workflow runs in a secure environment
|
|
66
|
+
|
|
67
|
+
## Installation
|
|
68
|
+
|
|
69
|
+
```bash
|
|
70
|
+
pip install polos-worker
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
Or with UV (recommended):
|
|
74
|
+
```bash
|
|
75
|
+
uv add polos-worker
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
### Optional Dependencies
|
|
79
|
+
|
|
80
|
+
Install provider-specific dependencies for LLM support:
|
|
81
|
+
|
|
82
|
+
```bash
|
|
83
|
+
# OpenAI
|
|
84
|
+
pip install polos-worker[openai]
|
|
85
|
+
|
|
86
|
+
# Anthropic
|
|
87
|
+
pip install polos-worker[anthropic]
|
|
88
|
+
|
|
89
|
+
# Google Gemini
|
|
90
|
+
pip install polos-worker[gemini]
|
|
91
|
+
|
|
92
|
+
# Groq
|
|
93
|
+
pip install polos-worker[groq]
|
|
94
|
+
|
|
95
|
+
# Fireworks
|
|
96
|
+
pip install polos-worker[fireworks]
|
|
97
|
+
|
|
98
|
+
# Together AI
|
|
99
|
+
pip install polos-worker[together]
|
|
100
|
+
|
|
101
|
+
# All providers
|
|
102
|
+
pip install polos-worker[openai,anthropic,gemini,groq,fireworks,together]
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
## Quick Start
|
|
106
|
+
|
|
107
|
+
### 1. Configure the SDK
|
|
108
|
+
|
|
109
|
+
```python
|
|
110
|
+
from polos import configure
|
|
111
|
+
|
|
112
|
+
configure(
|
|
113
|
+
api_url='http://localhost:8080',
|
|
114
|
+
api_key='your-api-key', # Optional if running in dev mode
|
|
115
|
+
project_id='your-project-id'
|
|
116
|
+
)
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
Or use environment variables:
|
|
120
|
+
```bash
|
|
121
|
+
export POLOS_API_URL=http://localhost:8080
|
|
122
|
+
export POLOS_API_KEY=your-api-key
|
|
123
|
+
export POLOS_PROJECT_ID=your-project-id
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
### 2. Define a Workflow
|
|
127
|
+
|
|
128
|
+
```python
|
|
129
|
+
from polos import workflow, WorkflowContext
|
|
130
|
+
|
|
131
|
+
@workflow(id="hello_world")
|
|
132
|
+
async def hello_world(ctx: WorkflowContext, payload: dict):
|
|
133
|
+
"""A simple workflow."""
|
|
134
|
+
name = payload.get('name', 'World')
|
|
135
|
+
return {'message': f'Hello, {name}!'}
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
### 3. Create an Agent
|
|
139
|
+
|
|
140
|
+
```python
|
|
141
|
+
from polos import Agent
|
|
142
|
+
|
|
143
|
+
weather_agent = Agent(
|
|
144
|
+
id="weather-agent",
|
|
145
|
+
provider="openai",
|
|
146
|
+
model="gpt-5-mini",
|
|
147
|
+
system_prompt="You are a helpful weather assistant",
|
|
148
|
+
tools=[get_weather] # Your custom tools
|
|
149
|
+
)
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
### 4. Run Your Code
|
|
153
|
+
|
|
154
|
+
Start a worker to execute workflows and agents:
|
|
155
|
+
|
|
156
|
+
```bash
|
|
157
|
+
# Using the Python worker
|
|
158
|
+
python -m polos.runtime.worker
|
|
159
|
+
|
|
160
|
+
# Or use the Node.js worker (supports Python workflows)
|
|
161
|
+
npm install -g polos-worker
|
|
162
|
+
polos-worker start
|
|
163
|
+
```
|
|
164
|
+
|
|
165
|
+
## Core Concepts
|
|
166
|
+
|
|
167
|
+
### Workflows
|
|
168
|
+
|
|
169
|
+
Workflows are durable functions that can survive failures and resume execution:
|
|
170
|
+
|
|
171
|
+
```python
|
|
172
|
+
from polos import workflow, WorkflowContext, WorkflowState
|
|
173
|
+
|
|
174
|
+
class MyState(WorkflowState):
|
|
175
|
+
counter: int = 0
|
|
176
|
+
messages: list[str] = []
|
|
177
|
+
|
|
178
|
+
@workflow(id="my_workflow", state_schema=MyState)
|
|
179
|
+
async def my_workflow(ctx: WorkflowContext, payload: dict):
|
|
180
|
+
# Access state
|
|
181
|
+
ctx.state.counter += 1
|
|
182
|
+
ctx.state.messages.append(payload.get('message', ''))
|
|
183
|
+
|
|
184
|
+
# Use step.run() for durable execution
|
|
185
|
+
result = await ctx.step.run("process_data", process_data, payload)
|
|
186
|
+
|
|
187
|
+
return {'result': result, 'counter': ctx.state.counter}
|
|
188
|
+
```
|
|
189
|
+
|
|
190
|
+
### Agents
|
|
191
|
+
|
|
192
|
+
Agents are LLM-powered workflows with tool calling capabilities:
|
|
193
|
+
|
|
194
|
+
```python
|
|
195
|
+
from polos import Agent, max_steps, executed_tool
|
|
196
|
+
|
|
197
|
+
# Define a tool
|
|
198
|
+
from polos import tool
|
|
199
|
+
|
|
200
|
+
@tool
|
|
201
|
+
def get_weather(ctx, location: str) -> str:
|
|
202
|
+
"""Get weather for a location."""
|
|
203
|
+
# Your weather API call here
|
|
204
|
+
return f"Weather in {location}: 72°F, sunny"
|
|
205
|
+
|
|
206
|
+
# Create an agent
|
|
207
|
+
weather_agent = Agent(
|
|
208
|
+
id="weather-agent",
|
|
209
|
+
provider="openai",
|
|
210
|
+
model="gpt-5-mini",
|
|
211
|
+
system_prompt="You are a helpful weather assistant",
|
|
212
|
+
tools=[get_weather],
|
|
213
|
+
stop_conditions=[max_steps(10), executed_tool("get_weather")]
|
|
214
|
+
)
|
|
215
|
+
|
|
216
|
+
# Use the agent
|
|
217
|
+
result = await weather_agent.run("What's the weather in NYC?")
|
|
218
|
+
print(result.result) # Agent's response
|
|
219
|
+
|
|
220
|
+
# Or stream the response
|
|
221
|
+
stream_result = await weather_agent.stream("What's the weather in NYC?")
|
|
222
|
+
async for event in stream_result.events:
|
|
223
|
+
if event.event_type == "text_delta":
|
|
224
|
+
print(event.data.get("content", ""), end="", flush=True)
|
|
225
|
+
```
|
|
226
|
+
|
|
227
|
+
### Tools
|
|
228
|
+
|
|
229
|
+
Tools are functions that agents can call:
|
|
230
|
+
|
|
231
|
+
```python
|
|
232
|
+
from polos import tool, WorkflowContext
|
|
233
|
+
|
|
234
|
+
@tool
|
|
235
|
+
def calculate(ctx: WorkflowContext, expression: str) -> float:
|
|
236
|
+
"""Evaluate a mathematical expression."""
|
|
237
|
+
return eval(expression) # In production, use a safe evaluator
|
|
238
|
+
|
|
239
|
+
@tool
|
|
240
|
+
def search_web(ctx: WorkflowContext, query: str) -> str:
|
|
241
|
+
"""Search the web for information."""
|
|
242
|
+
# Your search implementation
|
|
243
|
+
return f"Results for: {query}"
|
|
244
|
+
```
|
|
245
|
+
|
|
246
|
+
### Hooks
|
|
247
|
+
|
|
248
|
+
Hooks allow you to intercept workflow execution:
|
|
249
|
+
|
|
250
|
+
```python
|
|
251
|
+
from polos import hook, HookContext, HookResult, HookAction
|
|
252
|
+
|
|
253
|
+
@hook
|
|
254
|
+
def log_execution(ctx: WorkflowContext, hook_ctx: HookContext) -> HookResult:
|
|
255
|
+
"""Log workflow execution."""
|
|
256
|
+
print(f"Workflow {ctx.workflow_id} started")
|
|
257
|
+
return HookResult.continue_with()
|
|
258
|
+
|
|
259
|
+
@workflow(id="my_workflow", on_start=log_execution)
|
|
260
|
+
async def my_workflow(ctx: WorkflowContext, payload: dict):
|
|
261
|
+
return {'result': 'done'}
|
|
262
|
+
```
|
|
263
|
+
|
|
264
|
+
### Guardrails
|
|
265
|
+
|
|
266
|
+
Guardrails validate and modify agent outputs:
|
|
267
|
+
|
|
268
|
+
```python
|
|
269
|
+
from polos import guardrail, GuardrailContext, GuardrailResult
|
|
270
|
+
|
|
271
|
+
@guardrail
|
|
272
|
+
def check_profanity(ctx: GuardrailContext) -> GuardrailResult:
|
|
273
|
+
"""Check for profanity in agent output."""
|
|
274
|
+
content = ctx.content or ""
|
|
275
|
+
if any(word in content.lower() for word in ["bad", "word"]):
|
|
276
|
+
return GuardrailResult.fail("Content contains inappropriate language")
|
|
277
|
+
return GuardrailResult.continue_with()
|
|
278
|
+
|
|
279
|
+
weather_agent = Agent(
|
|
280
|
+
id="weather-agent",
|
|
281
|
+
provider="openai",
|
|
282
|
+
model="gpt-5-mini",
|
|
283
|
+
guardrails=[check_profanity]
|
|
284
|
+
)
|
|
285
|
+
```
|
|
286
|
+
|
|
287
|
+
## Advanced Features
|
|
288
|
+
|
|
289
|
+
### State Management
|
|
290
|
+
|
|
291
|
+
Workflows can maintain state across executions:
|
|
292
|
+
|
|
293
|
+
```python
|
|
294
|
+
from polos import workflow, WorkflowContext, WorkflowState
|
|
295
|
+
|
|
296
|
+
class CounterState(WorkflowState):
|
|
297
|
+
count: int = 0
|
|
298
|
+
history: list[str] = []
|
|
299
|
+
|
|
300
|
+
@workflow(id="counter", state_schema=CounterState)
|
|
301
|
+
async def counter(ctx: WorkflowContext, payload: dict):
|
|
302
|
+
ctx.state.count += 1
|
|
303
|
+
ctx.state.history.append(payload.get('action', ''))
|
|
304
|
+
return {'count': ctx.state.count}
|
|
305
|
+
```
|
|
306
|
+
|
|
307
|
+
### Step Execution
|
|
308
|
+
|
|
309
|
+
Use `ctx.step` for durable execution:
|
|
310
|
+
|
|
311
|
+
```python
|
|
312
|
+
@workflow(id="my_workflow")
|
|
313
|
+
async def my_workflow(ctx: WorkflowContext, payload: dict):
|
|
314
|
+
# Run a step (durable)
|
|
315
|
+
result = await ctx.step.run("step_name", my_function, arg1, arg2)
|
|
316
|
+
|
|
317
|
+
# Invoke another workflow and wait
|
|
318
|
+
result = await ctx.step.invoke_and_wait("other_workflow", other_workflow, payload)
|
|
319
|
+
|
|
320
|
+
# Invoke another workflow (fire and forget)
|
|
321
|
+
handle = await ctx.step.invoke("other_workflow", other_workflow, payload)
|
|
322
|
+
|
|
323
|
+
# Wait for a workflow to complete
|
|
324
|
+
result = await ctx.step.wait_for(handle)
|
|
325
|
+
|
|
326
|
+
return result
|
|
327
|
+
```
|
|
328
|
+
|
|
329
|
+
### Events
|
|
330
|
+
|
|
331
|
+
Publish and subscribe to events:
|
|
332
|
+
|
|
333
|
+
```python
|
|
334
|
+
from polos import events
|
|
335
|
+
|
|
336
|
+
# Publish an event
|
|
337
|
+
await events.publish(
|
|
338
|
+
topic="user/123",
|
|
339
|
+
event_type="message",
|
|
340
|
+
data={"text": "Hello"}
|
|
341
|
+
)
|
|
342
|
+
|
|
343
|
+
# Stream events
|
|
344
|
+
async for event in events.stream_topic("user/123"):
|
|
345
|
+
print(f"Event: {event.event_type} - {event.data}")
|
|
346
|
+
```
|
|
347
|
+
|
|
348
|
+
### Scheduled Workflows
|
|
349
|
+
|
|
350
|
+
Schedule workflows to run on a schedule:
|
|
351
|
+
|
|
352
|
+
```python
|
|
353
|
+
@workflow(
|
|
354
|
+
id="daily_report",
|
|
355
|
+
schedule="0 9 * * *" # 9 AM daily
|
|
356
|
+
)
|
|
357
|
+
async def daily_report(ctx: WorkflowContext, payload: dict):
|
|
358
|
+
# Generate daily report
|
|
359
|
+
return {'report': '...'}
|
|
360
|
+
```
|
|
361
|
+
|
|
362
|
+
### Queues
|
|
363
|
+
|
|
364
|
+
Use queues for concurrency control:
|
|
365
|
+
|
|
366
|
+
```python
|
|
367
|
+
from polos import Queue
|
|
368
|
+
|
|
369
|
+
my_queue = Queue(name="processing", concurrency_limit=5)
|
|
370
|
+
|
|
371
|
+
@workflow(id="processor", queue=my_queue)
|
|
372
|
+
async def processor(ctx: WorkflowContext, payload: dict):
|
|
373
|
+
# Process item
|
|
374
|
+
return {'status': 'processed'}
|
|
375
|
+
```
|
|
376
|
+
|
|
377
|
+
### Batch Execution
|
|
378
|
+
|
|
379
|
+
Execute workflows in batch:
|
|
380
|
+
|
|
381
|
+
```python
|
|
382
|
+
from polos import batch
|
|
383
|
+
|
|
384
|
+
results = await batch.run(
|
|
385
|
+
workflow_id="my_workflow",
|
|
386
|
+
inputs=[
|
|
387
|
+
{"value": 1},
|
|
388
|
+
{"value": 2},
|
|
389
|
+
{"value": 3}
|
|
390
|
+
]
|
|
391
|
+
)
|
|
392
|
+
|
|
393
|
+
for result in results:
|
|
394
|
+
print(result.result)
|
|
395
|
+
```
|
|
396
|
+
|
|
397
|
+
## Testing
|
|
398
|
+
|
|
399
|
+
The SDK includes comprehensive unit tests. Run tests with:
|
|
400
|
+
|
|
401
|
+
```bash
|
|
402
|
+
# Install test dependencies
|
|
403
|
+
uv sync --dev
|
|
404
|
+
|
|
405
|
+
# Run all tests
|
|
406
|
+
uv run pytest
|
|
407
|
+
|
|
408
|
+
# Run with coverage
|
|
409
|
+
uv run pytest --cov=polos --cov-report=html
|
|
410
|
+
|
|
411
|
+
# Run specific test file
|
|
412
|
+
uv run pytest tests/unit/test_core/test_workflow.py
|
|
413
|
+
```
|
|
414
|
+
|
|
415
|
+
See [`TESTING_PLAN.md`](./TESTING_PLAN.md) for testing guidelines.
|
|
416
|
+
|
|
417
|
+
## Development Setup
|
|
418
|
+
|
|
419
|
+
### Using UV (Recommended)
|
|
420
|
+
|
|
421
|
+
```bash
|
|
422
|
+
# Fork the repository on GitHub first: https://github.com/polos-dev/polos
|
|
423
|
+
# Then clone your fork
|
|
424
|
+
git clone https://github.com/YOUR_USERNAME/polos.git
|
|
425
|
+
cd polos/sdk/python
|
|
426
|
+
|
|
427
|
+
# Install dependencies (creates venv automatically)
|
|
428
|
+
uv sync
|
|
429
|
+
|
|
430
|
+
# Format code with Ruff
|
|
431
|
+
uv run ruff format .
|
|
432
|
+
|
|
433
|
+
# Lint code
|
|
434
|
+
uv run ruff check .
|
|
435
|
+
|
|
436
|
+
# Run tests
|
|
437
|
+
uv run pytest
|
|
438
|
+
|
|
439
|
+
# Build package
|
|
440
|
+
uv build
|
|
441
|
+
```
|
|
442
|
+
|
|
443
|
+
### Using pip
|
|
444
|
+
|
|
445
|
+
```bash
|
|
446
|
+
# Create virtual environment
|
|
447
|
+
python -m venv venv
|
|
448
|
+
source venv/bin/activate # Windows: venv\Scripts\activate
|
|
449
|
+
|
|
450
|
+
# Install in development mode
|
|
451
|
+
pip install -e ".[dev]"
|
|
452
|
+
|
|
453
|
+
# Format code
|
|
454
|
+
ruff format .
|
|
455
|
+
|
|
456
|
+
# Lint code
|
|
457
|
+
ruff check .
|
|
458
|
+
|
|
459
|
+
# Run tests
|
|
460
|
+
pytest
|
|
461
|
+
```
|
|
462
|
+
|
|
463
|
+
### Pre-commit Hooks
|
|
464
|
+
|
|
465
|
+
The project uses pre-commit hooks for code quality:
|
|
466
|
+
|
|
467
|
+
```bash
|
|
468
|
+
# Install pre-commit
|
|
469
|
+
pip install pre-commit
|
|
470
|
+
|
|
471
|
+
# Install hooks
|
|
472
|
+
pre-commit install
|
|
473
|
+
|
|
474
|
+
# Run hooks manually
|
|
475
|
+
pre-commit run --all-files
|
|
476
|
+
```
|
|
477
|
+
|
|
478
|
+
## Project Structure
|
|
479
|
+
|
|
480
|
+
```
|
|
481
|
+
sdk/python/
|
|
482
|
+
├── polos/ # Main package
|
|
483
|
+
│ ├── agents/ # Agent implementation
|
|
484
|
+
│ ├── core/ # Core workflow/step/context
|
|
485
|
+
│ ├── features/ # Events, schedules, tracing
|
|
486
|
+
│ ├── llm/ # LLM providers
|
|
487
|
+
│ ├── middleware/ # Hooks and guardrails
|
|
488
|
+
│ ├── runtime/ # Worker and client
|
|
489
|
+
│ ├── tools/ # Tool implementation
|
|
490
|
+
│ ├── types/ # Type definitions
|
|
491
|
+
│ └── utils/ # Utility functions
|
|
492
|
+
├── tests/ # Test suite
|
|
493
|
+
│ └── unit/ # Unit tests
|
|
494
|
+
├── pyproject.toml # Project configuration
|
|
495
|
+
└── README.md # This file
|
|
496
|
+
```
|
|
497
|
+
|
|
498
|
+
## API Reference
|
|
499
|
+
|
|
500
|
+
### Core Functions
|
|
501
|
+
|
|
502
|
+
#### `@workflow(id, queue, schedule, state_schema, ...)`
|
|
503
|
+
|
|
504
|
+
Decorator to register a workflow function.
|
|
505
|
+
|
|
506
|
+
**Parameters:**
|
|
507
|
+
- `id` (str): Unique workflow identifier
|
|
508
|
+
- `queue` (str | Queue | dict, optional): Queue configuration
|
|
509
|
+
- `schedule` (str | dict, optional): Cron schedule or schedule config
|
|
510
|
+
- `state_schema` (type[WorkflowState], optional): State schema class
|
|
511
|
+
- `on_start` (Callable | list, optional): Hooks to run on start
|
|
512
|
+
- `on_end` (Callable | list, optional): Hooks to run on end
|
|
513
|
+
|
|
514
|
+
**Example:**
|
|
515
|
+
```python
|
|
516
|
+
@workflow(id="my_workflow", state_schema=MyState)
|
|
517
|
+
async def my_workflow(ctx: WorkflowContext, payload: dict):
|
|
518
|
+
return {'result': 'done'}
|
|
519
|
+
```
|
|
520
|
+
|
|
521
|
+
#### `Agent(id, provider, model, ...)`
|
|
522
|
+
|
|
523
|
+
Create an AI agent.
|
|
524
|
+
|
|
525
|
+
**Parameters:**
|
|
526
|
+
- `id` (str): Unique agent identifier
|
|
527
|
+
- `provider` (str): LLM provider ("openai", "anthropic", etc.)
|
|
528
|
+
- `model` (str): Model name (e.g., "gpt-4o", "claude-sonnet-4-5")
|
|
529
|
+
- `system_prompt` (str, optional): System prompt
|
|
530
|
+
- `tools` (list, optional): List of tools
|
|
531
|
+
- `stop_conditions` (list, optional): Stop condition callables
|
|
532
|
+
- `guardrails` (list, optional): Guardrail callables
|
|
533
|
+
|
|
534
|
+
**Example:**
|
|
535
|
+
```python
|
|
536
|
+
agent = Agent(
|
|
537
|
+
id="my-agent",
|
|
538
|
+
provider="openai",
|
|
539
|
+
model="gpt-5-mini",
|
|
540
|
+
tools=[my_tool]
|
|
541
|
+
)
|
|
542
|
+
```
|
|
543
|
+
|
|
544
|
+
#### `@tool`
|
|
545
|
+
|
|
546
|
+
Decorator to register a tool function.
|
|
547
|
+
|
|
548
|
+
**Example:**
|
|
549
|
+
```python
|
|
550
|
+
@tool
|
|
551
|
+
def my_tool(ctx: WorkflowContext, arg: str) -> str:
|
|
552
|
+
return f"Processed: {arg}"
|
|
553
|
+
```
|
|
554
|
+
|
|
555
|
+
### Context Objects
|
|
556
|
+
|
|
557
|
+
#### `WorkflowContext`
|
|
558
|
+
|
|
559
|
+
Context object passed to workflow functions.
|
|
560
|
+
|
|
561
|
+
**Properties:**
|
|
562
|
+
- `workflow_id` (str): Workflow identifier
|
|
563
|
+
- `execution_id` (str): Current execution ID
|
|
564
|
+
- `root_execution_id` (str): Root execution ID
|
|
565
|
+
- `state` (WorkflowState): Workflow state
|
|
566
|
+
- `step` (Step): Step helper for durable execution
|
|
567
|
+
|
|
568
|
+
#### `AgentContext`
|
|
569
|
+
|
|
570
|
+
Context object passed to agent functions (extends `WorkflowContext`).
|
|
571
|
+
|
|
572
|
+
**Additional Properties:**
|
|
573
|
+
- `agent_id` (str): Agent identifier
|
|
574
|
+
- `conversation_id` (str, optional): Conversation ID for history
|
|
575
|
+
|
|
576
|
+
## Environment Variables
|
|
577
|
+
|
|
578
|
+
- `POLOS_API_URL` - Orchestrator URL (default: `http://localhost:8080`)
|
|
579
|
+
- `POLOS_API_KEY` - API key for authentication
|
|
580
|
+
- `POLOS_PROJECT_ID` - Project ID for multi-project support
|
|
581
|
+
|
|
582
|
+
## Architecture
|
|
583
|
+
|
|
584
|
+
Polos uses a push-based worker architecture:
|
|
585
|
+
|
|
586
|
+
```
|
|
587
|
+
┌─────────────┐
|
|
588
|
+
│ Client │ ── trigger ──> ┌──────────────┐
|
|
589
|
+
└─────────────┘ │ Orchestrator │
|
|
590
|
+
│ (Rust API) │
|
|
591
|
+
└──────────────┘
|
|
592
|
+
│
|
|
593
|
+
invokes
|
|
594
|
+
↓
|
|
595
|
+
┌─────────────┐ ┌──────────────┐
|
|
596
|
+
│ Executor │ <── pushes ──│ Worker │
|
|
597
|
+
│ (Python) │ │ (Python) │
|
|
598
|
+
└─────────────┘ └──────────────┘
|
|
599
|
+
```
|
|
600
|
+
|
|
601
|
+
1. **Client** triggers workflow/agent via SDK
|
|
602
|
+
2. **Orchestrator** queues execution in database
|
|
603
|
+
3. **Orchestrator** pushes execution to worker via HTTP
|
|
604
|
+
4. **Worker** receives execution request and executes workflow/agent in process
|
|
605
|
+
5. **Worker** reports result back to orchestrator
|
|
606
|
+
6. **Orchestrator** marks execution complete
|
|
607
|
+
7. **Client** receives result
|
|
608
|
+
|
|
609
|
+
## Contributing
|
|
610
|
+
|
|
611
|
+
We welcome contributions! See [CONTRIBUTING.md](../../CONTRIBUTING.md) for guidelines.
|
|
612
|
+
|
|
613
|
+
### Development Workflow
|
|
614
|
+
|
|
615
|
+
1. **Fork the repository** on GitHub: https://github.com/polos-dev/polos
|
|
616
|
+
2. **Clone your fork**:
|
|
617
|
+
```bash
|
|
618
|
+
git clone https://github.com/YOUR_USERNAME/polos.git
|
|
619
|
+
cd polos/sdk/python
|
|
620
|
+
```
|
|
621
|
+
3. **Add upstream remote** (optional, for syncing with main repo):
|
|
622
|
+
```bash
|
|
623
|
+
git remote add upstream https://github.com/polos-dev/polos.git
|
|
624
|
+
```
|
|
625
|
+
4. Create a feature branch
|
|
626
|
+
5. Make your changes
|
|
627
|
+
6. Run tests: `uv run pytest`
|
|
628
|
+
7. Format code: `uv run ruff format .`
|
|
629
|
+
8. Lint code: `uv run ruff check .`
|
|
630
|
+
9. Submit a pull request to `polos-dev/polos`
|
|
631
|
+
|
|
632
|
+
## License
|
|
633
|
+
|
|
634
|
+
MIT License - see [LICENSE](../../LICENSE) for details.
|
|
635
|
+
|
|
636
|
+
## Support
|
|
637
|
+
|
|
638
|
+
- 📖 [Documentation](https://docs.polos.dev)
|
|
639
|
+
- 💬 [Discord Community](https://discord.gg/polos)
|
|
640
|
+
- 🐛 [Issue Tracker](https://github.com/polos-dev/polos/issues)
|
|
641
|
+
- 📧 [Email Support](mailto:support@polos.dev)
|
|
642
|
+
|
|
643
|
+
## Related Projects
|
|
644
|
+
|
|
645
|
+
- [Polos Orchestrator](../../orchestrator) - Rust-based orchestrator
|
|
646
|
+
- [Polos UI](../../ui) - Web interface for monitoring and management
|
|
647
|
+
|
|
648
|
+
---
|
|
649
|
+
|
|
650
|
+
Built with ❤️ by the Polos team
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
polos/__init__.py,sha256=WTVHY01DfbioRF7A130k7SLizsC9UJZoySmz9lJ_VnA,2402
|
|
2
|
+
polos/agents/__init__.py,sha256=z6Pyyzbd8fKf2oA49hQ_1srj0nyCrS8XsuWAMYExz3c,122
|
|
3
|
+
polos/agents/agent.py,sha256=Y1JZvhqrEoirwbMvP948G-95iWB4kJAv9qBswmXGxP8,26426
|
|
4
|
+
polos/agents/conversation_history.py,sha256=W4efQU4p7yyMAkAw8jFkA9YEkQn-7giZKr0lkyBUJ-w,3918
|
|
5
|
+
polos/agents/stop_conditions.py,sha256=2qV27F4J8SfOHDZ3iGqUUELuBLmLIK08Fj7WOapo8cQ,9456
|
|
6
|
+
polos/agents/stream.py,sha256=d6L2e7RmmN275tuAcugFiqRq7snUndgXoPGSoCBMR6g,25127
|
|
7
|
+
polos/core/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
8
|
+
polos/core/context.py,sha256=vkwM4cQ93p1-K_muT6obxYV_dzgsPDOC3HT-OKNF6jE,5053
|
|
9
|
+
polos/core/state.py,sha256=MKWaGM1kLQEw0Q2VWD-PXB3CkK1tiqoTmeoQRwIpngg,774
|
|
10
|
+
polos/core/step.py,sha256=6uZcbbyk_zo7sR76PVw_9hXdgfpRyf5veDsoQwJcKrk,51700
|
|
11
|
+
polos/core/workflow.py,sha256=xGGNye2bb5B7W2RWsVFmYb5fz8349sGeAWZXjJgUuD0,51093
|
|
12
|
+
polos/features/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
13
|
+
polos/features/events.py,sha256=7viS9ryCeEnOaAO4blsLH45ulBPknDa9ISfif0jmhig,14237
|
|
14
|
+
polos/features/schedules.py,sha256=SpnQLOHKShZJA-Hteoqto5eTiq3Sin4-y8-K2RTQouw,3077
|
|
15
|
+
polos/features/tracing.py,sha256=xOaj9WLqK7B9IwRUDyCDVZ7IGhVPDxQ1YB1UQ3aFK2w,22736
|
|
16
|
+
polos/features/wait.py,sha256=c2NUz1BPG56EGIdxQwuDhydnyztqD5B9KBTlaimdofE,2504
|
|
17
|
+
polos/llm/__init__.py,sha256=SYLl5KdW3ah2lF5kpNsAuT-86MrNtsUDKXmznNsW8Fk,170
|
|
18
|
+
polos/llm/generate.py,sha256=FuuY2vPeka7q8cpQ4pHzygmkO_pnvAadLP65xyFgESI,6491
|
|
19
|
+
polos/llm/stream.py,sha256=oBPDL2t5srTMYKeKTm7dhVmTjWRgV-ynPhPKGM6ud98,6732
|
|
20
|
+
polos/llm/providers/__init__.py,sha256=xFiGlL4P1_3IxVXYFIphXWLBEnillFcRVmpa34YEysk,192
|
|
21
|
+
polos/llm/providers/anthropic.py,sha256=1aAbDIG8pYhTD2aOFBKK87Qh_LZDmZU5nGqqDh29opQ,26488
|
|
22
|
+
polos/llm/providers/azure.py,sha256=-_IxZhu6tCgoQ9TdmltUrimKVC6kw4DsU6eDIsitzOQ,1487
|
|
23
|
+
polos/llm/providers/base.py,sha256=w7HkErRbP0eKJPh-vFu4KZXhYnH_SBbeLk1jt55tpT4,6922
|
|
24
|
+
polos/llm/providers/fireworks.py,sha256=qg5UeRVy9mekJEd_OFU4VXvQpTMzpKMsZYCs1fS2ZEQ,1414
|
|
25
|
+
polos/llm/providers/gemini.py,sha256=yq4mWN-RG6LjZfKeP-P2JeBxHip754m0yLBoDX8bPlg,1342
|
|
26
|
+
polos/llm/providers/groq.py,sha256=oPIyy3eGTtyEWZOfuOxTZYMbu-Gz8OZ9t3RYDmENiE8,1285
|
|
27
|
+
polos/llm/providers/openai.py,sha256=MCPUiz_V25Suivdb-GAflop_oOPznnGbLjwZHOD4vC0,42487
|
|
28
|
+
polos/llm/providers/together.py,sha256=VAMV-y_nTOJCSNvVXvFg2WGxRdhVpHTYswX9wLjsROg,1344
|
|
29
|
+
polos/middleware/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
30
|
+
polos/middleware/guardrail.py,sha256=EKkKEh4qEIsaDDRztzYEYG128NOyM8T-KstSZxlPNT0,4840
|
|
31
|
+
polos/middleware/guardrail_executor.py,sha256=hvXEC5J2mfBGEaXIXwmCtfopi0boawKJkTtZsOMz2IM,9411
|
|
32
|
+
polos/middleware/hook.py,sha256=qhzjQP5I6DkL4GbdIrauDTesUQh067whyt1PxbP8qH8,4780
|
|
33
|
+
polos/middleware/hook_executor.py,sha256=732Kw89deLjDZPJyKQ0ZDlmOinp7dksBZenBdPSkpfg,3669
|
|
34
|
+
polos/runtime/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
35
|
+
polos/runtime/batch.py,sha256=hrzHi2oP5jJ6SJemESnkWI22v3m_GIMgh38fOscmM8k,2879
|
|
36
|
+
polos/runtime/client.py,sha256=iiJOU4zeNHHv61uzWNO7TablP4IQFPWcROMXRoSaQd0,32984
|
|
37
|
+
polos/runtime/queue.py,sha256=QeLgCVnMemchj7FdT_K5zsy-86iNTOqf2Wm3s1Cg-lA,1257
|
|
38
|
+
polos/runtime/worker.py,sha256=5KF0IgknIFNDhnZiFx2m7MLUXysgmt_np-1dI5euFf0,56398
|
|
39
|
+
polos/runtime/worker_server.py,sha256=HtWCyufL184R-aAIU_p0vVhwlWH4PDseQjZ2qPKpEns,10120
|
|
40
|
+
polos/tools/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
41
|
+
polos/tools/tool.py,sha256=OmaNBCQgA8e5uRJTO88pNR3eP1BDQiK2jvxkrPYdrag,22483
|
|
42
|
+
polos/types/__init__.py,sha256=Va3Yg1JbuDIzXFt5czG2hrN7DZL19k04DNBguUf-JlA,360
|
|
43
|
+
polos/types/types.py,sha256=wqYKLqm8nZ2Ba5BEDJpMgS88XzzeGV9zzX_ntGP8lpw,2919
|
|
44
|
+
polos/utils/__init__.py,sha256=E9PPg23MCxh-82TlQeD4-VNo0gQo3rJDQxaw7EVbVfo,636
|
|
45
|
+
polos/utils/agent.py,sha256=DR_f67DcuJY1vJQSNgDFeHkFZH9auvef2Zcv2W7mrYY,675
|
|
46
|
+
polos/utils/client_context.py,sha256=drSXD_4zPwn83KaNbp3B7vQcDH9IUbni1hT7TSG2DGU,1157
|
|
47
|
+
polos/utils/config.py,sha256=VXfccQgtVTX5ieFnHosB8xqcoHecDT2hJ09rJMnKvWw,399
|
|
48
|
+
polos/utils/output_schema.py,sha256=wJSZviqPCcqWrDfUlBFHCKYmuOqNZTqw4hYhqGZFI10,14235
|
|
49
|
+
polos/utils/retry.py,sha256=Igz4degC0IUWnsJsRKVIrkNjYm57DG467EcLbmMOMuM,1398
|
|
50
|
+
polos/utils/serializer.py,sha256=5BvdBSXPKGwO0F3FubiXV8vuMyW3AC1-4Jwz3Bc7eik,5486
|
|
51
|
+
polos/utils/tracing.py,sha256=SkBktU12KyIQcl8q1wjyTX7QfGwyzdzEcBY8QTX_t6Y,1021
|
|
52
|
+
polos/utils/worker_singleton.py,sha256=7-MOx9VFlyC-8waB7OMg6xER5NB7Ld7ofMku9yoitDo,1081
|
|
53
|
+
polos_sdk-0.1.0.dist-info/METADATA,sha256=66gs2_-Sk0xinDRF7KD6pR8JfY8Q0I2zmOijV_aibFQ,16924
|
|
54
|
+
polos_sdk-0.1.0.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87
|
|
55
|
+
polos_sdk-0.1.0.dist-info/RECORD,,
|