polos-sdk 0.1.0__tar.gz → 0.1.1__tar.gz
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_sdk-0.1.0 → polos_sdk-0.1.1}/BUILD.md +17 -0
- polos_sdk-0.1.1/PKG-INFO +121 -0
- polos_sdk-0.1.1/README.md +70 -0
- {polos_sdk-0.1.0 → polos_sdk-0.1.1}/polos/agents/agent.py +3 -0
- {polos_sdk-0.1.0 → polos_sdk-0.1.1}/polos/agents/stream.py +19 -3
- {polos_sdk-0.1.0 → polos_sdk-0.1.1}/polos/core/step.py +7 -0
- {polos_sdk-0.1.0 → polos_sdk-0.1.1}/polos/core/workflow.py +6 -0
- {polos_sdk-0.1.0 → polos_sdk-0.1.1}/polos/features/events.py +5 -10
- {polos_sdk-0.1.0 → polos_sdk-0.1.1}/polos/runtime/client.py +8 -4
- {polos_sdk-0.1.0 → polos_sdk-0.1.1}/polos/runtime/worker.py +5 -3
- {polos_sdk-0.1.0 → polos_sdk-0.1.1}/polos/tools/tool.py +1 -0
- {polos_sdk-0.1.0 → polos_sdk-0.1.1}/polos/utils/serializer.py +21 -1
- polos_sdk-0.1.0/PKG-INFO +0 -650
- polos_sdk-0.1.0/README.md +0 -599
- {polos_sdk-0.1.0 → polos_sdk-0.1.1}/.gitignore +0 -0
- {polos_sdk-0.1.0 → polos_sdk-0.1.1}/polos/__init__.py +0 -0
- {polos_sdk-0.1.0 → polos_sdk-0.1.1}/polos/agents/__init__.py +0 -0
- {polos_sdk-0.1.0 → polos_sdk-0.1.1}/polos/agents/conversation_history.py +0 -0
- {polos_sdk-0.1.0 → polos_sdk-0.1.1}/polos/agents/stop_conditions.py +0 -0
- {polos_sdk-0.1.0 → polos_sdk-0.1.1}/polos/core/__init__.py +0 -0
- {polos_sdk-0.1.0 → polos_sdk-0.1.1}/polos/core/context.py +0 -0
- {polos_sdk-0.1.0 → polos_sdk-0.1.1}/polos/core/state.py +0 -0
- {polos_sdk-0.1.0 → polos_sdk-0.1.1}/polos/features/__init__.py +0 -0
- {polos_sdk-0.1.0 → polos_sdk-0.1.1}/polos/features/schedules.py +0 -0
- {polos_sdk-0.1.0 → polos_sdk-0.1.1}/polos/features/tracing.py +0 -0
- {polos_sdk-0.1.0 → polos_sdk-0.1.1}/polos/features/wait.py +0 -0
- {polos_sdk-0.1.0 → polos_sdk-0.1.1}/polos/llm/__init__.py +0 -0
- {polos_sdk-0.1.0 → polos_sdk-0.1.1}/polos/llm/generate.py +0 -0
- {polos_sdk-0.1.0 → polos_sdk-0.1.1}/polos/llm/providers/__init__.py +0 -0
- {polos_sdk-0.1.0 → polos_sdk-0.1.1}/polos/llm/providers/anthropic.py +0 -0
- {polos_sdk-0.1.0 → polos_sdk-0.1.1}/polos/llm/providers/azure.py +0 -0
- {polos_sdk-0.1.0 → polos_sdk-0.1.1}/polos/llm/providers/base.py +0 -0
- {polos_sdk-0.1.0 → polos_sdk-0.1.1}/polos/llm/providers/fireworks.py +0 -0
- {polos_sdk-0.1.0 → polos_sdk-0.1.1}/polos/llm/providers/gemini.py +0 -0
- {polos_sdk-0.1.0 → polos_sdk-0.1.1}/polos/llm/providers/groq.py +0 -0
- {polos_sdk-0.1.0 → polos_sdk-0.1.1}/polos/llm/providers/openai.py +0 -0
- {polos_sdk-0.1.0 → polos_sdk-0.1.1}/polos/llm/providers/together.py +0 -0
- {polos_sdk-0.1.0 → polos_sdk-0.1.1}/polos/llm/stream.py +0 -0
- {polos_sdk-0.1.0 → polos_sdk-0.1.1}/polos/middleware/__init__.py +0 -0
- {polos_sdk-0.1.0 → polos_sdk-0.1.1}/polos/middleware/guardrail.py +0 -0
- {polos_sdk-0.1.0 → polos_sdk-0.1.1}/polos/middleware/guardrail_executor.py +0 -0
- {polos_sdk-0.1.0 → polos_sdk-0.1.1}/polos/middleware/hook.py +0 -0
- {polos_sdk-0.1.0 → polos_sdk-0.1.1}/polos/middleware/hook_executor.py +0 -0
- {polos_sdk-0.1.0 → polos_sdk-0.1.1}/polos/runtime/__init__.py +0 -0
- {polos_sdk-0.1.0 → polos_sdk-0.1.1}/polos/runtime/batch.py +0 -0
- {polos_sdk-0.1.0 → polos_sdk-0.1.1}/polos/runtime/queue.py +0 -0
- {polos_sdk-0.1.0 → polos_sdk-0.1.1}/polos/runtime/worker_server.py +0 -0
- {polos_sdk-0.1.0 → polos_sdk-0.1.1}/polos/tools/__init__.py +0 -0
- {polos_sdk-0.1.0 → polos_sdk-0.1.1}/polos/types/__init__.py +0 -0
- {polos_sdk-0.1.0 → polos_sdk-0.1.1}/polos/types/types.py +0 -0
- {polos_sdk-0.1.0 → polos_sdk-0.1.1}/polos/utils/__init__.py +0 -0
- {polos_sdk-0.1.0 → polos_sdk-0.1.1}/polos/utils/agent.py +0 -0
- {polos_sdk-0.1.0 → polos_sdk-0.1.1}/polos/utils/client_context.py +0 -0
- {polos_sdk-0.1.0 → polos_sdk-0.1.1}/polos/utils/config.py +0 -0
- {polos_sdk-0.1.0 → polos_sdk-0.1.1}/polos/utils/output_schema.py +0 -0
- {polos_sdk-0.1.0 → polos_sdk-0.1.1}/polos/utils/retry.py +0 -0
- {polos_sdk-0.1.0 → polos_sdk-0.1.1}/polos/utils/tracing.py +0 -0
- {polos_sdk-0.1.0 → polos_sdk-0.1.1}/polos/utils/worker_singleton.py +0 -0
- {polos_sdk-0.1.0 → polos_sdk-0.1.1}/pyproject.toml +0 -0
- {polos_sdk-0.1.0 → polos_sdk-0.1.1}/tests/__init__.py +0 -0
- {polos_sdk-0.1.0 → polos_sdk-0.1.1}/tests/conftest.py +0 -0
- {polos_sdk-0.1.0 → polos_sdk-0.1.1}/tests/integration/__init__.py +0 -0
- {polos_sdk-0.1.0 → polos_sdk-0.1.1}/tests/integration/test_agent_execution.py +0 -0
- {polos_sdk-0.1.0 → polos_sdk-0.1.1}/tests/integration/test_runtime_interactions.py +0 -0
- {polos_sdk-0.1.0 → polos_sdk-0.1.1}/tests/integration/test_tool_execution.py +0 -0
- {polos_sdk-0.1.0 → polos_sdk-0.1.1}/tests/integration/test_workflow_execution.py +0 -0
- {polos_sdk-0.1.0 → polos_sdk-0.1.1}/tests/unit/__init__.py +0 -0
- {polos_sdk-0.1.0 → polos_sdk-0.1.1}/tests/unit/test_agents/__init__.py +0 -0
- {polos_sdk-0.1.0 → polos_sdk-0.1.1}/tests/unit/test_agents/test_agent.py +0 -0
- {polos_sdk-0.1.0 → polos_sdk-0.1.1}/tests/unit/test_agents/test_conversation_history.py +0 -0
- {polos_sdk-0.1.0 → polos_sdk-0.1.1}/tests/unit/test_agents/test_stop_conditions.py +0 -0
- {polos_sdk-0.1.0 → polos_sdk-0.1.1}/tests/unit/test_agents/test_stream.py +0 -0
- {polos_sdk-0.1.0 → polos_sdk-0.1.1}/tests/unit/test_core/__init__.py +0 -0
- {polos_sdk-0.1.0 → polos_sdk-0.1.1}/tests/unit/test_core/test_context.py +0 -0
- {polos_sdk-0.1.0 → polos_sdk-0.1.1}/tests/unit/test_core/test_state.py +0 -0
- {polos_sdk-0.1.0 → polos_sdk-0.1.1}/tests/unit/test_core/test_step.py +0 -0
- {polos_sdk-0.1.0 → polos_sdk-0.1.1}/tests/unit/test_core/test_workflow.py +0 -0
- {polos_sdk-0.1.0 → polos_sdk-0.1.1}/tests/unit/test_llm/__init__.py +0 -0
- {polos_sdk-0.1.0 → polos_sdk-0.1.1}/tests/unit/test_llm/test_providers_base.py +0 -0
- {polos_sdk-0.1.0 → polos_sdk-0.1.1}/tests/unit/test_llm/test_utils.py +0 -0
- {polos_sdk-0.1.0 → polos_sdk-0.1.1}/tests/unit/test_middleware/__init__.py +0 -0
- {polos_sdk-0.1.0 → polos_sdk-0.1.1}/tests/unit/test_middleware/test_guardrail.py +0 -0
- {polos_sdk-0.1.0 → polos_sdk-0.1.1}/tests/unit/test_middleware/test_hook.py +0 -0
- {polos_sdk-0.1.0 → polos_sdk-0.1.1}/tests/unit/test_runtime/__init__.py +0 -0
- {polos_sdk-0.1.0 → polos_sdk-0.1.1}/tests/unit/test_runtime/test_client.py +0 -0
- {polos_sdk-0.1.0 → polos_sdk-0.1.1}/tests/unit/test_utils/__init__.py +0 -0
- {polos_sdk-0.1.0 → polos_sdk-0.1.1}/tests/unit/test_utils/test_config.py +0 -0
- {polos_sdk-0.1.0 → polos_sdk-0.1.1}/tests/unit/test_utils/test_output_schema.py +0 -0
- {polos_sdk-0.1.0 → polos_sdk-0.1.1}/tests/unit/test_utils/test_retry.py +0 -0
- {polos_sdk-0.1.0 → polos_sdk-0.1.1}/tests/unit/test_utils/test_serializer.py +0 -0
- {polos_sdk-0.1.0 → polos_sdk-0.1.1}/uv.lock +0 -0
|
@@ -51,6 +51,23 @@ uv run ruff check .
|
|
|
51
51
|
uv run ruff check --fix .
|
|
52
52
|
```
|
|
53
53
|
|
|
54
|
+
#### Using uvx (Alternative)
|
|
55
|
+
|
|
56
|
+
If `uv run` fails due to version detection issues (e.g., missing git tags), use `uvx` to run tools directly without building the package:
|
|
57
|
+
|
|
58
|
+
```bash
|
|
59
|
+
# Format code
|
|
60
|
+
uvx ruff format .
|
|
61
|
+
|
|
62
|
+
# Lint code
|
|
63
|
+
uvx ruff check .
|
|
64
|
+
|
|
65
|
+
# Auto-fix linting issues
|
|
66
|
+
uvx ruff check --fix .
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
`uvx` runs the tool in an isolated environment without needing to install or build the SDK package.
|
|
70
|
+
|
|
54
71
|
## Building the Package
|
|
55
72
|
|
|
56
73
|
### Build Locally
|
polos_sdk-0.1.1/PKG-INFO
ADDED
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: polos-sdk
|
|
3
|
+
Version: 0.1.1
|
|
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
|
+
|
|
66
|
+
## Installation
|
|
67
|
+
|
|
68
|
+
```bash
|
|
69
|
+
pip install polos-sdk
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
Or with UV (recommended):
|
|
73
|
+
```bash
|
|
74
|
+
uv add polos-sdk
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
### Optional Dependencies
|
|
78
|
+
|
|
79
|
+
Install provider-specific dependencies for LLM support:
|
|
80
|
+
|
|
81
|
+
```bash
|
|
82
|
+
# OpenAI
|
|
83
|
+
pip install polos-sdk[openai]
|
|
84
|
+
|
|
85
|
+
# Anthropic
|
|
86
|
+
pip install polos-sdk[anthropic]
|
|
87
|
+
|
|
88
|
+
# Google Gemini
|
|
89
|
+
pip install polos-sdk[gemini]
|
|
90
|
+
|
|
91
|
+
# Groq
|
|
92
|
+
pip install polos-sdk[groq]
|
|
93
|
+
|
|
94
|
+
# Fireworks
|
|
95
|
+
pip install polos-sdk[fireworks]
|
|
96
|
+
|
|
97
|
+
# Together AI
|
|
98
|
+
pip install polos-sdk[together]
|
|
99
|
+
|
|
100
|
+
# All providers
|
|
101
|
+
pip install polos-sdk[openai,anthropic,gemini,groq,fireworks,together]
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
## Quick Start
|
|
105
|
+
|
|
106
|
+
Use the quickstart guide at [https://docs.polos.dev](https://docs.polos.dev) to get started in minutes.
|
|
107
|
+
|
|
108
|
+
## License
|
|
109
|
+
|
|
110
|
+
Apache-2.0 - see [LICENSE](../../LICENSE) for details.
|
|
111
|
+
|
|
112
|
+
## Support
|
|
113
|
+
|
|
114
|
+
- 📖 [Documentation](https://docs.polos.dev)
|
|
115
|
+
- 💬 [Discord Community](https://discord.gg/polos)
|
|
116
|
+
- 🐛 [Issue Tracker](https://github.com/polos-dev/polos/issues)
|
|
117
|
+
- 📧 [Email Support](mailto:support@polos.dev)
|
|
118
|
+
|
|
119
|
+
---
|
|
120
|
+
|
|
121
|
+
Built with ❤️ by the Polos team
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
# Polos Python SDK
|
|
2
|
+
|
|
3
|
+
Durable execution engine for Python. Build reliable AI agents and workflows that can survive failures, handle long-running tasks, and coordinate complex processes.
|
|
4
|
+
|
|
5
|
+
## Features
|
|
6
|
+
|
|
7
|
+
- 🤖 **AI Agents** - Build LLM-powered agents with tool calling, streaming, and conversation history
|
|
8
|
+
- 🔄 **Durable Workflows** - Workflows survive failures and resume from checkpoints
|
|
9
|
+
- ⏰ **Long-Running** - Execute workflows that run for hours or days
|
|
10
|
+
- 🔗 **Workflow Orchestration** - Chain workflows together and build complex processes
|
|
11
|
+
- 🛠️ **Tools** - Define reusable tools that agents can call
|
|
12
|
+
- 🐍 **Native Python** - Async/await support, type hints, and Pythonic APIs
|
|
13
|
+
- 📊 **Observability** - Built-in tracing, events, and monitoring
|
|
14
|
+
|
|
15
|
+
## Installation
|
|
16
|
+
|
|
17
|
+
```bash
|
|
18
|
+
pip install polos-sdk
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
Or with UV (recommended):
|
|
22
|
+
```bash
|
|
23
|
+
uv add polos-sdk
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
### Optional Dependencies
|
|
27
|
+
|
|
28
|
+
Install provider-specific dependencies for LLM support:
|
|
29
|
+
|
|
30
|
+
```bash
|
|
31
|
+
# OpenAI
|
|
32
|
+
pip install polos-sdk[openai]
|
|
33
|
+
|
|
34
|
+
# Anthropic
|
|
35
|
+
pip install polos-sdk[anthropic]
|
|
36
|
+
|
|
37
|
+
# Google Gemini
|
|
38
|
+
pip install polos-sdk[gemini]
|
|
39
|
+
|
|
40
|
+
# Groq
|
|
41
|
+
pip install polos-sdk[groq]
|
|
42
|
+
|
|
43
|
+
# Fireworks
|
|
44
|
+
pip install polos-sdk[fireworks]
|
|
45
|
+
|
|
46
|
+
# Together AI
|
|
47
|
+
pip install polos-sdk[together]
|
|
48
|
+
|
|
49
|
+
# All providers
|
|
50
|
+
pip install polos-sdk[openai,anthropic,gemini,groq,fireworks,together]
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
## Quick Start
|
|
54
|
+
|
|
55
|
+
Use the quickstart guide at [https://docs.polos.dev](https://docs.polos.dev) to get started in minutes.
|
|
56
|
+
|
|
57
|
+
## License
|
|
58
|
+
|
|
59
|
+
Apache-2.0 - see [LICENSE](../../LICENSE) for details.
|
|
60
|
+
|
|
61
|
+
## Support
|
|
62
|
+
|
|
63
|
+
- 📖 [Documentation](https://docs.polos.dev)
|
|
64
|
+
- 💬 [Discord Community](https://discord.gg/polos)
|
|
65
|
+
- 🐛 [Issue Tracker](https://github.com/polos-dev/polos/issues)
|
|
66
|
+
- 📧 [Email Support](mailto:support@polos.dev)
|
|
67
|
+
|
|
68
|
+
---
|
|
69
|
+
|
|
70
|
+
Built with ❤️ by the Polos team
|
|
@@ -351,6 +351,7 @@ class Agent(Workflow):
|
|
|
351
351
|
id: str, # Required: task ID
|
|
352
352
|
provider: str,
|
|
353
353
|
model: str,
|
|
354
|
+
description: str | None = None, # Description for team coordination
|
|
354
355
|
system_prompt: str | None = None,
|
|
355
356
|
tools: list[Any] | None = None,
|
|
356
357
|
temperature: float | None = None,
|
|
@@ -388,6 +389,7 @@ class Agent(Workflow):
|
|
|
388
389
|
super().__init__(
|
|
389
390
|
id=id,
|
|
390
391
|
func=self._agent_execute,
|
|
392
|
+
description=description,
|
|
391
393
|
workflow_type="agent",
|
|
392
394
|
queue_name=queue_name,
|
|
393
395
|
queue_concurrency_limit=queue_concurrency_limit,
|
|
@@ -689,6 +691,7 @@ class Agent(Workflow):
|
|
|
689
691
|
"input": input,
|
|
690
692
|
"session_id": session_id,
|
|
691
693
|
"user_id": user_id,
|
|
694
|
+
"conversation_id": conversation_id,
|
|
692
695
|
"streaming": True,
|
|
693
696
|
"provider_kwargs": kwargs, # Pass kwargs to provider
|
|
694
697
|
},
|
|
@@ -56,7 +56,6 @@ async def _agent_stream_function(ctx: AgentContext, payload: dict[str, Any]) ->
|
|
|
56
56
|
agent_run_id = ctx.execution_id # Use execution_id from context
|
|
57
57
|
agent_config = payload["agent_config"]
|
|
58
58
|
streaming = payload.get("streaming", True) # Default to True for backward compatibility
|
|
59
|
-
tool_stop_action = False
|
|
60
59
|
input_data = payload.get("input")
|
|
61
60
|
|
|
62
61
|
result = {
|
|
@@ -206,6 +205,23 @@ async def _agent_stream_function(ctx: AgentContext, payload: dict[str, Any]) ->
|
|
|
206
205
|
"tool_results": tool_results, # Tool results from previous iteration
|
|
207
206
|
},
|
|
208
207
|
)
|
|
208
|
+
|
|
209
|
+
if guardrails and streaming and llm_result.get("content"):
|
|
210
|
+
# Emit one text_delta event with full response for clients who are streaming
|
|
211
|
+
await ctx.step.publish_event(
|
|
212
|
+
f"llm_generate:text_delta:{agent_step}",
|
|
213
|
+
topic=f"workflow:{agent_run_id}",
|
|
214
|
+
event_type="text_delta",
|
|
215
|
+
data={
|
|
216
|
+
"step": agent_step,
|
|
217
|
+
"chunk_index": 1,
|
|
218
|
+
"content": llm_result.get("content"),
|
|
219
|
+
"_metadata": {
|
|
220
|
+
"execution_id": agent_run_id,
|
|
221
|
+
"workflow_id": ctx.workflow_id,
|
|
222
|
+
},
|
|
223
|
+
},
|
|
224
|
+
)
|
|
209
225
|
else:
|
|
210
226
|
# No guardrails - use streaming
|
|
211
227
|
llm_result = await _llm_stream(
|
|
@@ -313,7 +329,7 @@ async def _agent_stream_function(ctx: AgentContext, payload: dict[str, Any]) ->
|
|
|
313
329
|
)
|
|
314
330
|
|
|
315
331
|
# Execute all tools in batch
|
|
316
|
-
if
|
|
332
|
+
if len(batch_workflows) > 0:
|
|
317
333
|
tool_results_list: list[BatchStepResult] = await ctx.step.batch_invoke_and_wait(
|
|
318
334
|
f"execute_tools:step_{agent_step}", batch_workflows
|
|
319
335
|
)
|
|
@@ -453,7 +469,7 @@ async def _agent_stream_function(ctx: AgentContext, payload: dict[str, Any]) ->
|
|
|
453
469
|
raise StepExecutionError(hook_result.error_message or "Hook execution failed")
|
|
454
470
|
|
|
455
471
|
# No tool results, we're done
|
|
456
|
-
if tool_results is None or len(tool_results) == 0
|
|
472
|
+
if tool_results is None or len(tool_results) == 0:
|
|
457
473
|
end_steps = True
|
|
458
474
|
|
|
459
475
|
# Evaluate stop conditions (if any)
|
|
@@ -134,6 +134,13 @@ class Step:
|
|
|
134
134
|
# Extract full module path for Pydantic class
|
|
135
135
|
# (e.g., "polos.llm.providers.base.LLMResponse")
|
|
136
136
|
output_schema_name = f"{result.__class__.__module__}.{result.__class__.__name__}"
|
|
137
|
+
elif isinstance(result, list) and result and isinstance(result[0], BaseModel):
|
|
138
|
+
# Handle list of Pydantic models
|
|
139
|
+
outputs = [item.model_dump(mode="json") for item in result]
|
|
140
|
+
# Store schema name of the list item type for deserialization
|
|
141
|
+
output_schema_name = (
|
|
142
|
+
f"list[{result[0].__class__.__module__}.{result[0].__class__.__name__}]"
|
|
143
|
+
)
|
|
137
144
|
else:
|
|
138
145
|
outputs = result
|
|
139
146
|
|
|
@@ -80,6 +80,7 @@ class Workflow:
|
|
|
80
80
|
self,
|
|
81
81
|
id: str,
|
|
82
82
|
func: Callable,
|
|
83
|
+
description: str | None = None, # Description for team coordination
|
|
83
84
|
workflow_type: str | None = "workflow",
|
|
84
85
|
queue_name: str | None = None,
|
|
85
86
|
queue_concurrency_limit: int | None = None,
|
|
@@ -94,6 +95,7 @@ class Workflow:
|
|
|
94
95
|
state_schema: type[BaseModel] | None = None,
|
|
95
96
|
):
|
|
96
97
|
self.id = id
|
|
98
|
+
self.description = description # Description for team coordination
|
|
97
99
|
self.func = func
|
|
98
100
|
self.is_async = asyncio.iscoroutinefunction(func)
|
|
99
101
|
# Check if function has a payload parameter
|
|
@@ -849,6 +851,7 @@ class Workflow:
|
|
|
849
851
|
|
|
850
852
|
def workflow(
|
|
851
853
|
id: str | None = None,
|
|
854
|
+
description: str | None = None, # Description for team coordination
|
|
852
855
|
queue: str | Queue | dict[str, Any] | None = None,
|
|
853
856
|
trigger_on_event: str | None = None,
|
|
854
857
|
batch_size: int = 1,
|
|
@@ -938,6 +941,8 @@ def workflow(
|
|
|
938
941
|
|
|
939
942
|
Args:
|
|
940
943
|
id: Optional workflow ID (defaults to function name)
|
|
944
|
+
description: Optional description for team coordination. Used when this
|
|
945
|
+
workflow is added to a Team so the coordinator LLM understands its purpose.
|
|
941
946
|
queue: Optional queue configuration. Can be:
|
|
942
947
|
- str: Queue name
|
|
943
948
|
- Queue: Queue object
|
|
@@ -1158,6 +1163,7 @@ def workflow(
|
|
|
1158
1163
|
workflow_obj = Workflow(
|
|
1159
1164
|
id=workflow_id,
|
|
1160
1165
|
func=func,
|
|
1166
|
+
description=description,
|
|
1161
1167
|
queue_name=queue_name,
|
|
1162
1168
|
queue_concurrency_limit=queue_concurrency_limit,
|
|
1163
1169
|
trigger_on_event=trigger_on_event,
|
|
@@ -235,8 +235,7 @@ async def batch_publish(
|
|
|
235
235
|
async def publish(
|
|
236
236
|
client: PolosClient,
|
|
237
237
|
topic: str,
|
|
238
|
-
|
|
239
|
-
data: dict[str, Any] = None,
|
|
238
|
+
event_data: EventData,
|
|
240
239
|
execution_id: str | None = None,
|
|
241
240
|
root_execution_id: str | None = None,
|
|
242
241
|
) -> int:
|
|
@@ -247,20 +246,16 @@ async def publish(
|
|
|
247
246
|
Args:
|
|
248
247
|
client: PolosClient instance
|
|
249
248
|
topic: Event topic
|
|
250
|
-
|
|
251
|
-
|
|
249
|
+
event_data: EventData
|
|
250
|
+
execution_id: Optional execution ID
|
|
251
|
+
root_execution_id: Optional root execution ID
|
|
252
252
|
|
|
253
253
|
Returns:
|
|
254
254
|
sequence_id: Global sequence ID for the event
|
|
255
255
|
"""
|
|
256
256
|
sequence_ids = await batch_publish(
|
|
257
257
|
topic=topic,
|
|
258
|
-
events=[
|
|
259
|
-
EventData(
|
|
260
|
-
event_type=event_type,
|
|
261
|
-
data=data or {},
|
|
262
|
-
)
|
|
263
|
-
],
|
|
258
|
+
events=[event_data],
|
|
264
259
|
execution_id=execution_id,
|
|
265
260
|
root_execution_id=root_execution_id,
|
|
266
261
|
client=client,
|
|
@@ -48,17 +48,22 @@ class PolosClient:
|
|
|
48
48
|
api_url: str | None = None,
|
|
49
49
|
api_key: str | None = None,
|
|
50
50
|
project_id: str | None = None,
|
|
51
|
+
deployment_id: str | None = None,
|
|
51
52
|
):
|
|
52
53
|
"""Initialize Polos client.
|
|
53
54
|
|
|
54
55
|
Args:
|
|
55
|
-
api_url: Orchestrator API URL (default: from POLOS_API_URL env var or
|
|
56
|
+
api_url: Orchestrator API URL (default: from POLOS_API_URL env var or
|
|
57
|
+
http://localhost:8080)
|
|
56
58
|
api_key: API key for authentication (default: from POLOS_API_KEY env var)
|
|
57
59
|
project_id: Project ID for multi-tenancy (default: from POLOS_PROJECT_ID env var)
|
|
60
|
+
deployment_id: Deployment ID for workflow invocations (default: from
|
|
61
|
+
POLOS_DEPLOYMENT_ID env var, None uses latest deployment)
|
|
58
62
|
"""
|
|
59
63
|
self.api_url = api_url or os.getenv("POLOS_API_URL", "http://localhost:8080")
|
|
60
64
|
self.api_key = api_key or os.getenv("POLOS_API_KEY")
|
|
61
65
|
self.project_id = project_id or os.getenv("POLOS_PROJECT_ID")
|
|
66
|
+
self.deployment_id = deployment_id or os.getenv("POLOS_DEPLOYMENT_ID")
|
|
62
67
|
|
|
63
68
|
# Validate required fields (with local mode support)
|
|
64
69
|
local_mode_requested = os.getenv("POLOS_LOCAL_MODE", "False").lower() == "true"
|
|
@@ -158,7 +163,6 @@ class PolosClient:
|
|
|
158
163
|
Args:
|
|
159
164
|
workflow_id: The workflow identifier
|
|
160
165
|
payload: The workflow payload
|
|
161
|
-
deployment_id: Optional deployment ID (if not provided, uses latest active)
|
|
162
166
|
queue_name: Optional queue name (if not provided, defaults to workflow_id)
|
|
163
167
|
queue_concurrency_limit: Optional concurrency limit for queue creation
|
|
164
168
|
concurrency_key: Optional concurrency key for per-tenant queuing
|
|
@@ -172,7 +176,7 @@ class PolosClient:
|
|
|
172
176
|
"""
|
|
173
177
|
return await self._submit_workflow(
|
|
174
178
|
workflow_id=workflow_id,
|
|
175
|
-
deployment_id=
|
|
179
|
+
deployment_id=self.deployment_id, # Use client's deployment_id (None uses latest)
|
|
176
180
|
payload=payload,
|
|
177
181
|
queue_name=queue_name,
|
|
178
182
|
queue_concurrency_limit=queue_concurrency_limit,
|
|
@@ -235,7 +239,7 @@ class PolosClient:
|
|
|
235
239
|
# Submit all workflows in a single batch using the batch endpoint
|
|
236
240
|
handles = await self._submit_workflows(
|
|
237
241
|
workflows=workflow_requests,
|
|
238
|
-
deployment_id=
|
|
242
|
+
deployment_id=self.deployment_id, # Use client's deployment_id (None uses latest)
|
|
239
243
|
parent_execution_id=None,
|
|
240
244
|
root_execution_id=None,
|
|
241
245
|
step_key=None, # Not invoked from a step, so no step_key
|
|
@@ -187,8 +187,8 @@ class Worker:
|
|
|
187
187
|
|
|
188
188
|
# Build workflow registry
|
|
189
189
|
self.workflows_registry: dict[str, Workflow] = {}
|
|
190
|
-
self.agents: list[Agent] = [a for a in agents if isinstance(a, Agent)] or []
|
|
191
|
-
self.tools: list[Tool] = [t for t in tools if isinstance(t, Tool)] or []
|
|
190
|
+
self.agents: list[Agent] = [a for a in (agents or []) if isinstance(a, Agent)] or []
|
|
191
|
+
self.tools: list[Tool] = [t for t in (tools or []) if isinstance(t, Tool)] or []
|
|
192
192
|
self.agent_ids: list[str] = []
|
|
193
193
|
self.tool_ids: list[str] = []
|
|
194
194
|
self.workflow_ids: list[str] = []
|
|
@@ -938,7 +938,9 @@ class Worker:
|
|
|
938
938
|
# Check if error is StepExecutionError - if so, mark as non-retryable
|
|
939
939
|
# Tools are not retryable by default. We feed the error back to the LLM to handle.
|
|
940
940
|
retryable = (
|
|
941
|
-
|
|
941
|
+
workflow
|
|
942
|
+
and not isinstance(error, StepExecutionError)
|
|
943
|
+
and workflow.workflow_type != "tool"
|
|
942
944
|
)
|
|
943
945
|
await self._report_failure(
|
|
944
946
|
workflow_data["execution_id"], error_message, stack_trace, retryable=retryable
|
|
@@ -87,11 +87,31 @@ async def deserialize(obj: Any, output_schema_name: str | None = None) -> Any:
|
|
|
87
87
|
|
|
88
88
|
Args:
|
|
89
89
|
obj: Object to deserialize
|
|
90
|
-
output_schema_name: The name of the output schema
|
|
90
|
+
output_schema_name: The name of the output schema (can be
|
|
91
|
+
"list[module.ClassName]" for lists)
|
|
91
92
|
|
|
92
93
|
Returns:
|
|
93
94
|
Deserialized object
|
|
94
95
|
"""
|
|
96
|
+
# Handle list of Pydantic models (schema format: "list[module.ClassName]")
|
|
97
|
+
if output_schema_name and output_schema_name.startswith("list[") and isinstance(obj, list):
|
|
98
|
+
try:
|
|
99
|
+
# Extract the inner class name from "list[module.ClassName]"
|
|
100
|
+
inner_schema = output_schema_name[5:-1] # Remove "list[" and "]"
|
|
101
|
+
module_path, class_name = inner_schema.rsplit(".", 1)
|
|
102
|
+
module = __import__(module_path, fromlist=[class_name])
|
|
103
|
+
model_class = getattr(module, class_name)
|
|
104
|
+
|
|
105
|
+
# Validate each item in the list back to the Pydantic model
|
|
106
|
+
if issubclass(model_class, BaseModel):
|
|
107
|
+
obj = [model_class.model_validate(item) for item in obj]
|
|
108
|
+
except (ImportError, AttributeError, ValueError, TypeError) as e:
|
|
109
|
+
raise Exception(
|
|
110
|
+
f"Failed to reconstruct Pydantic model list from output_schema_name: "
|
|
111
|
+
f"{output_schema_name}. Error: {str(e)}"
|
|
112
|
+
) from e
|
|
113
|
+
return obj
|
|
114
|
+
|
|
95
115
|
# If output_schema_name is present, try to reconstruct the Pydantic model
|
|
96
116
|
if output_schema_name and isinstance(obj, dict):
|
|
97
117
|
try:
|