pop-framework 0.1.0__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.
- pop_framework-0.1.0/.claude/scheduled_tasks.lock +1 -0
- pop_framework-0.1.0/.claude/settings.local.json +7 -0
- pop_framework-0.1.0/.gitignore +32 -0
- pop_framework-0.1.0/.python-version +1 -0
- pop_framework-0.1.0/LICENSE +21 -0
- pop_framework-0.1.0/PKG-INFO +148 -0
- pop_framework-0.1.0/README.md +111 -0
- pop_framework-0.1.0/SKILLS.md +368 -0
- pop_framework-0.1.0/assets/logo.svg +23 -0
- pop_framework-0.1.0/benchmarks/bench_dx.py +121 -0
- pop_framework-0.1.0/benchmarks/bench_startup.py +329 -0
- pop_framework-0.1.0/benchmarks/results/dx_comparison.json +49 -0
- pop_framework-0.1.0/benchmarks/results/latest.json +34 -0
- pop_framework-0.1.0/benchmarks/results/latest_report.md +92 -0
- pop_framework-0.1.0/docs/benchmarks.md +46 -0
- pop_framework-0.1.0/docs/memory.md +56 -0
- pop_framework-0.1.0/docs/multi-agent.md +81 -0
- pop_framework-0.1.0/docs/providers.md +71 -0
- pop_framework-0.1.0/docs/streaming.md +46 -0
- pop_framework-0.1.0/docs/workflows.md +91 -0
- pop_framework-0.1.0/examples/01_hello_agent.py +37 -0
- pop_framework-0.1.0/examples/02_web_search_agent.py +64 -0
- pop_framework-0.1.0/examples/03_structured_output.py +56 -0
- pop_framework-0.1.0/examples/04_streaming.py +77 -0
- pop_framework-0.1.0/examples/05_multi_provider.py +54 -0
- pop_framework-0.1.0/examples/06_augmented_llm.py +103 -0
- pop_framework-0.1.0/examples/07_prompt_chaining.py +54 -0
- pop_framework-0.1.0/examples/08_routing.py +68 -0
- pop_framework-0.1.0/examples/09_parallelization.py +65 -0
- pop_framework-0.1.0/examples/10_orchestrator_workers.py +79 -0
- pop_framework-0.1.0/examples/11_evaluator_optimizer.py +72 -0
- pop_framework-0.1.0/pyproject.toml +104 -0
- pop_framework-0.1.0/src/pop/__init__.py +85 -0
- pop_framework-0.1.0/src/pop/_sync.py +32 -0
- pop_framework-0.1.0/src/pop/agent.py +328 -0
- pop_framework-0.1.0/src/pop/hooks/__init__.py +31 -0
- pop_framework-0.1.0/src/pop/hooks/base.py +66 -0
- pop_framework-0.1.0/src/pop/hooks/console.py +50 -0
- pop_framework-0.1.0/src/pop/hooks/cost.py +53 -0
- pop_framework-0.1.0/src/pop/hooks/file_log.py +46 -0
- pop_framework-0.1.0/src/pop/memory/__init__.py +24 -0
- pop_framework-0.1.0/src/pop/memory/base.py +38 -0
- pop_framework-0.1.0/src/pop/memory/inmemory.py +89 -0
- pop_framework-0.1.0/src/pop/memory/markdown.py +183 -0
- pop_framework-0.1.0/src/pop/models/__init__.py +43 -0
- pop_framework-0.1.0/src/pop/models/anthropic.py +228 -0
- pop_framework-0.1.0/src/pop/models/base.py +37 -0
- pop_framework-0.1.0/src/pop/models/deepseek.py +19 -0
- pop_framework-0.1.0/src/pop/models/gemini.py +222 -0
- pop_framework-0.1.0/src/pop/models/glm.py +19 -0
- pop_framework-0.1.0/src/pop/models/kimi.py +19 -0
- pop_framework-0.1.0/src/pop/models/minimax.py +19 -0
- pop_framework-0.1.0/src/pop/models/openai.py +187 -0
- pop_framework-0.1.0/src/pop/models/router.py +169 -0
- pop_framework-0.1.0/src/pop/multi/__init__.py +23 -0
- pop_framework-0.1.0/src/pop/multi/handoff.py +55 -0
- pop_framework-0.1.0/src/pop/multi/patterns.py +223 -0
- pop_framework-0.1.0/src/pop/py.typed +0 -0
- pop_framework-0.1.0/src/pop/runner.py +133 -0
- pop_framework-0.1.0/src/pop/tool.py +200 -0
- pop_framework-0.1.0/src/pop/types.py +314 -0
- pop_framework-0.1.0/src/pop/workflows/__init__.py +5 -0
- pop_framework-0.1.0/src/pop/workflows/patterns.py +103 -0
- pop_framework-0.1.0/tests/__init__.py +0 -0
- pop_framework-0.1.0/tests/conftest.py +52 -0
- pop_framework-0.1.0/tests/test_agent.py +892 -0
- pop_framework-0.1.0/tests/test_hooks.py +350 -0
- pop_framework-0.1.0/tests/test_memory/__init__.py +0 -0
- pop_framework-0.1.0/tests/test_memory/test_inmemory.py +110 -0
- pop_framework-0.1.0/tests/test_memory/test_markdown.py +279 -0
- pop_framework-0.1.0/tests/test_models/__init__.py +0 -0
- pop_framework-0.1.0/tests/test_models/test_anthropic.py +487 -0
- pop_framework-0.1.0/tests/test_models/test_deepseek.py +58 -0
- pop_framework-0.1.0/tests/test_models/test_gemini.py +479 -0
- pop_framework-0.1.0/tests/test_models/test_glm.py +58 -0
- pop_framework-0.1.0/tests/test_models/test_kimi.py +58 -0
- pop_framework-0.1.0/tests/test_models/test_minimax.py +66 -0
- pop_framework-0.1.0/tests/test_models/test_openai.py +516 -0
- pop_framework-0.1.0/tests/test_models/test_router.py +320 -0
- pop_framework-0.1.0/tests/test_multi/__init__.py +0 -0
- pop_framework-0.1.0/tests/test_multi/test_handoff.py +85 -0
- pop_framework-0.1.0/tests/test_multi/test_patterns.py +260 -0
- pop_framework-0.1.0/tests/test_runner.py +334 -0
- pop_framework-0.1.0/tests/test_sync.py +20 -0
- pop_framework-0.1.0/tests/test_tool.py +422 -0
- pop_framework-0.1.0/tests/test_types.py +127 -0
- pop_framework-0.1.0/tests/test_workflows/__init__.py +0 -0
- pop_framework-0.1.0/tests/test_workflows/test_patterns.py +227 -0
- pop_framework-0.1.0/uv.lock +1340 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"sessionId":"8ece6f4d-7253-482f-a1fe-c9f20d8e45ae","pid":82460,"acquiredAt":1774307712777}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
# Python
|
|
2
|
+
__pycache__/
|
|
3
|
+
*.pyc
|
|
4
|
+
*.pyo
|
|
5
|
+
*.egg-info/
|
|
6
|
+
dist/
|
|
7
|
+
build/
|
|
8
|
+
|
|
9
|
+
# Testing
|
|
10
|
+
.pytest_cache/
|
|
11
|
+
.coverage
|
|
12
|
+
htmlcov/
|
|
13
|
+
|
|
14
|
+
# Type checking & linting
|
|
15
|
+
.mypy_cache/
|
|
16
|
+
.ruff_cache/
|
|
17
|
+
|
|
18
|
+
# Environment
|
|
19
|
+
.env
|
|
20
|
+
.env.local
|
|
21
|
+
.venv/
|
|
22
|
+
venv/
|
|
23
|
+
|
|
24
|
+
# OS
|
|
25
|
+
.DS_Store
|
|
26
|
+
Thumbs.db
|
|
27
|
+
|
|
28
|
+
# IDE
|
|
29
|
+
.idea/
|
|
30
|
+
.vscode/
|
|
31
|
+
*.swp
|
|
32
|
+
*.swo
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
3.12
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 Chester Lee
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: pop-framework
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Fast, lean AI agents. 5 lines to production.
|
|
5
|
+
Project-URL: Homepage, https://github.com/WYSIATI/pop
|
|
6
|
+
Project-URL: Documentation, https://github.com/WYSIATI/pop/tree/main/docs
|
|
7
|
+
Project-URL: Repository, https://github.com/WYSIATI/pop
|
|
8
|
+
Project-URL: Issues, https://github.com/WYSIATI/pop/issues
|
|
9
|
+
Author: Chester Lee
|
|
10
|
+
License-Expression: MIT
|
|
11
|
+
License-File: LICENSE
|
|
12
|
+
Keywords: agents,ai,anthropic,framework,llm,openai
|
|
13
|
+
Classifier: Development Status :: 3 - Alpha
|
|
14
|
+
Classifier: Intended Audience :: Developers
|
|
15
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
16
|
+
Classifier: Programming Language :: Python :: 3
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
20
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
21
|
+
Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
|
|
22
|
+
Classifier: Typing :: Typed
|
|
23
|
+
Requires-Python: >=3.10
|
|
24
|
+
Requires-Dist: httpx>=0.27
|
|
25
|
+
Requires-Dist: pydantic>=2.0
|
|
26
|
+
Provides-Extra: all
|
|
27
|
+
Requires-Dist: anthropic>=0.30; extra == 'all'
|
|
28
|
+
Requires-Dist: google-genai>=1.0; extra == 'all'
|
|
29
|
+
Requires-Dist: openai>=1.0; extra == 'all'
|
|
30
|
+
Provides-Extra: anthropic
|
|
31
|
+
Requires-Dist: anthropic>=0.30; extra == 'anthropic'
|
|
32
|
+
Provides-Extra: gemini
|
|
33
|
+
Requires-Dist: google-genai>=1.0; extra == 'gemini'
|
|
34
|
+
Provides-Extra: openai
|
|
35
|
+
Requires-Dist: openai>=1.0; extra == 'openai'
|
|
36
|
+
Description-Content-Type: text/markdown
|
|
37
|
+
|
|
38
|
+
<p align="center">
|
|
39
|
+
<img src="assets/logo.svg" alt="pop" width="300">
|
|
40
|
+
</p>
|
|
41
|
+
|
|
42
|
+
<p align="center">
|
|
43
|
+
<em>Fast, lean AI agents. 5 lines to production.</em>
|
|
44
|
+
</p>
|
|
45
|
+
|
|
46
|
+
<p align="center">
|
|
47
|
+
<a href="https://github.com/WYSIATI/pop/actions"><img src="https://img.shields.io/github/actions/workflow/status/WYSIATI/pop/ci.yml?label=CI" alt="CI"></a>
|
|
48
|
+
<a href="https://github.com/WYSIATI/pop"><img src="https://img.shields.io/badge/coverage-99%25-brightgreen" alt="Coverage"></a>
|
|
49
|
+
<a href="https://pypi.org/project/pop-framework/"><img src="https://img.shields.io/pypi/v/pop-framework" alt="PyPI"></a>
|
|
50
|
+
<a href="https://pypi.org/project/pop-framework/"><img src="https://img.shields.io/pypi/pyversions/pop-framework" alt="Python"></a>
|
|
51
|
+
<img src="https://img.shields.io/badge/license-MIT-blue" alt="License">
|
|
52
|
+
</p>
|
|
53
|
+
|
|
54
|
+
<p align="center">
|
|
55
|
+
<a href="docs/">Documentation</a> |
|
|
56
|
+
<a href="https://github.com/WYSIATI/pop">Source Code</a> |
|
|
57
|
+
<a href="https://discord.gg/pop">Discord</a>
|
|
58
|
+
</p>
|
|
59
|
+
|
|
60
|
+
---
|
|
61
|
+
|
|
62
|
+
**pop** is a lightweight Python framework for building AI agents. It supports multiple LLM providers, has 5 core concepts, and gets you from install to a working agent in under 2 minutes.
|
|
63
|
+
|
|
64
|
+
## Why pop?
|
|
65
|
+
|
|
66
|
+
- **5 lines to a working agent** -- define a tool, create an agent, call `run`.
|
|
67
|
+
- **7 LLM providers built-in** -- OpenAI, Anthropic, Gemini, DeepSeek, Kimi, MiniMax, GLM. Switch by changing one string.
|
|
68
|
+
- **~2,500 lines of code** -- read the entire framework in an afternoon.
|
|
69
|
+
- **2 runtime dependencies** -- `httpx` and `pydantic`. Import time under 1ms (lazy imports).
|
|
70
|
+
- **Zero commercial dependencies** -- no forced telemetry, no vendor lock-in.
|
|
71
|
+
|
|
72
|
+
## Install
|
|
73
|
+
|
|
74
|
+
```bash
|
|
75
|
+
# Recommended
|
|
76
|
+
uv add pop-framework
|
|
77
|
+
|
|
78
|
+
# With a provider extra
|
|
79
|
+
uv add "pop-framework[openai]"
|
|
80
|
+
uv add "pop-framework[anthropic]"
|
|
81
|
+
uv add "pop-framework[all]"
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
Or with pip:
|
|
85
|
+
|
|
86
|
+
```bash
|
|
87
|
+
pip install pop-framework
|
|
88
|
+
pip install "pop-framework[openai]"
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
## Quick Start
|
|
92
|
+
|
|
93
|
+
```python
|
|
94
|
+
from pop import Agent, tool
|
|
95
|
+
|
|
96
|
+
@tool
|
|
97
|
+
def search(query: str) -> str:
|
|
98
|
+
"""Search the web for current information."""
|
|
99
|
+
return web_search(query) # your implementation
|
|
100
|
+
|
|
101
|
+
agent = Agent(model="openai:gpt-4o", tools=[search])
|
|
102
|
+
result = agent.run("What happened in AI today?")
|
|
103
|
+
print(result.output)
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
That's it. No `StateGraph`, no `RunnableSequence`, no `ChannelWrite`.
|
|
107
|
+
|
|
108
|
+
## Docs
|
|
109
|
+
|
|
110
|
+
| Guide | What it covers |
|
|
111
|
+
|-------|---------------|
|
|
112
|
+
| [Providers](docs/providers.md) | Switching LLMs, failover, model adapters |
|
|
113
|
+
| [Streaming](docs/streaming.md) | Real-time events, pattern matching |
|
|
114
|
+
| [Workflows](docs/workflows.md) | Chain, route, parallel, agent, orchestration |
|
|
115
|
+
| [Multi-Agent](docs/multi-agent.md) | Handoff, pipeline, debate, fan_out |
|
|
116
|
+
| [Memory](docs/memory.md) | In-memory and markdown-based persistence |
|
|
117
|
+
| [Skills](SKILLS.md) | Complete API guide for building agents |
|
|
118
|
+
| [Benchmarks](docs/benchmarks.md) | Performance numbers, framework comparison |
|
|
119
|
+
|
|
120
|
+
## Benchmarks
|
|
121
|
+
|
|
122
|
+
| Metric | pop | LangChain + LangGraph | Delta |
|
|
123
|
+
|--------|-----|----------------------|-------|
|
|
124
|
+
| Framework overhead | ~0.15ms | ~45ms | ~300x faster |
|
|
125
|
+
| Import time | ~0.17ms | ~1,200ms | ~7,000x faster |
|
|
126
|
+
| Lines of code (avg task) | ~12 | ~42 | 71% less |
|
|
127
|
+
| Dependencies | 2 | 20+ | 90% fewer |
|
|
128
|
+
|
|
129
|
+
Details: [docs/benchmarks.md](docs/benchmarks.md)
|
|
130
|
+
|
|
131
|
+
## Community
|
|
132
|
+
|
|
133
|
+
- [Discord](https://discord.gg/pop) -- questions, help, showcase
|
|
134
|
+
- [GitHub Issues](https://github.com/WYSIATI/pop/issues) -- bug reports
|
|
135
|
+
- [GitHub Discussions](https://github.com/WYSIATI/pop/discussions) -- feature ideas, Q&A
|
|
136
|
+
|
|
137
|
+
## Contributing
|
|
138
|
+
|
|
139
|
+
```bash
|
|
140
|
+
git clone https://github.com/WYSIATI/pop.git
|
|
141
|
+
cd pop
|
|
142
|
+
uv sync --group dev
|
|
143
|
+
uv run pytest
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
## License
|
|
147
|
+
|
|
148
|
+
MIT
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
<p align="center">
|
|
2
|
+
<img src="assets/logo.svg" alt="pop" width="300">
|
|
3
|
+
</p>
|
|
4
|
+
|
|
5
|
+
<p align="center">
|
|
6
|
+
<em>Fast, lean AI agents. 5 lines to production.</em>
|
|
7
|
+
</p>
|
|
8
|
+
|
|
9
|
+
<p align="center">
|
|
10
|
+
<a href="https://github.com/WYSIATI/pop/actions"><img src="https://img.shields.io/github/actions/workflow/status/WYSIATI/pop/ci.yml?label=CI" alt="CI"></a>
|
|
11
|
+
<a href="https://github.com/WYSIATI/pop"><img src="https://img.shields.io/badge/coverage-99%25-brightgreen" alt="Coverage"></a>
|
|
12
|
+
<a href="https://pypi.org/project/pop-framework/"><img src="https://img.shields.io/pypi/v/pop-framework" alt="PyPI"></a>
|
|
13
|
+
<a href="https://pypi.org/project/pop-framework/"><img src="https://img.shields.io/pypi/pyversions/pop-framework" alt="Python"></a>
|
|
14
|
+
<img src="https://img.shields.io/badge/license-MIT-blue" alt="License">
|
|
15
|
+
</p>
|
|
16
|
+
|
|
17
|
+
<p align="center">
|
|
18
|
+
<a href="docs/">Documentation</a> |
|
|
19
|
+
<a href="https://github.com/WYSIATI/pop">Source Code</a> |
|
|
20
|
+
<a href="https://discord.gg/pop">Discord</a>
|
|
21
|
+
</p>
|
|
22
|
+
|
|
23
|
+
---
|
|
24
|
+
|
|
25
|
+
**pop** is a lightweight Python framework for building AI agents. It supports multiple LLM providers, has 5 core concepts, and gets you from install to a working agent in under 2 minutes.
|
|
26
|
+
|
|
27
|
+
## Why pop?
|
|
28
|
+
|
|
29
|
+
- **5 lines to a working agent** -- define a tool, create an agent, call `run`.
|
|
30
|
+
- **7 LLM providers built-in** -- OpenAI, Anthropic, Gemini, DeepSeek, Kimi, MiniMax, GLM. Switch by changing one string.
|
|
31
|
+
- **~2,500 lines of code** -- read the entire framework in an afternoon.
|
|
32
|
+
- **2 runtime dependencies** -- `httpx` and `pydantic`. Import time under 1ms (lazy imports).
|
|
33
|
+
- **Zero commercial dependencies** -- no forced telemetry, no vendor lock-in.
|
|
34
|
+
|
|
35
|
+
## Install
|
|
36
|
+
|
|
37
|
+
```bash
|
|
38
|
+
# Recommended
|
|
39
|
+
uv add pop-framework
|
|
40
|
+
|
|
41
|
+
# With a provider extra
|
|
42
|
+
uv add "pop-framework[openai]"
|
|
43
|
+
uv add "pop-framework[anthropic]"
|
|
44
|
+
uv add "pop-framework[all]"
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
Or with pip:
|
|
48
|
+
|
|
49
|
+
```bash
|
|
50
|
+
pip install pop-framework
|
|
51
|
+
pip install "pop-framework[openai]"
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
## Quick Start
|
|
55
|
+
|
|
56
|
+
```python
|
|
57
|
+
from pop import Agent, tool
|
|
58
|
+
|
|
59
|
+
@tool
|
|
60
|
+
def search(query: str) -> str:
|
|
61
|
+
"""Search the web for current information."""
|
|
62
|
+
return web_search(query) # your implementation
|
|
63
|
+
|
|
64
|
+
agent = Agent(model="openai:gpt-4o", tools=[search])
|
|
65
|
+
result = agent.run("What happened in AI today?")
|
|
66
|
+
print(result.output)
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
That's it. No `StateGraph`, no `RunnableSequence`, no `ChannelWrite`.
|
|
70
|
+
|
|
71
|
+
## Docs
|
|
72
|
+
|
|
73
|
+
| Guide | What it covers |
|
|
74
|
+
|-------|---------------|
|
|
75
|
+
| [Providers](docs/providers.md) | Switching LLMs, failover, model adapters |
|
|
76
|
+
| [Streaming](docs/streaming.md) | Real-time events, pattern matching |
|
|
77
|
+
| [Workflows](docs/workflows.md) | Chain, route, parallel, agent, orchestration |
|
|
78
|
+
| [Multi-Agent](docs/multi-agent.md) | Handoff, pipeline, debate, fan_out |
|
|
79
|
+
| [Memory](docs/memory.md) | In-memory and markdown-based persistence |
|
|
80
|
+
| [Skills](SKILLS.md) | Complete API guide for building agents |
|
|
81
|
+
| [Benchmarks](docs/benchmarks.md) | Performance numbers, framework comparison |
|
|
82
|
+
|
|
83
|
+
## Benchmarks
|
|
84
|
+
|
|
85
|
+
| Metric | pop | LangChain + LangGraph | Delta |
|
|
86
|
+
|--------|-----|----------------------|-------|
|
|
87
|
+
| Framework overhead | ~0.15ms | ~45ms | ~300x faster |
|
|
88
|
+
| Import time | ~0.17ms | ~1,200ms | ~7,000x faster |
|
|
89
|
+
| Lines of code (avg task) | ~12 | ~42 | 71% less |
|
|
90
|
+
| Dependencies | 2 | 20+ | 90% fewer |
|
|
91
|
+
|
|
92
|
+
Details: [docs/benchmarks.md](docs/benchmarks.md)
|
|
93
|
+
|
|
94
|
+
## Community
|
|
95
|
+
|
|
96
|
+
- [Discord](https://discord.gg/pop) -- questions, help, showcase
|
|
97
|
+
- [GitHub Issues](https://github.com/WYSIATI/pop/issues) -- bug reports
|
|
98
|
+
- [GitHub Discussions](https://github.com/WYSIATI/pop/discussions) -- feature ideas, Q&A
|
|
99
|
+
|
|
100
|
+
## Contributing
|
|
101
|
+
|
|
102
|
+
```bash
|
|
103
|
+
git clone https://github.com/WYSIATI/pop.git
|
|
104
|
+
cd pop
|
|
105
|
+
uv sync --group dev
|
|
106
|
+
uv run pytest
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
## License
|
|
110
|
+
|
|
111
|
+
MIT
|
|
@@ -0,0 +1,368 @@
|
|
|
1
|
+
# Skills Guide
|
|
2
|
+
|
|
3
|
+
How to build AI agents with pop. This guide is written for coding agents (Claude, Cursor, Copilot) and humans alike.
|
|
4
|
+
|
|
5
|
+
## 5 Core Concepts
|
|
6
|
+
|
|
7
|
+
| Concept | What it does |
|
|
8
|
+
|---------|-------------|
|
|
9
|
+
| `Agent` | ReAct loop: thinks, calls tools, repeats until done |
|
|
10
|
+
| `@tool` | Turns a Python function into a tool the LLM can call |
|
|
11
|
+
| `Runner` | Streams events from an agent run |
|
|
12
|
+
| Memory | Pluggable storage (in-memory or markdown files) |
|
|
13
|
+
| Patterns | Multi-agent composition: `handoff`, `pipeline`, `debate`, `orchestrate`, `fan_out` |
|
|
14
|
+
|
|
15
|
+
## Install
|
|
16
|
+
|
|
17
|
+
```bash
|
|
18
|
+
uv add pop-framework
|
|
19
|
+
# With a provider
|
|
20
|
+
uv add "pop-framework[openai]"
|
|
21
|
+
uv add "pop-framework[anthropic]"
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
## 1. Minimal Agent
|
|
25
|
+
|
|
26
|
+
```python
|
|
27
|
+
from pop import Agent, tool
|
|
28
|
+
|
|
29
|
+
@tool
|
|
30
|
+
def add(a: float, b: float) -> float:
|
|
31
|
+
"""Add two numbers.
|
|
32
|
+
|
|
33
|
+
Args:
|
|
34
|
+
a: First number.
|
|
35
|
+
b: Second number.
|
|
36
|
+
"""
|
|
37
|
+
return a + b
|
|
38
|
+
|
|
39
|
+
agent = Agent(model="openai:gpt-4o-mini", tools=[add])
|
|
40
|
+
result = agent.run("What is 42 + 17?")
|
|
41
|
+
print(result.output)
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
**Key points:**
|
|
45
|
+
- `@tool` reads the function signature and docstring to generate JSON Schema automatically
|
|
46
|
+
- Use Google-style docstrings with `Args:` section for parameter descriptions
|
|
47
|
+
- `model` is a string in `provider:model-name` format
|
|
48
|
+
|
|
49
|
+
## 2. Defining Tools
|
|
50
|
+
|
|
51
|
+
```python
|
|
52
|
+
from pop import tool
|
|
53
|
+
|
|
54
|
+
# Simple tool
|
|
55
|
+
@tool
|
|
56
|
+
def search(query: str) -> str:
|
|
57
|
+
"""Search the web."""
|
|
58
|
+
return f"Results for {query}"
|
|
59
|
+
|
|
60
|
+
# Tool with optional params
|
|
61
|
+
@tool
|
|
62
|
+
def fetch_page(url: str, max_length: int = 5000) -> str:
|
|
63
|
+
"""Fetch a web page.
|
|
64
|
+
|
|
65
|
+
Args:
|
|
66
|
+
url: The URL to fetch.
|
|
67
|
+
max_length: Max characters to return.
|
|
68
|
+
"""
|
|
69
|
+
return httpx.get(url).text[:max_length]
|
|
70
|
+
|
|
71
|
+
# Tool with Pydantic input
|
|
72
|
+
from pydantic import BaseModel, Field
|
|
73
|
+
|
|
74
|
+
class Contact(BaseModel):
|
|
75
|
+
name: str = Field(description="Full name")
|
|
76
|
+
email: str = Field(description="Email address")
|
|
77
|
+
|
|
78
|
+
@tool
|
|
79
|
+
def save_contact(contact: Contact) -> str:
|
|
80
|
+
"""Save a contact to the database.
|
|
81
|
+
|
|
82
|
+
Args:
|
|
83
|
+
contact: Contact information.
|
|
84
|
+
"""
|
|
85
|
+
return f"Saved {contact.name}"
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
**Supported types:** `str`, `int`, `float`, `bool`, `list[T]`, `dict`, Pydantic models, `Optional[T]`.
|
|
89
|
+
|
|
90
|
+
## 3. Agent Configuration
|
|
91
|
+
|
|
92
|
+
```python
|
|
93
|
+
agent = Agent(
|
|
94
|
+
model="openai:gpt-4o", # or "anthropic:claude-sonnet-4-20250514"
|
|
95
|
+
name="researcher", # name for multi-agent identification
|
|
96
|
+
tools=[search, fetch_page], # list of @tool-decorated functions
|
|
97
|
+
instructions="You are a research assistant. Cite sources.",
|
|
98
|
+
max_steps=10, # max ReAct loop iterations (default: 10)
|
|
99
|
+
max_cost=0.50, # USD budget cap (optional)
|
|
100
|
+
max_retries=3, # retries on transient errors
|
|
101
|
+
reflect_on_failure=False, # enable Reflexion loop on errors
|
|
102
|
+
output_guardrails=[is_safe], # validators for final answer
|
|
103
|
+
)
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
**Model fallback:** pass a list to try models in order.
|
|
107
|
+
|
|
108
|
+
```python
|
|
109
|
+
agent = Agent(
|
|
110
|
+
model=["anthropic:claude-sonnet-4-20250514", "openai:gpt-4o", "openai:gpt-4o-mini"],
|
|
111
|
+
tools=[search],
|
|
112
|
+
)
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
## 4. Running Agents
|
|
116
|
+
|
|
117
|
+
### Sync
|
|
118
|
+
|
|
119
|
+
```python
|
|
120
|
+
result = agent.run("What happened in AI today?")
|
|
121
|
+
print(result.output) # final answer string
|
|
122
|
+
print(result.cost) # total USD cost
|
|
123
|
+
print(result.token_usage) # TokenUsage(input_tokens=..., output_tokens=...)
|
|
124
|
+
print(result.steps) # list[Step] -- full execution trace
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
### Async
|
|
128
|
+
|
|
129
|
+
```python
|
|
130
|
+
result = await agent.arun("What happened in AI today?")
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
### Streaming
|
|
134
|
+
|
|
135
|
+
```python
|
|
136
|
+
import asyncio
|
|
137
|
+
from pop import Agent, Runner, ToolCallEvent, ToolResultEvent, TextDeltaEvent, DoneEvent, tool
|
|
138
|
+
|
|
139
|
+
runner = Runner(agent)
|
|
140
|
+
|
|
141
|
+
async for event in runner.stream("What should I wear in SF?"):
|
|
142
|
+
match event:
|
|
143
|
+
case ToolCallEvent(name=name, args=args):
|
|
144
|
+
print(f"Calling {name}({args})")
|
|
145
|
+
case ToolResultEvent(name=name, output=output):
|
|
146
|
+
print(f"{name} -> {output}")
|
|
147
|
+
case TextDeltaEvent(delta=text):
|
|
148
|
+
print(text, end="")
|
|
149
|
+
case DoneEvent(result=result):
|
|
150
|
+
print(f"\nCost: ${result.cost:.6f}")
|
|
151
|
+
```
|
|
152
|
+
|
|
153
|
+
## 5. Multi-Agent Patterns
|
|
154
|
+
|
|
155
|
+
### Handoff
|
|
156
|
+
|
|
157
|
+
Route to specialist agents based on the task.
|
|
158
|
+
|
|
159
|
+
```python
|
|
160
|
+
from pop import Agent, handoff, tool
|
|
161
|
+
|
|
162
|
+
billing = Agent(model="openai:gpt-4o-mini", tools=[lookup_invoice],
|
|
163
|
+
instructions="Handle billing questions.")
|
|
164
|
+
tech = Agent(model="openai:gpt-4o-mini", tools=[check_logs],
|
|
165
|
+
instructions="Handle technical issues.")
|
|
166
|
+
|
|
167
|
+
triage = Agent(
|
|
168
|
+
model="openai:gpt-4o-mini",
|
|
169
|
+
tools=[
|
|
170
|
+
handoff(billing, when="billing or payment issues"),
|
|
171
|
+
handoff(tech, when="technical problems or errors"),
|
|
172
|
+
],
|
|
173
|
+
)
|
|
174
|
+
result = triage.run("I was charged twice")
|
|
175
|
+
```
|
|
176
|
+
|
|
177
|
+
### Pipeline
|
|
178
|
+
|
|
179
|
+
Sequential: each agent's output feeds into the next.
|
|
180
|
+
|
|
181
|
+
```python
|
|
182
|
+
from pop import Agent, pipeline
|
|
183
|
+
|
|
184
|
+
result = await pipeline(
|
|
185
|
+
[researcher, writer, editor],
|
|
186
|
+
task="Report on AI agent frameworks",
|
|
187
|
+
)
|
|
188
|
+
```
|
|
189
|
+
|
|
190
|
+
### Debate
|
|
191
|
+
|
|
192
|
+
Generator proposes, critic reviews, loop until approved.
|
|
193
|
+
|
|
194
|
+
```python
|
|
195
|
+
from pop import Agent, debate
|
|
196
|
+
|
|
197
|
+
result = await debate(writer, editor, task="Write a product announcement", max_rounds=3)
|
|
198
|
+
```
|
|
199
|
+
|
|
200
|
+
### Orchestrate
|
|
201
|
+
|
|
202
|
+
A boss agent dynamically delegates to workers.
|
|
203
|
+
|
|
204
|
+
```python
|
|
205
|
+
from pop import Agent, orchestrate
|
|
206
|
+
|
|
207
|
+
boss = Agent(model="openai:gpt-4o", name="boss",
|
|
208
|
+
instructions="Coordinate research, writing, and SEO.")
|
|
209
|
+
result = await orchestrate(boss, [researcher, writer, seo_optimizer], task="Write a blog post")
|
|
210
|
+
```
|
|
211
|
+
|
|
212
|
+
### Fan Out
|
|
213
|
+
|
|
214
|
+
Run multiple agents in parallel, aggregate results.
|
|
215
|
+
|
|
216
|
+
```python
|
|
217
|
+
from pop import Agent, fan_out
|
|
218
|
+
|
|
219
|
+
result = await fan_out(
|
|
220
|
+
[analyst_a, analyst_b, analyst_c],
|
|
221
|
+
task="Analyze Q4 earnings",
|
|
222
|
+
strategy="majority", # or "first", "weighted_vote"
|
|
223
|
+
)
|
|
224
|
+
```
|
|
225
|
+
|
|
226
|
+
## 6. Workflows (No Agent Loop)
|
|
227
|
+
|
|
228
|
+
For simpler LLM patterns that don't need tool-calling or a ReAct loop.
|
|
229
|
+
|
|
230
|
+
```python
|
|
231
|
+
from pop import chain, route, parallel, model
|
|
232
|
+
|
|
233
|
+
m = model("openai:gpt-4o")
|
|
234
|
+
|
|
235
|
+
# Chain: sequential prompts, {prev} is replaced with the previous output
|
|
236
|
+
result = await chain(m, steps=[
|
|
237
|
+
"Summarize: {input}",
|
|
238
|
+
"Translate to French: {prev}",
|
|
239
|
+
], input_text="...")
|
|
240
|
+
|
|
241
|
+
# Route: classify and dispatch
|
|
242
|
+
result = await route(m, "refund request", {
|
|
243
|
+
"refund": handle_refund,
|
|
244
|
+
"support": handle_support,
|
|
245
|
+
})
|
|
246
|
+
|
|
247
|
+
# Parallel: concurrent LLM calls
|
|
248
|
+
results = await parallel(m, [
|
|
249
|
+
"Summarize: {context}",
|
|
250
|
+
"Extract keywords: {context}",
|
|
251
|
+
], context="...")
|
|
252
|
+
```
|
|
253
|
+
|
|
254
|
+
## 7. Memory
|
|
255
|
+
|
|
256
|
+
### In-Memory (Default)
|
|
257
|
+
|
|
258
|
+
No configuration needed. State is lost when the process exits.
|
|
259
|
+
|
|
260
|
+
### Markdown Memory (Persistent)
|
|
261
|
+
|
|
262
|
+
```python
|
|
263
|
+
from pop.memory import MarkdownMemory
|
|
264
|
+
|
|
265
|
+
# Default: stores in ~/.pop/memory
|
|
266
|
+
memory = MarkdownMemory()
|
|
267
|
+
|
|
268
|
+
# Or specify a custom directory
|
|
269
|
+
memory = MarkdownMemory(base_dir="./agent_memory")
|
|
270
|
+
|
|
271
|
+
agent = Agent(
|
|
272
|
+
model="openai:gpt-4o",
|
|
273
|
+
tools=[search],
|
|
274
|
+
memory=memory,
|
|
275
|
+
core_memory={
|
|
276
|
+
"user_name": "Chester",
|
|
277
|
+
"preferences": "Concise answers. No fluff.",
|
|
278
|
+
},
|
|
279
|
+
)
|
|
280
|
+
```
|
|
281
|
+
|
|
282
|
+
Default directory: `~/.pop/memory`. Override with `POP_MEMORY_DIR` env var or the `base_dir` argument.
|
|
283
|
+
|
|
284
|
+
Core memory (`dict[str, str]`) is always included in context. Episodes and conversations persist as markdown files on disk.
|
|
285
|
+
|
|
286
|
+
## 8. Hooks
|
|
287
|
+
|
|
288
|
+
```python
|
|
289
|
+
from pop.hooks import ConsoleHook, CostHook, FileLogHook
|
|
290
|
+
|
|
291
|
+
runner = Runner(agent, hooks=[
|
|
292
|
+
ConsoleHook(), # print steps to stdout
|
|
293
|
+
CostHook(budget=1.00), # track cumulative cost
|
|
294
|
+
FileLogHook(path="./agent.log"), # append steps to JSON file
|
|
295
|
+
])
|
|
296
|
+
```
|
|
297
|
+
|
|
298
|
+
## 9. Providers
|
|
299
|
+
|
|
300
|
+
7 built-in: OpenAI, Anthropic, Gemini, DeepSeek, Kimi, MiniMax, GLM.
|
|
301
|
+
|
|
302
|
+
```python
|
|
303
|
+
# Switch by changing the string
|
|
304
|
+
Agent(model="openai:gpt-4o")
|
|
305
|
+
Agent(model="anthropic:claude-sonnet-4-20250514")
|
|
306
|
+
Agent(model="gemini:gemini-2.0-flash")
|
|
307
|
+
Agent(model="deepseek:deepseek-chat")
|
|
308
|
+
```
|
|
309
|
+
|
|
310
|
+
Custom providers implement `ModelAdapter`:
|
|
311
|
+
|
|
312
|
+
```python
|
|
313
|
+
from pop.models import ModelAdapter, register_provider
|
|
314
|
+
|
|
315
|
+
class MyAdapter(ModelAdapter):
|
|
316
|
+
async def chat(self, messages, tools=None):
|
|
317
|
+
... # return ModelResponse
|
|
318
|
+
|
|
319
|
+
async def chat_stream(self, messages, tools=None):
|
|
320
|
+
... # yield StreamChunk
|
|
321
|
+
|
|
322
|
+
register_provider("my_provider", MyAdapter)
|
|
323
|
+
Agent(model="my_provider:my-model")
|
|
324
|
+
```
|
|
325
|
+
|
|
326
|
+
## 10. Inspecting Results
|
|
327
|
+
|
|
328
|
+
Every run returns `AgentResult` with a full trace:
|
|
329
|
+
|
|
330
|
+
```python
|
|
331
|
+
result = agent.run("...")
|
|
332
|
+
|
|
333
|
+
# Walk the step trace
|
|
334
|
+
for step in result.steps:
|
|
335
|
+
print(f"Step {step.index}: {step.tool_name or 'final answer'}")
|
|
336
|
+
if step.tool_name:
|
|
337
|
+
print(f" Args: {step.tool_args}")
|
|
338
|
+
print(f" Result: {step.tool_result}")
|
|
339
|
+
print(f" Tokens: {step.token_usage.total}")
|
|
340
|
+
print(f" Cost: ${step.cost_usd:.6f}")
|
|
341
|
+
|
|
342
|
+
# Totals
|
|
343
|
+
print(f"Total cost: ${result.cost:.6f}")
|
|
344
|
+
print(f"Total tokens: {result.token_usage.total}")
|
|
345
|
+
print(f"Run ID: {result.run_id}")
|
|
346
|
+
```
|
|
347
|
+
|
|
348
|
+
## Quick Reference
|
|
349
|
+
|
|
350
|
+
```python
|
|
351
|
+
from pop import (
|
|
352
|
+
# Core
|
|
353
|
+
Agent, tool, Runner, run,
|
|
354
|
+
# Multi-agent
|
|
355
|
+
handoff, pipeline, orchestrate, debate, fan_out,
|
|
356
|
+
# Workflows
|
|
357
|
+
chain, route, parallel,
|
|
358
|
+
# Models
|
|
359
|
+
chat, model, register_provider,
|
|
360
|
+
# Types
|
|
361
|
+
AgentResult, Step, TokenUsage, Message,
|
|
362
|
+
# Stream events
|
|
363
|
+
ThinkEvent, ToolCallEvent, ToolResultEvent, TextDeltaEvent, DoneEvent,
|
|
364
|
+
)
|
|
365
|
+
|
|
366
|
+
from pop.memory import MarkdownMemory, InMemoryStore
|
|
367
|
+
from pop.hooks import ConsoleHook, CostHook, FileLogHook
|
|
368
|
+
```
|