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.
Files changed (100) hide show
  1. yoaiagent-0.2.2/.gitignore +10 -0
  2. yoaiagent-0.2.2/.python-version +1 -0
  3. yoaiagent-0.2.2/LICENSE +21 -0
  4. yoaiagent-0.2.2/PKG-INFO +447 -0
  5. yoaiagent-0.2.2/README.md +412 -0
  6. yoaiagent-0.2.2/asas +1 -0
  7. yoaiagent-0.2.2/docs/agents.md +262 -0
  8. yoaiagent-0.2.2/docs/api.md +592 -0
  9. yoaiagent-0.2.2/docs/cli.md +297 -0
  10. yoaiagent-0.2.2/docs/configuration.md +226 -0
  11. yoaiagent-0.2.2/docs/getting-started.md +210 -0
  12. yoaiagent-0.2.2/docs/index.md +181 -0
  13. yoaiagent-0.2.2/docs/memory.md +193 -0
  14. yoaiagent-0.2.2/docs/middleware.md +385 -0
  15. yoaiagent-0.2.2/docs/providers.md +337 -0
  16. yoaiagent-0.2.2/docs/streaming.md +233 -0
  17. yoaiagent-0.2.2/docs/tools.md +276 -0
  18. yoaiagent-0.2.2/docs/workflows.md +202 -0
  19. yoaiagent-0.2.2/examples/async_agent.py +24 -0
  20. yoaiagent-0.2.2/examples/basic.py +19 -0
  21. yoaiagent-0.2.2/examples/custom_endpoint.py +45 -0
  22. yoaiagent-0.2.2/examples/custom_provider.py +69 -0
  23. yoaiagent-0.2.2/examples/multi_agent.py +38 -0
  24. yoaiagent-0.2.2/examples/ollama.py +26 -0
  25. yoaiagent-0.2.2/examples/openrouter.py +15 -0
  26. yoaiagent-0.2.2/examples/streaming.py +24 -0
  27. yoaiagent-0.2.2/examples/structured_output.py +32 -0
  28. yoaiagent-0.2.2/examples/tools.py +32 -0
  29. yoaiagent-0.2.2/examples/workflow.py +38 -0
  30. yoaiagent-0.2.2/pyproject.toml +82 -0
  31. yoaiagent-0.2.2/src/yoaiagent/__init__.py +77 -0
  32. yoaiagent-0.2.2/src/yoaiagent/agent/__init__.py +5 -0
  33. yoaiagent-0.2.2/src/yoaiagent/agent/agent.py +401 -0
  34. yoaiagent-0.2.2/src/yoaiagent/agent/context.py +25 -0
  35. yoaiagent-0.2.2/src/yoaiagent/agent/lifecycle.py +31 -0
  36. yoaiagent-0.2.2/src/yoaiagent/agent/runner.py +21 -0
  37. yoaiagent-0.2.2/src/yoaiagent/builtin_tools/__init__.py +40 -0
  38. yoaiagent-0.2.2/src/yoaiagent/builtin_tools/coder.py +196 -0
  39. yoaiagent-0.2.2/src/yoaiagent/builtin_tools/context.py +35 -0
  40. yoaiagent-0.2.2/src/yoaiagent/builtin_tools/web.py +49 -0
  41. yoaiagent-0.2.2/src/yoaiagent/cli/__init__.py +98 -0
  42. yoaiagent-0.2.2/src/yoaiagent/config/__init__.py +5 -0
  43. yoaiagent-0.2.2/src/yoaiagent/config/settings.py +138 -0
  44. yoaiagent-0.2.2/src/yoaiagent/exceptions/__init__.py +117 -0
  45. yoaiagent-0.2.2/src/yoaiagent/memory/__init__.py +7 -0
  46. yoaiagent-0.2.2/src/yoaiagent/memory/base.py +35 -0
  47. yoaiagent-0.2.2/src/yoaiagent/memory/in_memory.py +39 -0
  48. yoaiagent-0.2.2/src/yoaiagent/memory/sqlite.py +144 -0
  49. yoaiagent-0.2.2/src/yoaiagent/middleware/__init__.py +78 -0
  50. yoaiagent-0.2.2/src/yoaiagent/middleware/budget.py +139 -0
  51. yoaiagent-0.2.2/src/yoaiagent/middleware/circuit_breaker.py +115 -0
  52. yoaiagent-0.2.2/src/yoaiagent/middleware/logging.py +121 -0
  53. yoaiagent-0.2.2/src/yoaiagent/middleware/otel.py +101 -0
  54. yoaiagent-0.2.2/src/yoaiagent/middleware/sandbox.py +48 -0
  55. yoaiagent-0.2.2/src/yoaiagent/models/__init__.py +29 -0
  56. yoaiagent-0.2.2/src/yoaiagent/models/base.py +84 -0
  57. yoaiagent-0.2.2/src/yoaiagent/models/config.py +172 -0
  58. yoaiagent-0.2.2/src/yoaiagent/models/messages.py +73 -0
  59. yoaiagent-0.2.2/src/yoaiagent/models/registry.py +5 -0
  60. yoaiagent-0.2.2/src/yoaiagent/models/response.py +79 -0
  61. yoaiagent-0.2.2/src/yoaiagent/observability/__init__.py +5 -0
  62. yoaiagent-0.2.2/src/yoaiagent/observability/events.py +70 -0
  63. yoaiagent-0.2.2/src/yoaiagent/observability/tracing.py +27 -0
  64. yoaiagent-0.2.2/src/yoaiagent/providers/__init__.py +5 -0
  65. yoaiagent-0.2.2/src/yoaiagent/providers/anthropic_provider.py +196 -0
  66. yoaiagent-0.2.2/src/yoaiagent/providers/base.py +5 -0
  67. yoaiagent-0.2.2/src/yoaiagent/providers/gemini_provider.py +150 -0
  68. yoaiagent-0.2.2/src/yoaiagent/providers/openai_compatible.py +530 -0
  69. yoaiagent-0.2.2/src/yoaiagent/providers/openai_provider.py +193 -0
  70. yoaiagent-0.2.2/src/yoaiagent/providers/registry.py +68 -0
  71. yoaiagent-0.2.2/src/yoaiagent/py.typed +0 -0
  72. yoaiagent-0.2.2/src/yoaiagent/streaming/__init__.py +5 -0
  73. yoaiagent-0.2.2/src/yoaiagent/streaming/events.py +30 -0
  74. yoaiagent-0.2.2/src/yoaiagent/structured/__init__.py +5 -0
  75. yoaiagent-0.2.2/src/yoaiagent/structured/parser.py +37 -0
  76. yoaiagent-0.2.2/src/yoaiagent/tools/__init__.py +6 -0
  77. yoaiagent-0.2.2/src/yoaiagent/tools/base.py +99 -0
  78. yoaiagent-0.2.2/src/yoaiagent/tools/decorator.py +5 -0
  79. yoaiagent-0.2.2/src/yoaiagent/tools/execution.py +22 -0
  80. yoaiagent-0.2.2/src/yoaiagent/tools/registry.py +22 -0
  81. yoaiagent-0.2.2/src/yoaiagent/tools/schema.py +133 -0
  82. yoaiagent-0.2.2/src/yoaiagent/utils/__init__.py +1 -0
  83. yoaiagent-0.2.2/src/yoaiagent/utils/logging.py +20 -0
  84. yoaiagent-0.2.2/src/yoaiagent/utils/retry.py +35 -0
  85. yoaiagent-0.2.2/src/yoaiagent/workflows/__init__.py +5 -0
  86. yoaiagent-0.2.2/src/yoaiagent/workflows/graph.py +104 -0
  87. yoaiagent-0.2.2/tests/__init__.py +1 -0
  88. yoaiagent-0.2.2/tests/conftest.py +129 -0
  89. yoaiagent-0.2.2/tests/test_agent.py +180 -0
  90. yoaiagent-0.2.2/tests/test_builtin_tools.py +138 -0
  91. yoaiagent-0.2.2/tests/test_errors.py +69 -0
  92. yoaiagent-0.2.2/tests/test_memory.py +44 -0
  93. yoaiagent-0.2.2/tests/test_models.py +145 -0
  94. yoaiagent-0.2.2/tests/test_observability.py +47 -0
  95. yoaiagent-0.2.2/tests/test_production.py +503 -0
  96. yoaiagent-0.2.2/tests/test_providers.py +157 -0
  97. yoaiagent-0.2.2/tests/test_real_endpoint.py +139 -0
  98. yoaiagent-0.2.2/tests/test_streaming.py +53 -0
  99. yoaiagent-0.2.2/tests/test_tools.py +143 -0
  100. yoaiagent-0.2.2/uv.lock +1647 -0
@@ -0,0 +1,10 @@
1
+ # Python-generated files
2
+ __pycache__/
3
+ *.py[oc]
4
+ build/
5
+ dist/
6
+ wheels/
7
+ *.egg-info
8
+
9
+ # Virtual environments
10
+ .venv
@@ -0,0 +1 @@
1
+ 3.12
@@ -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.
@@ -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