yoaiagent 0.2.2__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.
- yoaiagent-0.2.2/.gitignore +10 -0
- yoaiagent-0.2.2/.python-version +1 -0
- yoaiagent-0.2.2/LICENSE +21 -0
- yoaiagent-0.2.2/PKG-INFO +447 -0
- yoaiagent-0.2.2/README.md +412 -0
- yoaiagent-0.2.2/asas +1 -0
- yoaiagent-0.2.2/docs/agents.md +262 -0
- yoaiagent-0.2.2/docs/api.md +592 -0
- yoaiagent-0.2.2/docs/cli.md +297 -0
- yoaiagent-0.2.2/docs/configuration.md +226 -0
- yoaiagent-0.2.2/docs/getting-started.md +210 -0
- yoaiagent-0.2.2/docs/index.md +181 -0
- yoaiagent-0.2.2/docs/memory.md +193 -0
- yoaiagent-0.2.2/docs/middleware.md +385 -0
- yoaiagent-0.2.2/docs/providers.md +337 -0
- yoaiagent-0.2.2/docs/streaming.md +233 -0
- yoaiagent-0.2.2/docs/tools.md +276 -0
- yoaiagent-0.2.2/docs/workflows.md +202 -0
- yoaiagent-0.2.2/examples/async_agent.py +24 -0
- yoaiagent-0.2.2/examples/basic.py +19 -0
- yoaiagent-0.2.2/examples/custom_endpoint.py +45 -0
- yoaiagent-0.2.2/examples/custom_provider.py +69 -0
- yoaiagent-0.2.2/examples/multi_agent.py +38 -0
- yoaiagent-0.2.2/examples/ollama.py +26 -0
- yoaiagent-0.2.2/examples/openrouter.py +15 -0
- yoaiagent-0.2.2/examples/streaming.py +24 -0
- yoaiagent-0.2.2/examples/structured_output.py +32 -0
- yoaiagent-0.2.2/examples/tools.py +32 -0
- yoaiagent-0.2.2/examples/workflow.py +38 -0
- yoaiagent-0.2.2/pyproject.toml +82 -0
- yoaiagent-0.2.2/src/yoaiagent/__init__.py +77 -0
- yoaiagent-0.2.2/src/yoaiagent/agent/__init__.py +5 -0
- yoaiagent-0.2.2/src/yoaiagent/agent/agent.py +401 -0
- yoaiagent-0.2.2/src/yoaiagent/agent/context.py +25 -0
- yoaiagent-0.2.2/src/yoaiagent/agent/lifecycle.py +31 -0
- yoaiagent-0.2.2/src/yoaiagent/agent/runner.py +21 -0
- yoaiagent-0.2.2/src/yoaiagent/builtin_tools/__init__.py +40 -0
- yoaiagent-0.2.2/src/yoaiagent/builtin_tools/coder.py +196 -0
- yoaiagent-0.2.2/src/yoaiagent/builtin_tools/context.py +35 -0
- yoaiagent-0.2.2/src/yoaiagent/builtin_tools/web.py +49 -0
- yoaiagent-0.2.2/src/yoaiagent/cli/__init__.py +98 -0
- yoaiagent-0.2.2/src/yoaiagent/config/__init__.py +5 -0
- yoaiagent-0.2.2/src/yoaiagent/config/settings.py +138 -0
- yoaiagent-0.2.2/src/yoaiagent/exceptions/__init__.py +117 -0
- yoaiagent-0.2.2/src/yoaiagent/memory/__init__.py +7 -0
- yoaiagent-0.2.2/src/yoaiagent/memory/base.py +35 -0
- yoaiagent-0.2.2/src/yoaiagent/memory/in_memory.py +39 -0
- yoaiagent-0.2.2/src/yoaiagent/memory/sqlite.py +144 -0
- yoaiagent-0.2.2/src/yoaiagent/middleware/__init__.py +78 -0
- yoaiagent-0.2.2/src/yoaiagent/middleware/budget.py +139 -0
- yoaiagent-0.2.2/src/yoaiagent/middleware/circuit_breaker.py +115 -0
- yoaiagent-0.2.2/src/yoaiagent/middleware/logging.py +121 -0
- yoaiagent-0.2.2/src/yoaiagent/middleware/otel.py +101 -0
- yoaiagent-0.2.2/src/yoaiagent/middleware/sandbox.py +48 -0
- yoaiagent-0.2.2/src/yoaiagent/models/__init__.py +29 -0
- yoaiagent-0.2.2/src/yoaiagent/models/base.py +84 -0
- yoaiagent-0.2.2/src/yoaiagent/models/config.py +172 -0
- yoaiagent-0.2.2/src/yoaiagent/models/messages.py +73 -0
- yoaiagent-0.2.2/src/yoaiagent/models/registry.py +5 -0
- yoaiagent-0.2.2/src/yoaiagent/models/response.py +79 -0
- yoaiagent-0.2.2/src/yoaiagent/observability/__init__.py +5 -0
- yoaiagent-0.2.2/src/yoaiagent/observability/events.py +70 -0
- yoaiagent-0.2.2/src/yoaiagent/observability/tracing.py +27 -0
- yoaiagent-0.2.2/src/yoaiagent/providers/__init__.py +5 -0
- yoaiagent-0.2.2/src/yoaiagent/providers/anthropic_provider.py +196 -0
- yoaiagent-0.2.2/src/yoaiagent/providers/base.py +5 -0
- yoaiagent-0.2.2/src/yoaiagent/providers/gemini_provider.py +150 -0
- yoaiagent-0.2.2/src/yoaiagent/providers/openai_compatible.py +530 -0
- yoaiagent-0.2.2/src/yoaiagent/providers/openai_provider.py +193 -0
- yoaiagent-0.2.2/src/yoaiagent/providers/registry.py +68 -0
- yoaiagent-0.2.2/src/yoaiagent/py.typed +0 -0
- yoaiagent-0.2.2/src/yoaiagent/streaming/__init__.py +5 -0
- yoaiagent-0.2.2/src/yoaiagent/streaming/events.py +30 -0
- yoaiagent-0.2.2/src/yoaiagent/structured/__init__.py +5 -0
- yoaiagent-0.2.2/src/yoaiagent/structured/parser.py +37 -0
- yoaiagent-0.2.2/src/yoaiagent/tools/__init__.py +6 -0
- yoaiagent-0.2.2/src/yoaiagent/tools/base.py +99 -0
- yoaiagent-0.2.2/src/yoaiagent/tools/decorator.py +5 -0
- yoaiagent-0.2.2/src/yoaiagent/tools/execution.py +22 -0
- yoaiagent-0.2.2/src/yoaiagent/tools/registry.py +22 -0
- yoaiagent-0.2.2/src/yoaiagent/tools/schema.py +133 -0
- yoaiagent-0.2.2/src/yoaiagent/utils/__init__.py +1 -0
- yoaiagent-0.2.2/src/yoaiagent/utils/logging.py +20 -0
- yoaiagent-0.2.2/src/yoaiagent/utils/retry.py +35 -0
- yoaiagent-0.2.2/src/yoaiagent/workflows/__init__.py +5 -0
- yoaiagent-0.2.2/src/yoaiagent/workflows/graph.py +104 -0
- yoaiagent-0.2.2/tests/__init__.py +1 -0
- yoaiagent-0.2.2/tests/conftest.py +129 -0
- yoaiagent-0.2.2/tests/test_agent.py +180 -0
- yoaiagent-0.2.2/tests/test_builtin_tools.py +138 -0
- yoaiagent-0.2.2/tests/test_errors.py +69 -0
- yoaiagent-0.2.2/tests/test_memory.py +44 -0
- yoaiagent-0.2.2/tests/test_models.py +145 -0
- yoaiagent-0.2.2/tests/test_observability.py +47 -0
- yoaiagent-0.2.2/tests/test_production.py +503 -0
- yoaiagent-0.2.2/tests/test_providers.py +157 -0
- yoaiagent-0.2.2/tests/test_real_endpoint.py +139 -0
- yoaiagent-0.2.2/tests/test_streaming.py +53 -0
- yoaiagent-0.2.2/tests/test_tools.py +143 -0
- yoaiagent-0.2.2/uv.lock +1647 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
3.12
|
yoaiagent-0.2.2/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Shekh Saheb Ali
|
|
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.
|
yoaiagent-0.2.2/PKG-INFO
ADDED
|
@@ -0,0 +1,447 @@
|
|
|
1
|
+
Metadata-Version: 2.5
|
|
2
|
+
Name: yoaiagent
|
|
3
|
+
Version: 0.2.2
|
|
4
|
+
Summary: Provider-agnostic Python AI agent framework with Bring Your Own Provider architecture
|
|
5
|
+
Author-email: Shekh Saheb Ali <sahebali9277071@gmail.com>
|
|
6
|
+
License-Expression: MIT
|
|
7
|
+
License-File: LICENSE
|
|
8
|
+
Requires-Python: >=3.12
|
|
9
|
+
Requires-Dist: httpx<1.0,>=0.27
|
|
10
|
+
Requires-Dist: pydantic<3.0,>=2.0
|
|
11
|
+
Provides-Extra: all
|
|
12
|
+
Requires-Dist: anthropic<1.0,>=0.30; extra == 'all'
|
|
13
|
+
Requires-Dist: google-genai<2.0,>=1.0; extra == 'all'
|
|
14
|
+
Requires-Dist: openai<2.0,>=1.0; extra == 'all'
|
|
15
|
+
Provides-Extra: anthropic
|
|
16
|
+
Requires-Dist: anthropic<1.0,>=0.30; extra == 'anthropic'
|
|
17
|
+
Provides-Extra: cli
|
|
18
|
+
Requires-Dist: rich>=13.0; extra == 'cli'
|
|
19
|
+
Provides-Extra: config
|
|
20
|
+
Requires-Dist: python-dotenv>=1.0; extra == 'config'
|
|
21
|
+
Requires-Dist: pyyaml>=6.0; extra == 'config'
|
|
22
|
+
Provides-Extra: dev
|
|
23
|
+
Requires-Dist: mypy>=1.11; extra == 'dev'
|
|
24
|
+
Requires-Dist: pytest-asyncio>=0.24; extra == 'dev'
|
|
25
|
+
Requires-Dist: pytest-cov>=5.0; extra == 'dev'
|
|
26
|
+
Requires-Dist: pytest>=8.0; extra == 'dev'
|
|
27
|
+
Requires-Dist: ruff>=0.6; extra == 'dev'
|
|
28
|
+
Provides-Extra: gemini
|
|
29
|
+
Requires-Dist: google-genai<2.0,>=1.0; extra == 'gemini'
|
|
30
|
+
Provides-Extra: openai
|
|
31
|
+
Requires-Dist: openai<2.0,>=1.0; extra == 'openai'
|
|
32
|
+
Provides-Extra: otel
|
|
33
|
+
Requires-Dist: opentelemetry-api>=1.0; extra == 'otel'
|
|
34
|
+
Description-Content-Type: text/markdown
|
|
35
|
+
|
|
36
|
+
# YoAI Agent
|
|
37
|
+
|
|
38
|
+
**Provider-agnostic Python AI agent framework with Bring Your Own Provider architecture.**
|
|
39
|
+
|
|
40
|
+
Write your agent code once, run it against OpenAI, Anthropic, Gemini, Ollama, OpenRouter, vLLM, or any OpenAI-compatible endpoint — without changing a line of agent code.
|
|
41
|
+
|
|
42
|
+
## Installation
|
|
43
|
+
|
|
44
|
+
```bash
|
|
45
|
+
pip install yoaiagent
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
With provider SDKs:
|
|
49
|
+
|
|
50
|
+
```bash
|
|
51
|
+
pip install yoaiagent[openai] # Native OpenAI
|
|
52
|
+
pip install yoaiagent[anthropic] # Native Anthropic
|
|
53
|
+
pip install yoaiagent[gemini] # Native Google Gemini
|
|
54
|
+
pip install yoaiagent[all] # All providers
|
|
55
|
+
pip install yoaiagent[config] # YAML/TOML config + .env loading
|
|
56
|
+
pip install yoaiagent[otel] # OpenTelemetry tracing
|
|
57
|
+
pip install yoaiagent[cli] # CLI with rich output
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
## Quickstart
|
|
61
|
+
|
|
62
|
+
```python
|
|
63
|
+
from yoaiagent import Agent, LLM
|
|
64
|
+
|
|
65
|
+
llm = LLM(
|
|
66
|
+
provider="openai-compatible",
|
|
67
|
+
base_url="http://localhost:11434/v1",
|
|
68
|
+
api_key="ollama",
|
|
69
|
+
model="llama3.2",
|
|
70
|
+
)
|
|
71
|
+
|
|
72
|
+
agent = Agent(
|
|
73
|
+
model=llm,
|
|
74
|
+
instructions="You are a helpful AI assistant.",
|
|
75
|
+
)
|
|
76
|
+
|
|
77
|
+
result = agent.run("Explain quantum computing simply.")
|
|
78
|
+
print(result.output)
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
## Built-in Tools
|
|
82
|
+
|
|
83
|
+
10 ready-to-use tools included — no extra install needed:
|
|
84
|
+
|
|
85
|
+
```python
|
|
86
|
+
from yoaiagent import Agent, LLM, ALL_TOOLS
|
|
87
|
+
|
|
88
|
+
llm = LLM(provider="openai-compatible", base_url="http://localhost:11434/v1", api_key="ollama", model="llama3.2")
|
|
89
|
+
|
|
90
|
+
agent = Agent(
|
|
91
|
+
model=llm,
|
|
92
|
+
instructions="You are a coding assistant.",
|
|
93
|
+
tools=ALL_TOOLS,
|
|
94
|
+
)
|
|
95
|
+
|
|
96
|
+
result = agent.run("Read the file config.json and summarize it")
|
|
97
|
+
print(result.output)
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
| Tool | Description |
|
|
101
|
+
|------|-------------|
|
|
102
|
+
| `read_file` | Read a file's contents with line numbers |
|
|
103
|
+
| `write_file` | Create or overwrite a file |
|
|
104
|
+
| `edit_file` | Replace text in a file |
|
|
105
|
+
| `list_files` | List files in a directory |
|
|
106
|
+
| `search_files` | Search for text inside files (grep) |
|
|
107
|
+
| `shell` | Execute a shell command ⚠️ dangerous |
|
|
108
|
+
| `get_repo_context` | Get git repo overview |
|
|
109
|
+
| `web_fetch` | Fetch and extract text from a URL |
|
|
110
|
+
| `context_summary` | Summarize long text |
|
|
111
|
+
| `plan` | Create numbered task plans |
|
|
112
|
+
|
|
113
|
+
Select specific tools:
|
|
114
|
+
|
|
115
|
+
```python
|
|
116
|
+
from yoaiagent.builtin_tools.coder import read_file, write_file, shell
|
|
117
|
+
from yoaiagent.builtin_tools.web import web_fetch
|
|
118
|
+
|
|
119
|
+
agent = Agent(model=llm, tools=[read_file, write_file, shell, web_fetch])
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
## Providers
|
|
123
|
+
|
|
124
|
+
### OpenAI
|
|
125
|
+
|
|
126
|
+
```python
|
|
127
|
+
llm = LLM(provider="openai", api_key="sk-...", model="gpt-5")
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
### Anthropic
|
|
131
|
+
|
|
132
|
+
```python
|
|
133
|
+
llm = LLM(provider="anthropic", api_key="sk-ant-...", model="claude-sonnet-4-20250514")
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
### Google Gemini
|
|
137
|
+
|
|
138
|
+
```python
|
|
139
|
+
llm = LLM(provider="gemini", api_key="AIza...", model="gemini-2.0-flash")
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
### OpenAI-Compatible (OpenRouter, Ollama, vLLM, etc.)
|
|
143
|
+
|
|
144
|
+
```python
|
|
145
|
+
# OpenRouter
|
|
146
|
+
llm = LLM(
|
|
147
|
+
provider="openai-compatible",
|
|
148
|
+
base_url="https://openrouter.ai/api/v1",
|
|
149
|
+
api_key="your-key",
|
|
150
|
+
model="meta-llama/llama-3.1-8b-instruct",
|
|
151
|
+
)
|
|
152
|
+
|
|
153
|
+
# Ollama (local)
|
|
154
|
+
llm = LLM(
|
|
155
|
+
provider="openai-compatible",
|
|
156
|
+
base_url="http://localhost:11434/v1",
|
|
157
|
+
api_key="ollama",
|
|
158
|
+
model="llama3.2",
|
|
159
|
+
)
|
|
160
|
+
|
|
161
|
+
# vLLM
|
|
162
|
+
llm = LLM(
|
|
163
|
+
provider="openai-compatible",
|
|
164
|
+
base_url="http://localhost:8080/v1",
|
|
165
|
+
api_key="token",
|
|
166
|
+
model="meta-llama/Llama-3.1-8B-Instruct",
|
|
167
|
+
)
|
|
168
|
+
|
|
169
|
+
# Custom gateway with headers
|
|
170
|
+
llm = LLM(
|
|
171
|
+
provider="openai-compatible",
|
|
172
|
+
base_url="https://ai.company.internal/v1",
|
|
173
|
+
api_key="key",
|
|
174
|
+
model="internal-model",
|
|
175
|
+
headers={"X-Tenant-ID": "acme-corp"},
|
|
176
|
+
)
|
|
177
|
+
```
|
|
178
|
+
|
|
179
|
+
### Environment Variables
|
|
180
|
+
|
|
181
|
+
```bash
|
|
182
|
+
export YOAI_PROVIDER=openai
|
|
183
|
+
export YOAI_API_KEY=sk-...
|
|
184
|
+
export YOAI_MODEL=gpt-5
|
|
185
|
+
export YOAI_BASE_URL=https://api.openai.com/v1
|
|
186
|
+
```
|
|
187
|
+
|
|
188
|
+
```python
|
|
189
|
+
llm = LLM.from_env()
|
|
190
|
+
```
|
|
191
|
+
|
|
192
|
+
## Custom Tools
|
|
193
|
+
|
|
194
|
+
```python
|
|
195
|
+
from yoaiagent import Agent, LLM, tool
|
|
196
|
+
|
|
197
|
+
@tool
|
|
198
|
+
def calculator(a: float, b: float) -> float:
|
|
199
|
+
"""Add two numbers."""
|
|
200
|
+
return a + b
|
|
201
|
+
|
|
202
|
+
@tool
|
|
203
|
+
def get_weather(city: str) -> str:
|
|
204
|
+
"""Get current weather for a city."""
|
|
205
|
+
return f"Sunny, 25°C in {city}"
|
|
206
|
+
|
|
207
|
+
# Mark dangerous tools (requires user confirmation)
|
|
208
|
+
@tool(dangerous=True)
|
|
209
|
+
def shell(command: str) -> str:
|
|
210
|
+
"""Run a shell command."""
|
|
211
|
+
import subprocess
|
|
212
|
+
return subprocess.run(command, shell=True, capture_output=True, text=True).stdout
|
|
213
|
+
|
|
214
|
+
agent = Agent(
|
|
215
|
+
model=llm,
|
|
216
|
+
instructions="You are a helpful assistant.",
|
|
217
|
+
tools=[calculator, get_weather],
|
|
218
|
+
)
|
|
219
|
+
|
|
220
|
+
result = agent.run("What is 123 + 456?")
|
|
221
|
+
print(result.output)
|
|
222
|
+
```
|
|
223
|
+
|
|
224
|
+
## Middleware
|
|
225
|
+
|
|
226
|
+
Intercept agent lifecycle for logging, budgeting, and safety:
|
|
227
|
+
|
|
228
|
+
```python
|
|
229
|
+
from yoaiagent import (
|
|
230
|
+
Agent, LLM,
|
|
231
|
+
TokenBudgetMiddleware,
|
|
232
|
+
RateLimitMiddleware,
|
|
233
|
+
CircuitBreakerMiddleware,
|
|
234
|
+
ToolConfirmationMiddleware,
|
|
235
|
+
StructuredLoggingHook,
|
|
236
|
+
ConsoleLogger,
|
|
237
|
+
)
|
|
238
|
+
|
|
239
|
+
agent = Agent(
|
|
240
|
+
model=llm,
|
|
241
|
+
instructions="You are helpful.",
|
|
242
|
+
tools=ALL_TOOLS,
|
|
243
|
+
middleware=[
|
|
244
|
+
ConsoleLogger(), # Print tool calls
|
|
245
|
+
TokenBudgetMiddleware(max_total_tokens=50_000), # Cap token usage
|
|
246
|
+
RateLimitMiddleware(max_rpm=60), # Throttle requests
|
|
247
|
+
CircuitBreakerMiddleware(failure_threshold=3), # Stop on failures
|
|
248
|
+
ToolConfirmationMiddleware(auto_approve=["read_file", "list_files"]), # Confirm dangerous tools
|
|
249
|
+
],
|
|
250
|
+
)
|
|
251
|
+
```
|
|
252
|
+
|
|
253
|
+
### Available Middleware
|
|
254
|
+
|
|
255
|
+
| Middleware | Purpose |
|
|
256
|
+
|------------|---------|
|
|
257
|
+
| `ConsoleLogger` | Print tool calls to console |
|
|
258
|
+
| `TokenBudgetMiddleware` | Stop when token/cost limit exceeded |
|
|
259
|
+
| `RateLimitMiddleware` | Throttle to prevent rate limit hits |
|
|
260
|
+
| `CircuitBreakerMiddleware` | Stop calling LLM after repeated failures |
|
|
261
|
+
| `ToolConfirmationMiddleware` | Prompt before running dangerous tools |
|
|
262
|
+
| `StructuredLoggingHook` | JSON logs with correlation IDs |
|
|
263
|
+
| `OpenTelemetryMiddleware` | Export traces to Jaeger/Zipkin/Datadog |
|
|
264
|
+
|
|
265
|
+
## Streaming
|
|
266
|
+
|
|
267
|
+
```python
|
|
268
|
+
import asyncio
|
|
269
|
+
from yoaiagent import Agent, LLM
|
|
270
|
+
|
|
271
|
+
async def main():
|
|
272
|
+
llm = LLM(provider="openai-compatible", base_url="http://localhost:11434/v1", api_key="ollama", model="llama3.2")
|
|
273
|
+
agent = Agent(model=llm, instructions="You are a storyteller.")
|
|
274
|
+
|
|
275
|
+
async for event in agent.astream("Tell me a short story"):
|
|
276
|
+
if event.type == "text_delta":
|
|
277
|
+
print(event.delta, end="", flush=True)
|
|
278
|
+
elif event.type == "tool_call_started":
|
|
279
|
+
print(f"\n[Using {event.tool_name}]")
|
|
280
|
+
|
|
281
|
+
asyncio.run(main())
|
|
282
|
+
```
|
|
283
|
+
|
|
284
|
+
## Structured Output
|
|
285
|
+
|
|
286
|
+
```python
|
|
287
|
+
from pydantic import BaseModel
|
|
288
|
+
from yoaiagent import Agent, LLM
|
|
289
|
+
|
|
290
|
+
class UserInfo(BaseModel):
|
|
291
|
+
name: str
|
|
292
|
+
age: int
|
|
293
|
+
|
|
294
|
+
llm = LLM(provider="openai-compatible", base_url="http://localhost:11434/v1", api_key="ollama", model="llama3.2")
|
|
295
|
+
agent = Agent(model=llm, instructions="Extract info.")
|
|
296
|
+
|
|
297
|
+
result = agent.run("John is 30 years old.", response_model=UserInfo)
|
|
298
|
+
print(result.output.name) # "John"
|
|
299
|
+
print(result.output.age) # 30
|
|
300
|
+
```
|
|
301
|
+
|
|
302
|
+
## Memory
|
|
303
|
+
|
|
304
|
+
### In-Memory (Process Only)
|
|
305
|
+
|
|
306
|
+
```python
|
|
307
|
+
from yoaiagent import Agent, LLM, InMemory
|
|
308
|
+
|
|
309
|
+
llm = LLM(provider="openai-compatible", base_url="http://localhost:11434/v1", api_key="ollama", model="llama3.2")
|
|
310
|
+
memory = InMemory()
|
|
311
|
+
|
|
312
|
+
agent = Agent(model=llm, instructions="You are helpful.", memory=memory)
|
|
313
|
+
|
|
314
|
+
agent.run("My name is Alice.")
|
|
315
|
+
result = agent.run("What is my name?")
|
|
316
|
+
print(result.output) # "Your name is Alice."
|
|
317
|
+
```
|
|
318
|
+
|
|
319
|
+
### SQLite (Persistent)
|
|
320
|
+
|
|
321
|
+
```python
|
|
322
|
+
from yoaiagent import Agent, LLM, SQLiteMemory
|
|
323
|
+
|
|
324
|
+
memory = SQLiteMemory(db_path="~/.yoaiagent/memory.db")
|
|
325
|
+
agent = Agent(model=llm, memory=memory)
|
|
326
|
+
|
|
327
|
+
# Survives restarts
|
|
328
|
+
agent.run("My name is Alice.")
|
|
329
|
+
# ... restart your app ...
|
|
330
|
+
result = agent.run("What is my name?")
|
|
331
|
+
print(result.output) # "Your name is Alice."
|
|
332
|
+
```
|
|
333
|
+
|
|
334
|
+
## Multi-Agent
|
|
335
|
+
|
|
336
|
+
```python
|
|
337
|
+
from yoaiagent import Agent, LLM
|
|
338
|
+
|
|
339
|
+
researcher = Agent(name="researcher", model=llm, instructions="Research assistant.")
|
|
340
|
+
writer = Agent(name="writer", model=llm, instructions="Write articles.")
|
|
341
|
+
|
|
342
|
+
# Make researcher available as a tool
|
|
343
|
+
writer.add_tool(researcher.as_tool())
|
|
344
|
+
|
|
345
|
+
result = writer.run("Write about AI.")
|
|
346
|
+
```
|
|
347
|
+
|
|
348
|
+
## Workflows
|
|
349
|
+
|
|
350
|
+
```python
|
|
351
|
+
from yoaiagent import Agent, LLM, Workflow
|
|
352
|
+
|
|
353
|
+
researcher = Agent(name="researcher", model=llm, instructions="Gather facts.")
|
|
354
|
+
writer = Agent(name="writer", model=llm, instructions="Write content.")
|
|
355
|
+
reviewer = Agent(name="reviewer", model=llm, instructions="Review for quality.")
|
|
356
|
+
|
|
357
|
+
workflow = Workflow()
|
|
358
|
+
workflow.add_node("research", researcher)
|
|
359
|
+
workflow.add_node("write", writer)
|
|
360
|
+
workflow.add_node("review", reviewer)
|
|
361
|
+
|
|
362
|
+
workflow.connect("research", "write")
|
|
363
|
+
workflow.connect("write", "review")
|
|
364
|
+
|
|
365
|
+
results = workflow.run("History of the internet")
|
|
366
|
+
```
|
|
367
|
+
|
|
368
|
+
## Configuration
|
|
369
|
+
|
|
370
|
+
### Config File
|
|
371
|
+
|
|
372
|
+
Create `yoaiagent.yaml` in your project root:
|
|
373
|
+
|
|
374
|
+
```yaml
|
|
375
|
+
llm:
|
|
376
|
+
provider: openai-compatible
|
|
377
|
+
model: llama3
|
|
378
|
+
base_url: http://localhost:11434/v1
|
|
379
|
+
api_key: ollama
|
|
380
|
+
timeout: 60.0
|
|
381
|
+
max_retries: 3
|
|
382
|
+
```
|
|
383
|
+
|
|
384
|
+
Then load it:
|
|
385
|
+
|
|
386
|
+
```python
|
|
387
|
+
llm = LLM.from_env() # Reads config file + env vars
|
|
388
|
+
```
|
|
389
|
+
|
|
390
|
+
### Config Precedence
|
|
391
|
+
|
|
392
|
+
```
|
|
393
|
+
Direct code kwargs → Environment variables → Config file → .env → Defaults
|
|
394
|
+
```
|
|
395
|
+
|
|
396
|
+
## Custom Providers
|
|
397
|
+
|
|
398
|
+
```python
|
|
399
|
+
from yoaiagent import register_provider, BaseModel, ProviderCapabilities, LLMConfig, Message, RunResult
|
|
400
|
+
|
|
401
|
+
class MyProvider(BaseModel):
|
|
402
|
+
provider = "my-provider"
|
|
403
|
+
capabilities = ProviderCapabilities(supports_streaming=True)
|
|
404
|
+
|
|
405
|
+
def __init__(self, config: LLMConfig):
|
|
406
|
+
self.model = config.model
|
|
407
|
+
# Initialize your HTTP client or SDK here
|
|
408
|
+
|
|
409
|
+
async def generate(self, messages, **kwargs):
|
|
410
|
+
# Call your API
|
|
411
|
+
pass
|
|
412
|
+
|
|
413
|
+
async def stream(self, messages, **kwargs):
|
|
414
|
+
# Stream from your API
|
|
415
|
+
pass
|
|
416
|
+
|
|
417
|
+
register_provider("my-provider", MyProvider)
|
|
418
|
+
llm = LLM(provider="my-provider", model="my-model", api_key="key")
|
|
419
|
+
```
|
|
420
|
+
|
|
421
|
+
## CLI
|
|
422
|
+
|
|
423
|
+
```bash
|
|
424
|
+
yoai providers # List registered providers
|
|
425
|
+
yoai doctor # Diagnose configuration issues
|
|
426
|
+
yoai version # Show version
|
|
427
|
+
```
|
|
428
|
+
|
|
429
|
+
## Architecture
|
|
430
|
+
|
|
431
|
+
```
|
|
432
|
+
Agent
|
|
433
|
+
↓
|
|
434
|
+
LLM (config)
|
|
435
|
+
↓
|
|
436
|
+
Model Interface (BaseModel)
|
|
437
|
+
↓
|
|
438
|
+
Provider Adapter (OpenAICompatibleModel, OpenAIModel, AnthropicModel, GeminiModel)
|
|
439
|
+
↓
|
|
440
|
+
HTTP / SDK
|
|
441
|
+
```
|
|
442
|
+
|
|
443
|
+
The Agent communicates only with the common `BaseModel` interface. Provider adapters translate between internal messages and provider-specific formats. The agent code never changes when switching providers.
|
|
444
|
+
|
|
445
|
+
## License
|
|
446
|
+
|
|
447
|
+
MIT
|