axiom-coding-agent-setup 1.0.0 → 1.0.1
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.
- package/.agents/skills/ai-integration/SKILL.md +314 -0
- package/.agents/skills/deployment-patterns/SKILL.md +408 -0
- package/.agents/skills/mcp-builder/SKILL.md +292 -0
- package/.agents/skills/n8n-patterns/SKILL.md +272 -0
- package/.agents/templates/ai-engineering-python.md +508 -0
- package/.agents/templates/fullstack-ai-nextjs.md +632 -0
- package/AGENTS.md +5 -3
- package/README.md +20 -6
- package/bin/cli.js +21 -9
- package/docs/npx-cli-setup-guide.md +318 -0
- package/package.json +2 -2
- /package/{.axiom → .agents}/engineering.md +0 -0
- /package/{.axiom → .agents}/stack.md +0 -0
- /package/{.axiom → .agents}/workflow.md +0 -0
|
@@ -0,0 +1,508 @@
|
|
|
1
|
+
# AI Engineering with Python — Project Conventions
|
|
2
|
+
|
|
3
|
+
Context template for Python-based AI/ML engineering projects.
|
|
4
|
+
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## Core Stack
|
|
8
|
+
|
|
9
|
+
### Language & Runtime
|
|
10
|
+
- **Python 3.11+** — Modern Python with improved performance and typing
|
|
11
|
+
- **uv** — Fast Python package manager (replaces pip/poetry)
|
|
12
|
+
- **Ruff** — Linter and formatter (replaces flake8, black, isort)
|
|
13
|
+
- **pyright** or **mypy** — Type checking
|
|
14
|
+
|
|
15
|
+
### Web Framework
|
|
16
|
+
- **FastAPI** — Async, type-annotated, automatic OpenAPI docs
|
|
17
|
+
- **Pydantic v2** — Data validation and settings management
|
|
18
|
+
- **Uvicorn** — ASGI server with hot reload in dev
|
|
19
|
+
|
|
20
|
+
### AI/ML Stack
|
|
21
|
+
- **LangChain / LangGraph** — LLM orchestration and agent workflows
|
|
22
|
+
- **OpenAI SDK / Anthropic SDK** — Direct LLM API access
|
|
23
|
+
- **LiteLLM** — Provider-agnostic LLM proxy
|
|
24
|
+
- **Hugging Face Transformers** — Open-source model inference
|
|
25
|
+
- **ChromaDB / pgvector** — Vector storage for RAG
|
|
26
|
+
|
|
27
|
+
### Development Tools
|
|
28
|
+
- **pytest** — Testing framework
|
|
29
|
+
- **pytest-asyncio** — Async test support
|
|
30
|
+
- **httpx** — Async HTTP client for testing APIs
|
|
31
|
+
- **python-dotenv** — Environment variable management
|
|
32
|
+
|
|
33
|
+
---
|
|
34
|
+
|
|
35
|
+
## Project Structure
|
|
36
|
+
|
|
37
|
+
```
|
|
38
|
+
project-root/
|
|
39
|
+
├── src/
|
|
40
|
+
│ ├── api/ # FastAPI application
|
|
41
|
+
│ │ ├── __init__.py
|
|
42
|
+
│ │ ├── main.py # App entry point
|
|
43
|
+
│ │ ├── deps.py # Dependencies (DB, auth)
|
|
44
|
+
│ │ └── routers/ # API route modules
|
|
45
|
+
│ │ ├── __init__.py
|
|
46
|
+
│ │ ├── chat.py # Chat/LLM endpoints
|
|
47
|
+
│ │ ├── rag.py # RAG endpoints
|
|
48
|
+
│ │ └── health.py # Health checks
|
|
49
|
+
│ ├── core/ # Core business logic
|
|
50
|
+
│ │ ├── __init__.py
|
|
51
|
+
│ │ ├── config.py # Pydantic settings
|
|
52
|
+
│ │ ├── exceptions.py # Custom exceptions
|
|
53
|
+
│ │ └── security.py # Auth utilities
|
|
54
|
+
│ ├── services/ # Business services
|
|
55
|
+
│ │ ├── __init__.py
|
|
56
|
+
│ │ ├── llm/ # LLM-related services
|
|
57
|
+
│ │ │ ├── __init__.py
|
|
58
|
+
│ │ │ ├── client.py # LLM client wrapper
|
|
59
|
+
│ │ │ ├── chains.py # LangChain chains
|
|
60
|
+
│ │ │ └── agents.py # Agent definitions
|
|
61
|
+
│ │ ├── rag/ # RAG services
|
|
62
|
+
│ │ │ ├── __init__.py
|
|
63
|
+
│ │ │ ├── embeddings.py # Embedding generation
|
|
64
|
+
│ │ │ ├── retriever.py # Document retrieval
|
|
65
|
+
│ │ │ └── indexer.py # Document indexing
|
|
66
|
+
│ │ └── embeddings/ # Embedding model management
|
|
67
|
+
│ ├── models/ # Pydantic models
|
|
68
|
+
│ │ ├── __init__.py
|
|
69
|
+
│ │ ├── requests.py # API request schemas
|
|
70
|
+
│ │ ├── responses.py # API response schemas
|
|
71
|
+
│ │ └── domain.py # Domain models
|
|
72
|
+
│ ├── db/ # Database layer
|
|
73
|
+
│ │ ├── __init__.py
|
|
74
|
+
│ │ ├── connection.py # DB connection management
|
|
75
|
+
│ │ ├── models.py # SQLAlchemy/ORM models
|
|
76
|
+
│ │ └── repositories.py # Data access layer
|
|
77
|
+
│ └── utils/ # Utilities
|
|
78
|
+
│ ├── __init__.py
|
|
79
|
+
│ ├── logging.py # Logging configuration
|
|
80
|
+
│ └── helpers.py # Helper functions
|
|
81
|
+
├── tests/
|
|
82
|
+
│ ├── __init__.py
|
|
83
|
+
│ ├── conftest.py # pytest fixtures
|
|
84
|
+
│ ├── test_api/ # API/integration tests
|
|
85
|
+
│ └── test_services/ # Unit tests
|
|
86
|
+
├── scripts/ # Utility scripts
|
|
87
|
+
│ ├── seed_db.py
|
|
88
|
+
│ └── index_documents.py
|
|
89
|
+
├── notebooks/ # Jupyter notebooks for exploration
|
|
90
|
+
├── docs/ # Documentation
|
|
91
|
+
├── .env # Environment variables (gitignored)
|
|
92
|
+
├── .env.example # Example env file
|
|
93
|
+
├── pyproject.toml # Project config and dependencies
|
|
94
|
+
├── README.md
|
|
95
|
+
└── Dockerfile
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
---
|
|
99
|
+
|
|
100
|
+
## Development Guidelines
|
|
101
|
+
|
|
102
|
+
### Configuration (Pydantic Settings)
|
|
103
|
+
|
|
104
|
+
```python
|
|
105
|
+
# src/core/config.py
|
|
106
|
+
from pydantic_settings import BaseSettings
|
|
107
|
+
from functools import lru_cache
|
|
108
|
+
|
|
109
|
+
class Settings(BaseSettings):
|
|
110
|
+
app_name: str = "AI API"
|
|
111
|
+
debug: bool = False
|
|
112
|
+
|
|
113
|
+
# API Keys
|
|
114
|
+
openai_api_key: str | None = None
|
|
115
|
+
anthropic_api_key: str | None = None
|
|
116
|
+
|
|
117
|
+
# Database
|
|
118
|
+
database_url: str = "postgresql://localhost/aiapp"
|
|
119
|
+
|
|
120
|
+
# Vector DB
|
|
121
|
+
vector_db_path: str = "./chroma_db"
|
|
122
|
+
|
|
123
|
+
class Config:
|
|
124
|
+
env_file = ".env"
|
|
125
|
+
|
|
126
|
+
@lru_cache
|
|
127
|
+
def get_settings() -> Settings:
|
|
128
|
+
return Settings()
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
### FastAPI App Structure
|
|
132
|
+
|
|
133
|
+
```python
|
|
134
|
+
# src/api/main.py
|
|
135
|
+
from fastapi import FastAPI
|
|
136
|
+
from contextlib import asynccontextmanager
|
|
137
|
+
|
|
138
|
+
from api.routers import chat, rag, health
|
|
139
|
+
from core.config import get_settings
|
|
140
|
+
|
|
141
|
+
settings = get_settings()
|
|
142
|
+
|
|
143
|
+
@asynccontextmanager
|
|
144
|
+
async def lifespan(app: FastAPI):
|
|
145
|
+
# Startup: initialize connections, load models
|
|
146
|
+
yield
|
|
147
|
+
# Shutdown: cleanup
|
|
148
|
+
|
|
149
|
+
app = FastAPI(
|
|
150
|
+
title=settings.app_name,
|
|
151
|
+
debug=settings.debug,
|
|
152
|
+
lifespan=lifespan
|
|
153
|
+
)
|
|
154
|
+
|
|
155
|
+
app.include_router(health.router, prefix="/health", tags=["health"])
|
|
156
|
+
app.include_router(chat.router, prefix="/api/v1/chat", tags=["chat"])
|
|
157
|
+
app.include_router(rag.router, prefix="/api/v1/rag", tags=["rag"])
|
|
158
|
+
```
|
|
159
|
+
|
|
160
|
+
### Router Pattern
|
|
161
|
+
|
|
162
|
+
```python
|
|
163
|
+
# src/api/routers/chat.py
|
|
164
|
+
from fastapi import APIRouter, Depends, HTTPException
|
|
165
|
+
from pydantic import BaseModel
|
|
166
|
+
|
|
167
|
+
from services.llm.client import LLMClient
|
|
168
|
+
from models.requests import ChatRequest
|
|
169
|
+
from models.responses import ChatResponse
|
|
170
|
+
|
|
171
|
+
router = APIRouter()
|
|
172
|
+
|
|
173
|
+
@router.post("/", response_model=ChatResponse)
|
|
174
|
+
async def chat(
|
|
175
|
+
request: ChatRequest,
|
|
176
|
+
llm: LLMClient = Depends(get_llm_client)
|
|
177
|
+
) -> ChatResponse:
|
|
178
|
+
try:
|
|
179
|
+
response = await llm.generate(
|
|
180
|
+
messages=request.messages,
|
|
181
|
+
model=request.model
|
|
182
|
+
)
|
|
183
|
+
return ChatResponse(content=response.content)
|
|
184
|
+
except Exception as e:
|
|
185
|
+
raise HTTPException(status_code=500, detail=str(e))
|
|
186
|
+
```
|
|
187
|
+
|
|
188
|
+
### LLM Client Wrapper
|
|
189
|
+
|
|
190
|
+
```python
|
|
191
|
+
# src/services/llm/client.py
|
|
192
|
+
from typing import AsyncGenerator
|
|
193
|
+
import openai
|
|
194
|
+
from anthropic import AsyncAnthropic
|
|
195
|
+
|
|
196
|
+
from core.config import get_settings
|
|
197
|
+
|
|
198
|
+
class LLMClient:
|
|
199
|
+
def __init__(self):
|
|
200
|
+
settings = get_settings()
|
|
201
|
+
self.openai = openai.AsyncOpenAI(api_key=settings.openai_api_key)
|
|
202
|
+
self.anthropic = AsyncAnthropic(api_key=settings.anthropic_api_key)
|
|
203
|
+
|
|
204
|
+
async def generate(
|
|
205
|
+
self,
|
|
206
|
+
messages: list[dict],
|
|
207
|
+
model: str = "gpt-4",
|
|
208
|
+
stream: bool = False
|
|
209
|
+
) -> str:
|
|
210
|
+
if model.startswith("claude"):
|
|
211
|
+
return await self._generate_anthropic(messages, model)
|
|
212
|
+
return await self._generate_openai(messages, model)
|
|
213
|
+
|
|
214
|
+
async def _generate_openai(self, messages, model):
|
|
215
|
+
response = await self.openai.chat.completions.create(
|
|
216
|
+
model=model,
|
|
217
|
+
messages=messages
|
|
218
|
+
)
|
|
219
|
+
return response.choices[0].message.content
|
|
220
|
+
|
|
221
|
+
async def stream_generate(
|
|
222
|
+
self,
|
|
223
|
+
messages: list[dict],
|
|
224
|
+
model: str = "gpt-4"
|
|
225
|
+
) -> AsyncGenerator[str, None]:
|
|
226
|
+
stream = await self.openai.chat.completions.create(
|
|
227
|
+
model=model,
|
|
228
|
+
messages=messages,
|
|
229
|
+
stream=True
|
|
230
|
+
)
|
|
231
|
+
async for chunk in stream:
|
|
232
|
+
if chunk.choices[0].delta.content:
|
|
233
|
+
yield chunk.choices[0].delta.content
|
|
234
|
+
|
|
235
|
+
def get_llm_client() -> LLMClient:
|
|
236
|
+
return LLMClient()
|
|
237
|
+
```
|
|
238
|
+
|
|
239
|
+
### RAG Pattern with ChromaDB
|
|
240
|
+
|
|
241
|
+
```python
|
|
242
|
+
# src/services/rag/retriever.py
|
|
243
|
+
import chromadb
|
|
244
|
+
from chromadb.config import Settings
|
|
245
|
+
|
|
246
|
+
from core.config import get_settings
|
|
247
|
+
|
|
248
|
+
class RAGRetriever:
|
|
249
|
+
def __init__(self):
|
|
250
|
+
settings = get_settings()
|
|
251
|
+
self.client = chromadb.PersistentClient(
|
|
252
|
+
path=settings.vector_db_path,
|
|
253
|
+
settings=Settings(anonymized_telemetry=False)
|
|
254
|
+
)
|
|
255
|
+
self.collection = self.client.get_or_create_collection("documents")
|
|
256
|
+
|
|
257
|
+
async def add_documents(
|
|
258
|
+
self,
|
|
259
|
+
documents: list[str],
|
|
260
|
+
embeddings: list[list[float]],
|
|
261
|
+
ids: list[str],
|
|
262
|
+
metadatas: list[dict] | None = None
|
|
263
|
+
):
|
|
264
|
+
self.collection.add(
|
|
265
|
+
documents=documents,
|
|
266
|
+
embeddings=embeddings,
|
|
267
|
+
ids=ids,
|
|
268
|
+
metadatas=metadatas
|
|
269
|
+
)
|
|
270
|
+
|
|
271
|
+
async def query(
|
|
272
|
+
self,
|
|
273
|
+
query_embedding: list[float],
|
|
274
|
+
n_results: int = 5
|
|
275
|
+
) -> list[dict]:
|
|
276
|
+
results = self.collection.query(
|
|
277
|
+
query_embeddings=[query_embedding],
|
|
278
|
+
n_results=n_results
|
|
279
|
+
)
|
|
280
|
+
return [
|
|
281
|
+
{
|
|
282
|
+
"document": doc,
|
|
283
|
+
"metadata": meta,
|
|
284
|
+
"distance": dist
|
|
285
|
+
}
|
|
286
|
+
for doc, meta, dist in zip(
|
|
287
|
+
results["documents"][0],
|
|
288
|
+
results["metadatas"][0],
|
|
289
|
+
results["distances"][0]
|
|
290
|
+
)
|
|
291
|
+
]
|
|
292
|
+
```
|
|
293
|
+
|
|
294
|
+
---
|
|
295
|
+
|
|
296
|
+
## Testing Patterns
|
|
297
|
+
|
|
298
|
+
### pytest Configuration
|
|
299
|
+
|
|
300
|
+
```toml
|
|
301
|
+
# pyproject.toml
|
|
302
|
+
[tool.pytest.ini_options]
|
|
303
|
+
testpaths = ["tests"]
|
|
304
|
+
python_files = ["test_*.py"]
|
|
305
|
+
python_functions = ["test_*"]
|
|
306
|
+
addopts = "-v --tb=short"
|
|
307
|
+
asyncio_mode = "auto"
|
|
308
|
+
```
|
|
309
|
+
|
|
310
|
+
### Test Fixtures
|
|
311
|
+
|
|
312
|
+
```python
|
|
313
|
+
# tests/conftest.py
|
|
314
|
+
import pytest
|
|
315
|
+
from fastapi.testclient import TestClient
|
|
316
|
+
from httpx import AsyncClient
|
|
317
|
+
|
|
318
|
+
from api.main import app
|
|
319
|
+
|
|
320
|
+
@pytest.fixture
|
|
321
|
+
def client():
|
|
322
|
+
return TestClient(app)
|
|
323
|
+
|
|
324
|
+
@pytest.fixture
|
|
325
|
+
async def async_client():
|
|
326
|
+
async with AsyncClient(app=app, base_url="http://test") as client:
|
|
327
|
+
yield client
|
|
328
|
+
|
|
329
|
+
@pytest.fixture
|
|
330
|
+
def mock_llm_response():
|
|
331
|
+
return {
|
|
332
|
+
"content": "Test response",
|
|
333
|
+
"model": "gpt-4",
|
|
334
|
+
"usage": {"prompt_tokens": 10, "completion_tokens": 20}
|
|
335
|
+
}
|
|
336
|
+
```
|
|
337
|
+
|
|
338
|
+
### API Tests
|
|
339
|
+
|
|
340
|
+
```python
|
|
341
|
+
# tests/test_api/test_chat.py
|
|
342
|
+
import pytest
|
|
343
|
+
from unittest.mock import AsyncMock, patch
|
|
344
|
+
|
|
345
|
+
@pytest.mark.asyncio
|
|
346
|
+
async def test_chat_endpoint(async_client, mock_llm_response):
|
|
347
|
+
with patch("services.llm.client.LLMClient.generate", new_callable=AsyncMock) as mock:
|
|
348
|
+
mock.return_value = mock_llm_response
|
|
349
|
+
|
|
350
|
+
response = await async_client.post("/api/v1/chat/", json={
|
|
351
|
+
"messages": [{"role": "user", "content": "Hello"}],
|
|
352
|
+
"model": "gpt-4"
|
|
353
|
+
})
|
|
354
|
+
|
|
355
|
+
assert response.status_code == 200
|
|
356
|
+
assert response.json()["content"] == "Test response"
|
|
357
|
+
```
|
|
358
|
+
|
|
359
|
+
---
|
|
360
|
+
|
|
361
|
+
## Environment Setup
|
|
362
|
+
|
|
363
|
+
### .env.example
|
|
364
|
+
|
|
365
|
+
```bash
|
|
366
|
+
# App
|
|
367
|
+
APP_NAME="AI API"
|
|
368
|
+
DEBUG=true
|
|
369
|
+
|
|
370
|
+
# API Keys
|
|
371
|
+
OPENAI_API_KEY=sk-...
|
|
372
|
+
ANTHROPIC_API_KEY=sk-ant-...
|
|
373
|
+
|
|
374
|
+
# Database
|
|
375
|
+
DATABASE_URL=postgresql://user:pass@localhost/aiapp
|
|
376
|
+
|
|
377
|
+
# Vector DB
|
|
378
|
+
VECTOR_DB_PATH=./chroma_db
|
|
379
|
+
```
|
|
380
|
+
|
|
381
|
+
### pyproject.toml Dependencies
|
|
382
|
+
|
|
383
|
+
```toml
|
|
384
|
+
[project]
|
|
385
|
+
name = "ai-engineering-api"
|
|
386
|
+
version = "0.1.0"
|
|
387
|
+
description = "AI Engineering API"
|
|
388
|
+
requires-python = ">=3.11"
|
|
389
|
+
dependencies = [
|
|
390
|
+
# Web
|
|
391
|
+
"fastapi>=0.104.0",
|
|
392
|
+
"uvicorn[standard]>=0.24.0",
|
|
393
|
+
"pydantic>=2.0.0",
|
|
394
|
+
"pydantic-settings>=2.0.0",
|
|
395
|
+
|
|
396
|
+
# AI/ML
|
|
397
|
+
"openai>=1.0.0",
|
|
398
|
+
"anthropic>=0.8.0",
|
|
399
|
+
"langchain>=0.1.0",
|
|
400
|
+
"langgraph>=0.0.40",
|
|
401
|
+
"chromadb>=0.4.0",
|
|
402
|
+
"sentence-transformers>=2.2.0",
|
|
403
|
+
"litellm>=1.0.0",
|
|
404
|
+
|
|
405
|
+
# Database
|
|
406
|
+
"sqlalchemy>=2.0.0",
|
|
407
|
+
"asyncpg>=0.29.0",
|
|
408
|
+
"alembic>=1.12.0",
|
|
409
|
+
|
|
410
|
+
# Utils
|
|
411
|
+
"python-dotenv>=1.0.0",
|
|
412
|
+
"structlog>=23.0.0",
|
|
413
|
+
"orjson>=3.9.0",
|
|
414
|
+
]
|
|
415
|
+
|
|
416
|
+
[project.optional-dependencies]
|
|
417
|
+
dev = [
|
|
418
|
+
"pytest>=7.4.0",
|
|
419
|
+
"pytest-asyncio>=0.21.0",
|
|
420
|
+
"httpx>=0.25.0",
|
|
421
|
+
"ruff>=0.1.0",
|
|
422
|
+
"pyright>=1.1.0",
|
|
423
|
+
]
|
|
424
|
+
|
|
425
|
+
[tool.ruff]
|
|
426
|
+
line-length = 88
|
|
427
|
+
target-version = "py311"
|
|
428
|
+
select = ["E", "F", "I", "N", "W", "UP", "B", "C4", "SIM"]
|
|
429
|
+
ignore = ["E501"]
|
|
430
|
+
|
|
431
|
+
[tool.ruff.format]
|
|
432
|
+
quote-style = "double"
|
|
433
|
+
indent-style = "space"
|
|
434
|
+
|
|
435
|
+
[tool.pyright]
|
|
436
|
+
pythonVersion = "3.11"
|
|
437
|
+
strict = ["src"]
|
|
438
|
+
```
|
|
439
|
+
|
|
440
|
+
---
|
|
441
|
+
|
|
442
|
+
## Docker Setup
|
|
443
|
+
|
|
444
|
+
```dockerfile
|
|
445
|
+
# Dockerfile
|
|
446
|
+
FROM python:3.11-slim
|
|
447
|
+
|
|
448
|
+
WORKDIR /app
|
|
449
|
+
|
|
450
|
+
# Install uv
|
|
451
|
+
RUN pip install uv
|
|
452
|
+
|
|
453
|
+
# Copy dependency files
|
|
454
|
+
COPY pyproject.toml ./
|
|
455
|
+
|
|
456
|
+
# Install dependencies
|
|
457
|
+
RUN uv pip install --system -e ".[dev]"
|
|
458
|
+
|
|
459
|
+
# Copy source
|
|
460
|
+
COPY src/ ./src/
|
|
461
|
+
|
|
462
|
+
# Run
|
|
463
|
+
CMD ["uvicorn", "src.api.main:app", "--host", "0.0.0.0", "--port", "8000"]
|
|
464
|
+
```
|
|
465
|
+
|
|
466
|
+
---
|
|
467
|
+
|
|
468
|
+
## Code Quality Standards
|
|
469
|
+
|
|
470
|
+
### Ruff Configuration (in pyproject.toml)
|
|
471
|
+
- Line length: 88 (Black-compatible)
|
|
472
|
+
- Target Python: 3.11+
|
|
473
|
+
- Enable: E, F, I (isort), N (pep8-naming), W, UP (pyupgrade), B (flake8-bugbear)
|
|
474
|
+
|
|
475
|
+
### Type Hints
|
|
476
|
+
- Use `|` union syntax (Python 3.10+): `str | None`
|
|
477
|
+
- Use built-in generics: `list[str]`, `dict[str, int]`
|
|
478
|
+
- Annotate all function signatures
|
|
479
|
+
- Use Pydantic models for complex data structures
|
|
480
|
+
|
|
481
|
+
### Async Patterns
|
|
482
|
+
- Use `async`/`await` for all I/O operations
|
|
483
|
+
- Prefer `asyncio.gather()` for concurrent operations
|
|
484
|
+
- Use `asynccontextmanager` for resource management
|
|
485
|
+
- Don't use `asyncio.run()` in FastAPI handlers (already in async context)
|
|
486
|
+
|
|
487
|
+
### Error Handling
|
|
488
|
+
```python
|
|
489
|
+
# Custom exceptions
|
|
490
|
+
class LLMError(Exception):
|
|
491
|
+
"""Base class for LLM-related errors"""
|
|
492
|
+
pass
|
|
493
|
+
|
|
494
|
+
class RateLimitError(LLMError):
|
|
495
|
+
"""API rate limit exceeded"""
|
|
496
|
+
pass
|
|
497
|
+
|
|
498
|
+
# Usage in services
|
|
499
|
+
from fastapi import HTTPException
|
|
500
|
+
|
|
501
|
+
try:
|
|
502
|
+
result = await llm.generate(messages)
|
|
503
|
+
except RateLimitError as e:
|
|
504
|
+
raise HTTPException(status_code=429, detail="Rate limit exceeded")
|
|
505
|
+
except LLMError as e:
|
|
506
|
+
logger.error(f"LLM error: {e}")
|
|
507
|
+
raise HTTPException(status_code=502, detail="LLM service error")
|
|
508
|
+
```
|