pico-agent 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.
- pico_agent-0.1.0/.coveragerc +17 -0
- pico_agent-0.1.0/.github/workflows/ci.yml +102 -0
- pico_agent-0.1.0/.github/workflows/publish-to-pypi.yml +36 -0
- pico_agent-0.1.0/LICENSE +21 -0
- pico_agent-0.1.0/MANIFEST.in +18 -0
- pico_agent-0.1.0/PKG-INFO +63 -0
- pico_agent-0.1.0/README.md +228 -0
- pico_agent-0.1.0/pyproject.toml +75 -0
- pico_agent-0.1.0/setup.cfg +4 -0
- pico_agent-0.1.0/src/pico_agent/__init__.py +31 -0
- pico_agent-0.1.0/src/pico_agent/_version.py +1 -0
- pico_agent-0.1.0/src/pico_agent/config.py +36 -0
- pico_agent-0.1.0/src/pico_agent/decorators.py +42 -0
- pico_agent-0.1.0/src/pico_agent/exceptions.py +9 -0
- pico_agent-0.1.0/src/pico_agent/factory.py +65 -0
- pico_agent-0.1.0/src/pico_agent/interceptor.py +25 -0
- pico_agent-0.1.0/src/pico_agent/interfaces.py +14 -0
- pico_agent-0.1.0/src/pico_agent/providers.py +210 -0
- pico_agent-0.1.0/src/pico_agent/proxy.py +234 -0
- pico_agent-0.1.0/src/pico_agent/registry.py +77 -0
- pico_agent-0.1.0/src/pico_agent/router.py +23 -0
- pico_agent-0.1.0/src/pico_agent/scanner.py +37 -0
- pico_agent-0.1.0/src/pico_agent/tools.py +30 -0
- pico_agent-0.1.0/src/pico_agent/validation.py +43 -0
- pico_agent-0.1.0/src/pico_agent.egg-info/PKG-INFO +63 -0
- pico_agent-0.1.0/src/pico_agent.egg-info/SOURCES.txt +31 -0
- pico_agent-0.1.0/src/pico_agent.egg-info/dependency_links.txt +1 -0
- pico_agent-0.1.0/src/pico_agent.egg-info/entry_points.txt +2 -0
- pico_agent-0.1.0/src/pico_agent.egg-info/requires.txt +24 -0
- pico_agent-0.1.0/src/pico_agent.egg-info/top_level.txt +1 -0
- pico_agent-0.1.0/tests/test_gemini_real.py +49 -0
- pico_agent-0.1.0/tests/test_integration.py +108 -0
- pico_agent-0.1.0/tox.ini +34 -0
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
[run]
|
|
2
|
+
branch = True
|
|
3
|
+
source =
|
|
4
|
+
pico_agent
|
|
5
|
+
omit =
|
|
6
|
+
*/_version.py
|
|
7
|
+
|
|
8
|
+
[paths]
|
|
9
|
+
pico_agent =
|
|
10
|
+
src/pico_agent
|
|
11
|
+
.tox/*/lib/*/site-packages/pico_agent
|
|
12
|
+
.tox/*/site-packages/pico_agent
|
|
13
|
+
*/site-packages/pico_agent
|
|
14
|
+
|
|
15
|
+
[report]
|
|
16
|
+
show_missing = True
|
|
17
|
+
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
name: CI & Coverage
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches: [ main ]
|
|
6
|
+
pull_request:
|
|
7
|
+
branches: [ main ]
|
|
8
|
+
|
|
9
|
+
jobs:
|
|
10
|
+
tests:
|
|
11
|
+
name: Tests (Python ${{ matrix.python-version }})
|
|
12
|
+
runs-on: ubuntu-latest
|
|
13
|
+
strategy:
|
|
14
|
+
fail-fast: false
|
|
15
|
+
matrix:
|
|
16
|
+
python-version: [ "3.10", "3.11", "3.12", "3.13", "3.14" ]
|
|
17
|
+
|
|
18
|
+
steps:
|
|
19
|
+
- name: Checkout
|
|
20
|
+
uses: actions/checkout@v4
|
|
21
|
+
with:
|
|
22
|
+
fetch-depth: 0
|
|
23
|
+
|
|
24
|
+
- name: Set up Python ${{ matrix.python-version }}
|
|
25
|
+
uses: actions/setup-python@v5
|
|
26
|
+
with:
|
|
27
|
+
python-version: ${{ matrix.python-version }}
|
|
28
|
+
cache: pip
|
|
29
|
+
|
|
30
|
+
- name: Install tox
|
|
31
|
+
run: |
|
|
32
|
+
python -m pip install --upgrade pip
|
|
33
|
+
pip install tox
|
|
34
|
+
|
|
35
|
+
- name: Run tests with coverage (writes to repo root)
|
|
36
|
+
shell: bash
|
|
37
|
+
run: |
|
|
38
|
+
set -euxo pipefail
|
|
39
|
+
tox -e cov
|
|
40
|
+
# Rename to per-version file for artifact + merge job
|
|
41
|
+
mv .coverage .coverage.${{ matrix.python-version }}
|
|
42
|
+
ls -la .coverage.${{ matrix.python-version }}
|
|
43
|
+
|
|
44
|
+
- name: Upload .coverage artifact
|
|
45
|
+
uses: actions/upload-artifact@v4
|
|
46
|
+
with:
|
|
47
|
+
name: coverage-${{ matrix.python-version }}
|
|
48
|
+
path: .coverage.${{ matrix.python-version }}
|
|
49
|
+
include-hidden-files: true
|
|
50
|
+
if-no-files-found: error
|
|
51
|
+
|
|
52
|
+
coverage-merge:
|
|
53
|
+
name: Merge coverage
|
|
54
|
+
runs-on: ubuntu-latest
|
|
55
|
+
needs: tests
|
|
56
|
+
steps:
|
|
57
|
+
- name: Checkout (for repo context)
|
|
58
|
+
uses: actions/checkout@v4
|
|
59
|
+
|
|
60
|
+
- name: Download all coverage artifacts
|
|
61
|
+
uses: actions/download-artifact@v4
|
|
62
|
+
with:
|
|
63
|
+
path: coverage-reports
|
|
64
|
+
|
|
65
|
+
- name: Combine coverage and generate reports
|
|
66
|
+
shell: bash
|
|
67
|
+
run: |
|
|
68
|
+
set -euxo pipefail
|
|
69
|
+
python -m pip install coverage
|
|
70
|
+
files=$(find coverage-reports -type f -name ".coverage.*" -print)
|
|
71
|
+
if [ -z "$files" ]; then
|
|
72
|
+
echo "No coverage data files found"; exit 1
|
|
73
|
+
fi
|
|
74
|
+
coverage combine $files
|
|
75
|
+
coverage report -m --rcfile=.coveragerc
|
|
76
|
+
coverage xml -o coverage.xml --rcfile=.coveragerc
|
|
77
|
+
coverage html -d htmlcov --rcfile=.coveragerc
|
|
78
|
+
|
|
79
|
+
- name: Upload combined coverage XML
|
|
80
|
+
uses: actions/upload-artifact@v4
|
|
81
|
+
with:
|
|
82
|
+
name: coverage-xml
|
|
83
|
+
path: coverage.xml
|
|
84
|
+
if-no-files-found: error
|
|
85
|
+
|
|
86
|
+
- name: Upload HTML coverage report
|
|
87
|
+
uses: actions/upload-artifact@v4
|
|
88
|
+
with:
|
|
89
|
+
name: coverage-html
|
|
90
|
+
path: htmlcov
|
|
91
|
+
if-no-files-found: error
|
|
92
|
+
|
|
93
|
+
- name: Upload to Codecov
|
|
94
|
+
if: always() && !cancelled()
|
|
95
|
+
uses: codecov/codecov-action@v5
|
|
96
|
+
continue-on-error: true
|
|
97
|
+
with:
|
|
98
|
+
files: coverage.xml
|
|
99
|
+
fail_ci_if_error: false
|
|
100
|
+
token: ${{ secrets.CODECOV_TOKEN }}
|
|
101
|
+
slug: dperezcabrera/pico-agent
|
|
102
|
+
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
name: Publish Python Package to PyPI
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
release:
|
|
5
|
+
types: [published]
|
|
6
|
+
|
|
7
|
+
jobs:
|
|
8
|
+
deploy:
|
|
9
|
+
runs-on: ubuntu-latest
|
|
10
|
+
permissions:
|
|
11
|
+
id-token: write
|
|
12
|
+
contents: read
|
|
13
|
+
|
|
14
|
+
steps:
|
|
15
|
+
- uses: actions/checkout@v4
|
|
16
|
+
with:
|
|
17
|
+
fetch-depth: 0
|
|
18
|
+
|
|
19
|
+
- name: Set up Python
|
|
20
|
+
uses: actions/setup-python@v5
|
|
21
|
+
with:
|
|
22
|
+
python-version: '3.x'
|
|
23
|
+
|
|
24
|
+
- name: Install build deps
|
|
25
|
+
run: python -m pip install --upgrade pip build
|
|
26
|
+
|
|
27
|
+
- name: Build package
|
|
28
|
+
run: python -m build
|
|
29
|
+
|
|
30
|
+
- name: Publish to PyPI
|
|
31
|
+
uses: pypa/gh-action-pypi-publish@release/v1
|
|
32
|
+
with:
|
|
33
|
+
skip-existing: true
|
|
34
|
+
verify-metadata: true
|
|
35
|
+
attestations: false
|
|
36
|
+
|
pico_agent-0.1.0/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 David Pérez Cabrera
|
|
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,18 @@
|
|
|
1
|
+
include README.md
|
|
2
|
+
include LICENSE
|
|
3
|
+
|
|
4
|
+
recursive-include src *.py
|
|
5
|
+
|
|
6
|
+
recursive-include tests *.py
|
|
7
|
+
|
|
8
|
+
exclude .gitignore
|
|
9
|
+
exclude Dockerfile*
|
|
10
|
+
exclude docker-compose*.yml
|
|
11
|
+
exclude Makefile
|
|
12
|
+
|
|
13
|
+
prune build
|
|
14
|
+
prune dist
|
|
15
|
+
prune .tox
|
|
16
|
+
prune .pytest_cache
|
|
17
|
+
prune __pycache__
|
|
18
|
+
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: pico-agent
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: A pico agent framework with LangChain integration.
|
|
5
|
+
Author-email: David Perez Cabrera <dperezcabrera@gmail.com>
|
|
6
|
+
License: MIT License
|
|
7
|
+
|
|
8
|
+
Copyright (c) 2025 David Pérez Cabrera
|
|
9
|
+
|
|
10
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
11
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
12
|
+
in the Software without restriction, including without limitation the rights
|
|
13
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
14
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
15
|
+
furnished to do so, subject to the following conditions:
|
|
16
|
+
|
|
17
|
+
The above copyright notice and this permission notice shall be included in all
|
|
18
|
+
copies or substantial portions of the Software.
|
|
19
|
+
|
|
20
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
21
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
22
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
23
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
24
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
25
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
26
|
+
SOFTWARE.
|
|
27
|
+
|
|
28
|
+
Project-URL: Homepage, https://github.com/dperezcabrera/pico-agent
|
|
29
|
+
Project-URL: Repository, https://github.com/dperezcabrera/pico-agent
|
|
30
|
+
Project-URL: Issue Tracker, https://github.com/dperezcabrera/pico-agent/issues
|
|
31
|
+
Keywords: ioc,di,dependency injection,agent,llm,langchain,ai
|
|
32
|
+
Classifier: Development Status :: 4 - Beta
|
|
33
|
+
Classifier: Framework :: AsyncIO
|
|
34
|
+
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
35
|
+
Classifier: Programming Language :: Python :: 3
|
|
36
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
37
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
38
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
39
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
40
|
+
Classifier: Operating System :: OS Independent
|
|
41
|
+
Requires-Python: >=3.10
|
|
42
|
+
Description-Content-Type: text/markdown
|
|
43
|
+
License-File: LICENSE
|
|
44
|
+
Requires-Dist: pico-ioc>=2.2.0
|
|
45
|
+
Requires-Dist: pydantic>=2.0
|
|
46
|
+
Requires-Dist: langchain>=1.0.2
|
|
47
|
+
Requires-Dist: langchain-core
|
|
48
|
+
Requires-Dist: langchain-community
|
|
49
|
+
Requires-Dist: langchain-model-profiles
|
|
50
|
+
Provides-Extra: openai
|
|
51
|
+
Requires-Dist: langchain-openai; extra == "openai"
|
|
52
|
+
Provides-Extra: google
|
|
53
|
+
Requires-Dist: langchain-google-genai; extra == "google"
|
|
54
|
+
Provides-Extra: anthropic
|
|
55
|
+
Requires-Dist: langchain-anthropic; extra == "anthropic"
|
|
56
|
+
Provides-Extra: community
|
|
57
|
+
Requires-Dist: langchain-community; extra == "community"
|
|
58
|
+
Provides-Extra: all
|
|
59
|
+
Requires-Dist: langchain-openai; extra == "all"
|
|
60
|
+
Requires-Dist: langchain-google-genai; extra == "all"
|
|
61
|
+
Requires-Dist: langchain-anthropic; extra == "all"
|
|
62
|
+
Requires-Dist: langchain-community; extra == "all"
|
|
63
|
+
Dynamic: license-file
|
|
@@ -0,0 +1,228 @@
|
|
|
1
|
+
# 📦 pico-agent
|
|
2
|
+
|
|
3
|
+
[](https://pypi.org/project/pico-agent/)
|
|
4
|
+
[](https://deepwiki.com/dperezcabrera/pico-agent)
|
|
5
|
+
[](https://opensource.org/licenses/MIT)
|
|
6
|
+

|
|
7
|
+
[](https://codecov.io/gh/dperezcabrera/pico-agent)
|
|
8
|
+
[](https://sonarcloud.io/summary/new_code?id=dperezcabrera_pico-agent)
|
|
9
|
+
[](https://sonarcloud.io/summary/new_code?id=dperezcabrera_pico-agent)
|
|
10
|
+
[](https://sonarcloud.io/summary/new_code?id=dperezcabrera_pico-agent)
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
# Pico-Agent
|
|
14
|
+
|
|
15
|
+
**[Pico-Agent](https://github.com/dperezcabrera/pico-agent)** is a lightweight, protocol-based framework for building modular AI agents using dependency injection.
|
|
16
|
+
|
|
17
|
+
It eliminates the boilerplate of manually managing LLM chains, prompts, and tools. Instead, you declare **what** you want the agent to do using Python Protocols, and the framework handles **how** to execute it via **[Pico-IoC](https://github.com/dperezcabrera/pico-ioc)**.
|
|
18
|
+
|
|
19
|
+
> 🤖 **Protocol-First Design**
|
|
20
|
+
> 💉 **True Dependency Injection**
|
|
21
|
+
> 🧠 **Smart Model Routing**
|
|
22
|
+
> 🔄 **Multi-Agent Orchestration**
|
|
23
|
+
> 🔌 **LLM Agnostic (LangChain based)**
|
|
24
|
+
|
|
25
|
+
-----
|
|
26
|
+
|
|
27
|
+
## 🎯 Why pico-agent?
|
|
28
|
+
|
|
29
|
+
Most agent frameworks require you to instantiate classes, manually bind tools, and hardcode model names. Pico-Agent moves this complexity into the IoC container, promoting clean architecture and testability.
|
|
30
|
+
|
|
31
|
+
| Feature | Standard Approach | Pico-Agent |
|
|
32
|
+
|----------|-------------------|------------|
|
|
33
|
+
| **Definition** | Concrete Classes / Functions | Python Protocols (Interfaces) |
|
|
34
|
+
| **Discovery** | Manual Instantiation | Auto-scanning via IoC |
|
|
35
|
+
| **Tools** | Manual Binding | DI Injection & Name Reference |
|
|
36
|
+
| **Configuration** | Global Env Vars | Injected Configuration Components |
|
|
37
|
+
| **Orchestration** | Complex Chains | Agents using Agents as Tools |
|
|
38
|
+
|
|
39
|
+
-----
|
|
40
|
+
|
|
41
|
+
## 🧱 Core Features
|
|
42
|
+
|
|
43
|
+
- **Declarative Agents:** Use `@agent` on standard `typing.Protocol`.
|
|
44
|
+
- **Capability Routing:** Request `FAST`, `SMART`, `CODING` capabilities instead of specific model names.
|
|
45
|
+
- **Auto-Discovery:** Leverages `pico-ioc` 2.2+ custom scanners to find agents automatically.
|
|
46
|
+
- **Type-Safe:** Full support for Pydantic input/output schemas.
|
|
47
|
+
- **ReAct Loops:** Built-in support for reasoning loops and tool usage.
|
|
48
|
+
|
|
49
|
+
-----
|
|
50
|
+
|
|
51
|
+
## 📦 Installation
|
|
52
|
+
|
|
53
|
+
```bash
|
|
54
|
+
pip install pico-agent
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
Install with specific provider support:
|
|
58
|
+
|
|
59
|
+
```bash
|
|
60
|
+
pip install "pico-agent[openai,google]"
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
-----
|
|
64
|
+
|
|
65
|
+
## 🚀 Quick Example (with pico-stack)
|
|
66
|
+
|
|
67
|
+
The best way to use `pico-agent` is with `pico-stack`, which handles auto-discovery out of the box.
|
|
68
|
+
|
|
69
|
+
### 1\. Define the Agent Protocol
|
|
70
|
+
|
|
71
|
+
The docstring becomes the system prompt. The method signature defines the tool input.
|
|
72
|
+
|
|
73
|
+
```python
|
|
74
|
+
from typing import Protocol
|
|
75
|
+
from pico_agent import agent, AgentCapability, AgentType
|
|
76
|
+
|
|
77
|
+
@agent(
|
|
78
|
+
name="translator",
|
|
79
|
+
capability=AgentCapability.FAST, # Uses a faster/cheaper model
|
|
80
|
+
system_prompt="You are a professional translator.",
|
|
81
|
+
user_prompt_template="Translate this to Spanish: {text}",
|
|
82
|
+
agent_type=AgentType.ONE_SHOT
|
|
83
|
+
)
|
|
84
|
+
class TranslatorAgent(Protocol):
|
|
85
|
+
def translate(self, text: str) -> str: ...
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
### 2\. Configure Credentials
|
|
89
|
+
|
|
90
|
+
Inject the centralized `LLMConfig` and set your keys. This avoids hardcoded environment variable lookups inside the library.
|
|
91
|
+
|
|
92
|
+
```python
|
|
93
|
+
import os
|
|
94
|
+
from pico_ioc import component, configure
|
|
95
|
+
from pico_agent import LLMConfig
|
|
96
|
+
|
|
97
|
+
@component
|
|
98
|
+
class AppConfig:
|
|
99
|
+
@configure
|
|
100
|
+
def setup_llm(self, config: LLMConfig):
|
|
101
|
+
# Load from Env, Vault, AWS Secrets, etc.
|
|
102
|
+
config.api_keys["openai"] = os.getenv("OPENAI_API_KEY")
|
|
103
|
+
config.api_keys["google"] = os.getenv("GOOGLE_API_KEY")
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
### 3\. Run the Application
|
|
107
|
+
|
|
108
|
+
```python
|
|
109
|
+
import src
|
|
110
|
+
from pico_stack import init
|
|
111
|
+
from app.agents import TranslatorAgent
|
|
112
|
+
|
|
113
|
+
def main():
|
|
114
|
+
# Scans modules, finds agents, wires dependencies
|
|
115
|
+
container = init(src)
|
|
116
|
+
|
|
117
|
+
# Get your agent instance (auto-generated proxy)
|
|
118
|
+
translator = container.get(TranslatorAgent)
|
|
119
|
+
|
|
120
|
+
result = translator.translate(text="Dependency Injection is cool")
|
|
121
|
+
print(result) # "La inyección de dependencias es genial"
|
|
122
|
+
|
|
123
|
+
if __name__ == "__main__":
|
|
124
|
+
main()
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
-----
|
|
128
|
+
|
|
129
|
+
## 🛠 Advanced Usage
|
|
130
|
+
|
|
131
|
+
### ReAct Agents & Tools
|
|
132
|
+
|
|
133
|
+
Agents can use any dependency in the container as a tool.
|
|
134
|
+
|
|
135
|
+
```python
|
|
136
|
+
# 1. Define a tool (standard component)
|
|
137
|
+
from pico_ioc import component
|
|
138
|
+
|
|
139
|
+
@component
|
|
140
|
+
class Calculator:
|
|
141
|
+
def add(self, a: int, b: int) -> int:
|
|
142
|
+
"""Adds two numbers."""
|
|
143
|
+
return a + b
|
|
144
|
+
|
|
145
|
+
# 2. Define the Agent
|
|
146
|
+
@agent(
|
|
147
|
+
name="math_expert",
|
|
148
|
+
capability=AgentCapability.REASONING,
|
|
149
|
+
agent_type=AgentType.REACT, # Enables Reasoning Loop
|
|
150
|
+
tools=["calculator"], # Reference tool by name (snake_case of class)
|
|
151
|
+
max_iterations=5
|
|
152
|
+
)
|
|
153
|
+
class MathAgent(Protocol):
|
|
154
|
+
def solve(self, problem: str) -> str: ...
|
|
155
|
+
```
|
|
156
|
+
|
|
157
|
+
### Multi-Agent Orchestration
|
|
158
|
+
|
|
159
|
+
An agent can use other agents as tools simply by referencing them.
|
|
160
|
+
|
|
161
|
+
```python
|
|
162
|
+
@agent(
|
|
163
|
+
name="orchestrator",
|
|
164
|
+
capability=AgentCapability.SMART,
|
|
165
|
+
system_prompt="Coordinate the sub-agents to solve the task.",
|
|
166
|
+
# Inject other agents as tools available to this LLM
|
|
167
|
+
agents=["translator", "math_expert"]
|
|
168
|
+
)
|
|
169
|
+
class Orchestrator(Protocol):
|
|
170
|
+
def handle_request(self, task: str) -> str: ...
|
|
171
|
+
```
|
|
172
|
+
|
|
173
|
+
-----
|
|
174
|
+
|
|
175
|
+
## ⚙️ Model Routing & Configuration
|
|
176
|
+
|
|
177
|
+
`pico-agent` uses semantic capabilities to route to specific models. You can configure this globally using a configurer.
|
|
178
|
+
|
|
179
|
+
```python
|
|
180
|
+
from pico_ioc import component, configure
|
|
181
|
+
from pico_agent.router import ModelRouter, AgentCapability
|
|
182
|
+
|
|
183
|
+
@component
|
|
184
|
+
class RouterConfig:
|
|
185
|
+
@configure
|
|
186
|
+
def configure_router(self, router: ModelRouter):
|
|
187
|
+
# Update default mappings
|
|
188
|
+
router.update_mapping(AgentCapability.FAST, "openai:gpt-5-mini")
|
|
189
|
+
router.update_mapping(AgentCapability.SMART, "anthropic:claude-4-5-sonnet")
|
|
190
|
+
|
|
191
|
+
# You can also use "provider:model" syntax
|
|
192
|
+
router.update_mapping(AgentCapability.CODING, "deepseek:deepseek-coder")
|
|
193
|
+
```
|
|
194
|
+
|
|
195
|
+
-----
|
|
196
|
+
|
|
197
|
+
## 🧪 Testing
|
|
198
|
+
|
|
199
|
+
Testing is simple because you can mock the underlying `LLMFactory` or the agent protocol itself.
|
|
200
|
+
|
|
201
|
+
```python
|
|
202
|
+
from unittest.mock import MagicMock
|
|
203
|
+
from pico_ioc import init
|
|
204
|
+
from pico_agent import LLMFactory, LLM
|
|
205
|
+
|
|
206
|
+
def test_my_agent():
|
|
207
|
+
mock_llm = MagicMock(spec=LLM)
|
|
208
|
+
mock_llm.invoke.return_value = "Mocked Response"
|
|
209
|
+
|
|
210
|
+
mock_factory = MagicMock(spec=LLMFactory)
|
|
211
|
+
mock_factory.create.return_value = mock_llm
|
|
212
|
+
mock_factory.return_value = mock_factory # For singleton resolution
|
|
213
|
+
|
|
214
|
+
container = init(
|
|
215
|
+
modules=["pico_agent", "my_app"],
|
|
216
|
+
overrides={LLMFactory: mock_factory},
|
|
217
|
+
custom_scanners=[AgentScanner()] # If not using pico-stack
|
|
218
|
+
)
|
|
219
|
+
|
|
220
|
+
agent = container.get(MyAgent)
|
|
221
|
+
assert agent.run("test") == "Mocked Response"
|
|
222
|
+
```
|
|
223
|
+
|
|
224
|
+
-----
|
|
225
|
+
|
|
226
|
+
## 📝 License
|
|
227
|
+
|
|
228
|
+
This project is licensed under the MIT License.
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["setuptools>=69.0", "wheel", "setuptools-scm>=8.0"]
|
|
3
|
+
build-backend = "setuptools.build_meta"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "pico-agent"
|
|
7
|
+
dynamic = ["version"]
|
|
8
|
+
authors = [{ name = "David Perez Cabrera", email = "dperezcabrera@gmail.com" }]
|
|
9
|
+
description = "A pico agent framework with LangChain integration."
|
|
10
|
+
readme = { file = "pico.txt", content-type = "text/markdown" }
|
|
11
|
+
requires-python = ">=3.10"
|
|
12
|
+
license = { file = "LICENSE" }
|
|
13
|
+
keywords = [
|
|
14
|
+
"ioc",
|
|
15
|
+
"di",
|
|
16
|
+
"dependency injection",
|
|
17
|
+
"agent",
|
|
18
|
+
"llm",
|
|
19
|
+
"langchain",
|
|
20
|
+
"ai"
|
|
21
|
+
]
|
|
22
|
+
classifiers = [
|
|
23
|
+
"Development Status :: 4 - Beta",
|
|
24
|
+
"Framework :: AsyncIO",
|
|
25
|
+
"Topic :: Software Development :: Libraries :: Python Modules",
|
|
26
|
+
"Programming Language :: Python :: 3",
|
|
27
|
+
"Programming Language :: Python :: 3.10",
|
|
28
|
+
"Programming Language :: Python :: 3.11",
|
|
29
|
+
"Programming Language :: Python :: 3.12",
|
|
30
|
+
"License :: OSI Approved :: MIT License",
|
|
31
|
+
"Operating System :: OS Independent"
|
|
32
|
+
]
|
|
33
|
+
|
|
34
|
+
dependencies = [
|
|
35
|
+
"pico-ioc >= 2.2.0",
|
|
36
|
+
"pydantic>=2.0",
|
|
37
|
+
"langchain>=1.0.2",
|
|
38
|
+
"langchain-core",
|
|
39
|
+
"langchain-community",
|
|
40
|
+
"langchain-model-profiles",
|
|
41
|
+
]
|
|
42
|
+
|
|
43
|
+
[project.optional-dependencies]
|
|
44
|
+
openai = ["langchain-openai"]
|
|
45
|
+
google = ["langchain-google-genai"]
|
|
46
|
+
anthropic = ["langchain-anthropic"]
|
|
47
|
+
community = ["langchain-community"]
|
|
48
|
+
all = [
|
|
49
|
+
"langchain-openai",
|
|
50
|
+
"langchain-google-genai",
|
|
51
|
+
"langchain-anthropic",
|
|
52
|
+
"langchain-community",
|
|
53
|
+
]
|
|
54
|
+
|
|
55
|
+
[project.urls]
|
|
56
|
+
Homepage = "https://github.com/dperezcabrera/pico-agent"
|
|
57
|
+
Repository = "https://github.com/dperezcabrera/pico-agent"
|
|
58
|
+
"Issue Tracker" = "https://github.com/dperezcabrera/pico-agent/issues"
|
|
59
|
+
|
|
60
|
+
[tool.setuptools]
|
|
61
|
+
package-dir = {"" = "src"}
|
|
62
|
+
|
|
63
|
+
[tool.setuptools.packages.find]
|
|
64
|
+
where = ["src"]
|
|
65
|
+
include = ["pico_agent*"]
|
|
66
|
+
|
|
67
|
+
[tool.setuptools_scm]
|
|
68
|
+
version_scheme = "post-release"
|
|
69
|
+
local_scheme = "no-local-version"
|
|
70
|
+
fallback_version = "0.1.0"
|
|
71
|
+
write_to = "src/pico_agent/_version.py"
|
|
72
|
+
write_to_template = "__version__ = '{version}'\n"
|
|
73
|
+
|
|
74
|
+
[project.entry-points."pico_stack.modules"]
|
|
75
|
+
pico_agent = "pico_agent"
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
from .config import AgentConfig, AgentType, AgentCapability, LLMConfig
|
|
2
|
+
from .decorators import agent
|
|
3
|
+
from .registry import AgentConfigService, ToolRegistry
|
|
4
|
+
from .interfaces import CentralConfigClient, LLMFactory, LLM
|
|
5
|
+
from .scanner import AgentScanner
|
|
6
|
+
from .validation import AgentValidator, ValidationReport, ValidationIssue, Severity
|
|
7
|
+
from .exceptions import AgentError, AgentDisabledError, AgentConfigurationError
|
|
8
|
+
|
|
9
|
+
PICO_SCANNERS = [AgentScanner()]
|
|
10
|
+
|
|
11
|
+
__all__ = [
|
|
12
|
+
"AgentConfig",
|
|
13
|
+
"LLMConfig",
|
|
14
|
+
"AgentType",
|
|
15
|
+
"AgentCapability",
|
|
16
|
+
"agent",
|
|
17
|
+
"AgentConfigService",
|
|
18
|
+
"ToolRegistry",
|
|
19
|
+
"CentralConfigClient",
|
|
20
|
+
"LLMFactory",
|
|
21
|
+
"LLM",
|
|
22
|
+
"AgentScanner",
|
|
23
|
+
"PICO_SCANNERS",
|
|
24
|
+
"AgentValidator",
|
|
25
|
+
"ValidationReport",
|
|
26
|
+
"ValidationIssue",
|
|
27
|
+
"Severity",
|
|
28
|
+
"AgentError",
|
|
29
|
+
"AgentDisabledError",
|
|
30
|
+
"AgentConfigurationError"
|
|
31
|
+
]
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
__version__ = '0.1.0'
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
from dataclasses import dataclass, field
|
|
2
|
+
from typing import List, Optional, Dict
|
|
3
|
+
from enum import Enum
|
|
4
|
+
|
|
5
|
+
class AgentType(str, Enum):
|
|
6
|
+
ONE_SHOT = "one_shot"
|
|
7
|
+
REACT = "react"
|
|
8
|
+
|
|
9
|
+
class AgentCapability:
|
|
10
|
+
FAST = "fast"
|
|
11
|
+
SMART = "smart"
|
|
12
|
+
REASONING = "reasoning"
|
|
13
|
+
VISION = "vision"
|
|
14
|
+
CODING = "coding"
|
|
15
|
+
|
|
16
|
+
@dataclass
|
|
17
|
+
class AgentConfig:
|
|
18
|
+
name: str
|
|
19
|
+
system_prompt: str
|
|
20
|
+
user_prompt_template: str
|
|
21
|
+
capability: str = AgentCapability.SMART
|
|
22
|
+
enabled: bool = True
|
|
23
|
+
agent_type: AgentType = AgentType.ONE_SHOT
|
|
24
|
+
max_iterations: int = 5
|
|
25
|
+
tools: List[str] = field(default_factory=list)
|
|
26
|
+
agents: List[str] = field(default_factory=list)
|
|
27
|
+
tags: List[str] = field(default_factory=list)
|
|
28
|
+
tracing_enabled: bool = True
|
|
29
|
+
temperature: float = 0.7
|
|
30
|
+
max_tokens: Optional[int] = None
|
|
31
|
+
llm_profile: Optional[str] = None
|
|
32
|
+
|
|
33
|
+
@dataclass
|
|
34
|
+
class LLMConfig:
|
|
35
|
+
api_keys: Dict[str, str] = field(default_factory=dict)
|
|
36
|
+
base_urls: Dict[str, str] = field(default_factory=dict)
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
from typing import List, Optional, Callable, Type
|
|
2
|
+
from .config import AgentConfig, AgentType, AgentCapability
|
|
3
|
+
|
|
4
|
+
AGENT_META_KEY = "_pico_agent_meta"
|
|
5
|
+
IS_AGENT_INTERFACE = "_pico_is_agent_interface"
|
|
6
|
+
|
|
7
|
+
def agent(
|
|
8
|
+
name: str,
|
|
9
|
+
capability: str = AgentCapability.SMART,
|
|
10
|
+
system_prompt: str = "",
|
|
11
|
+
user_prompt_template: str = "{input}",
|
|
12
|
+
agent_type: AgentType = AgentType.ONE_SHOT,
|
|
13
|
+
max_iterations: int = 5,
|
|
14
|
+
tools: Optional[List[str]] = None,
|
|
15
|
+
agents: Optional[List[str]] = None,
|
|
16
|
+
tags: Optional[List[str]] = None,
|
|
17
|
+
tracing_enabled: bool = True,
|
|
18
|
+
temperature: float = 0.7,
|
|
19
|
+
llm_profile: Optional[str] = None
|
|
20
|
+
) -> Callable[[Type], Type]:
|
|
21
|
+
|
|
22
|
+
def decorator(cls_or_proto: Type) -> Type:
|
|
23
|
+
default_config = AgentConfig(
|
|
24
|
+
name=name,
|
|
25
|
+
capability=capability,
|
|
26
|
+
system_prompt=system_prompt,
|
|
27
|
+
user_prompt_template=user_prompt_template,
|
|
28
|
+
agent_type=agent_type,
|
|
29
|
+
max_iterations=max_iterations,
|
|
30
|
+
tools=tools or [],
|
|
31
|
+
agents=agents or [],
|
|
32
|
+
tags=tags or [],
|
|
33
|
+
tracing_enabled=tracing_enabled,
|
|
34
|
+
temperature=temperature,
|
|
35
|
+
llm_profile=llm_profile
|
|
36
|
+
)
|
|
37
|
+
|
|
38
|
+
setattr(cls_or_proto, AGENT_META_KEY, default_config)
|
|
39
|
+
setattr(cls_or_proto, IS_AGENT_INTERFACE, True)
|
|
40
|
+
return cls_or_proto
|
|
41
|
+
|
|
42
|
+
return decorator
|