litclaude-ai 0.2.2
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/CHANGELOG.md +155 -0
- package/LICENSE +21 -0
- package/README.md +369 -0
- package/README_ko-KR.md +374 -0
- package/RELEASE_CHECKLIST.md +165 -0
- package/bin/litclaude-ai.js +643 -0
- package/cover.png +0 -0
- package/docs/agents.md +67 -0
- package/docs/hooks.md +134 -0
- package/docs/lsp.md +40 -0
- package/docs/migration.md +209 -0
- package/docs/workflow-compatibility-audit.md +119 -0
- package/generate_cover.py +123 -0
- package/package.json +48 -0
- package/plugins/litclaude/.claude-plugin/plugin.json +25 -0
- package/plugins/litclaude/.lsp.json +13 -0
- package/plugins/litclaude/.mcp.json +9 -0
- package/plugins/litclaude/agents/boulder-executor.md +12 -0
- package/plugins/litclaude/agents/librarian-researcher.md +15 -0
- package/plugins/litclaude/agents/oracle-verifier.md +16 -0
- package/plugins/litclaude/agents/prometheus-planner.md +13 -0
- package/plugins/litclaude/agents/qa-runner.md +16 -0
- package/plugins/litclaude/agents/quality-reviewer.md +17 -0
- package/plugins/litclaude/bin/litclaude-hook.js +110 -0
- package/plugins/litclaude/bin/litclaude-hud.js +271 -0
- package/plugins/litclaude/bin/litclaude-lsp-doctor.js +15 -0
- package/plugins/litclaude/bin/litclaude-mcp.js +70 -0
- package/plugins/litclaude/commands/deep-interview.md +21 -0
- package/plugins/litclaude/commands/dynamic-workflow.md +36 -0
- package/plugins/litclaude/commands/lit-loop.md +40 -0
- package/plugins/litclaude/commands/lit-plan.md +35 -0
- package/plugins/litclaude/commands/litgoal.md +30 -0
- package/plugins/litclaude/commands/review-work.md +35 -0
- package/plugins/litclaude/commands/start-work.md +36 -0
- package/plugins/litclaude/hooks/hooks.json +54 -0
- package/plugins/litclaude/lib/context-pressure.mjs +25 -0
- package/plugins/litclaude/lib/hud-accent-palette.mjs +58 -0
- package/plugins/litclaude/lib/litgoal/cli.mjs +266 -0
- package/plugins/litclaude/lib/litgoal/ledger.mjs +16 -0
- package/plugins/litclaude/lib/litgoal/paths.mjs +7 -0
- package/plugins/litclaude/lib/litgoal/state.mjs +67 -0
- package/plugins/litclaude/lib/mutated-file-paths.mjs +63 -0
- package/plugins/litclaude/lib/start-work-continuation.mjs +99 -0
- package/plugins/litclaude/lib/workflow-check.mjs +83 -0
- package/plugins/litclaude/skills/ai-slop-remover/SKILL.md +142 -0
- package/plugins/litclaude/skills/comment-checker/SKILL.md +55 -0
- package/plugins/litclaude/skills/debugging/SKILL.md +70 -0
- package/plugins/litclaude/skills/debugging/references/methodology/00-setup.md +108 -0
- package/plugins/litclaude/skills/debugging/references/methodology/02-investigate.md +126 -0
- package/plugins/litclaude/skills/debugging/references/methodology/04-oracle-triple.md +106 -0
- package/plugins/litclaude/skills/debugging/references/methodology/05-escalate.md +69 -0
- package/plugins/litclaude/skills/debugging/references/methodology/06-fix.md +116 -0
- package/plugins/litclaude/skills/debugging/references/methodology/08-qa.md +94 -0
- package/plugins/litclaude/skills/debugging/references/methodology/09-cleanup.md +164 -0
- package/plugins/litclaude/skills/debugging/references/methodology/partial-runtime-evidence.md +228 -0
- package/plugins/litclaude/skills/debugging/references/runtimes/bundled-js-binary.md +415 -0
- package/plugins/litclaude/skills/debugging/references/runtimes/go.md +252 -0
- package/plugins/litclaude/skills/debugging/references/runtimes/native-binary.md +484 -0
- package/plugins/litclaude/skills/debugging/references/runtimes/node.md +260 -0
- package/plugins/litclaude/skills/debugging/references/runtimes/python.md +248 -0
- package/plugins/litclaude/skills/debugging/references/runtimes/rust.md +234 -0
- package/plugins/litclaude/skills/debugging/references/tools/ghidra.md +212 -0
- package/plugins/litclaude/skills/debugging/references/tools/playwright-cli.md +194 -0
- package/plugins/litclaude/skills/debugging/references/tools/pwndbg.md +263 -0
- package/plugins/litclaude/skills/debugging/references/tools/pwntools.md +265 -0
- package/plugins/litclaude/skills/deep-interview/SKILL.md +323 -0
- package/plugins/litclaude/skills/deep-interview/scripts/render_progress.py +193 -0
- package/plugins/litclaude/skills/frontend-ui-ux/SKILL.md +62 -0
- package/plugins/litclaude/skills/lit-loop/SKILL.md +144 -0
- package/plugins/litclaude/skills/lit-plan/SKILL.md +125 -0
- package/plugins/litclaude/skills/litgoal/SKILL.md +219 -0
- package/plugins/litclaude/skills/lsp/SKILL.md +63 -0
- package/plugins/litclaude/skills/programming/SKILL.md +106 -0
- package/plugins/litclaude/skills/programming/references/go/README.md +90 -0
- package/plugins/litclaude/skills/programming/references/go/backend-stack.md +641 -0
- package/plugins/litclaude/skills/programming/references/go/bootstrap.md +328 -0
- package/plugins/litclaude/skills/programming/references/go/bubbletea-v2.md +360 -0
- package/plugins/litclaude/skills/programming/references/go/cobra-stack.md +468 -0
- package/plugins/litclaude/skills/programming/references/go/concurrency.md +362 -0
- package/plugins/litclaude/skills/programming/references/go/data-modeling.md +329 -0
- package/plugins/litclaude/skills/programming/references/go/error-handling.md +359 -0
- package/plugins/litclaude/skills/programming/references/go/golangci-strict.md +236 -0
- package/plugins/litclaude/skills/programming/references/go/grpc-connect.md +375 -0
- package/plugins/litclaude/skills/programming/references/go/libraries.md +337 -0
- package/plugins/litclaude/skills/programming/references/go/one-liners.md +202 -0
- package/plugins/litclaude/skills/programming/references/go/sqlc-pgx.md +471 -0
- package/plugins/litclaude/skills/programming/references/go/testing.md +467 -0
- package/plugins/litclaude/skills/programming/references/go/type-patterns.md +298 -0
- package/plugins/litclaude/skills/programming/references/python/README.md +314 -0
- package/plugins/litclaude/skills/programming/references/python/async-anyio.md +442 -0
- package/plugins/litclaude/skills/programming/references/python/data-modeling.md +233 -0
- package/plugins/litclaude/skills/programming/references/python/data-processing.md +133 -0
- package/plugins/litclaude/skills/programming/references/python/error-handling.md +218 -0
- package/plugins/litclaude/skills/programming/references/python/fastapi-stack.md +316 -0
- package/plugins/litclaude/skills/programming/references/python/httpx2-optimization.md +360 -0
- package/plugins/litclaude/skills/programming/references/python/libraries.md +307 -0
- package/plugins/litclaude/skills/programming/references/python/one-liners.md +268 -0
- package/plugins/litclaude/skills/programming/references/python/orjson-stack.md +378 -0
- package/plugins/litclaude/skills/programming/references/python/pydantic-ai.md +285 -0
- package/plugins/litclaude/skills/programming/references/python/pyproject-strict.md +232 -0
- package/plugins/litclaude/skills/programming/references/python/textual-tui.md +201 -0
- package/plugins/litclaude/skills/programming/references/python/type-patterns.md +176 -0
- package/plugins/litclaude/skills/programming/references/rust/README.md +317 -0
- package/plugins/litclaude/skills/programming/references/rust/async-tokio.md +299 -0
- package/plugins/litclaude/skills/programming/references/rust/axum-stack.md +467 -0
- package/plugins/litclaude/skills/programming/references/rust/cargo-strict.md +317 -0
- package/plugins/litclaude/skills/programming/references/rust/clap-stack.md +409 -0
- package/plugins/litclaude/skills/programming/references/rust/concurrency.md +375 -0
- package/plugins/litclaude/skills/programming/references/rust/libraries.md +439 -0
- package/plugins/litclaude/skills/programming/references/rust/one-liners.md +291 -0
- package/plugins/litclaude/skills/programming/references/rust/proptest-insta.md +429 -0
- package/plugins/litclaude/skills/programming/references/rust/type-state.md +354 -0
- package/plugins/litclaude/skills/programming/references/rust/unsafe-discipline.md +250 -0
- package/plugins/litclaude/skills/programming/references/rust/zero-cost-safety.md +527 -0
- package/plugins/litclaude/skills/programming/references/rust-ub/README.md +289 -0
- package/plugins/litclaude/skills/programming/references/rust-ub/miri-sanitizers-loom.md +411 -0
- package/plugins/litclaude/skills/programming/references/rust-ub/ub-taxonomy.md +269 -0
- package/plugins/litclaude/skills/programming/references/typescript/README.md +195 -0
- package/plugins/litclaude/skills/programming/references/typescript/backend-hono.md +672 -0
- package/plugins/litclaude/skills/programming/references/typescript/bootstrap.md +199 -0
- package/plugins/litclaude/skills/programming/references/typescript/data-modeling.md +202 -0
- package/plugins/litclaude/skills/programming/references/typescript/error-handling.md +169 -0
- package/plugins/litclaude/skills/programming/references/typescript/tsconfig-strict.md +152 -0
- package/plugins/litclaude/skills/programming/references/typescript/type-patterns.md +196 -0
- package/plugins/litclaude/skills/programming/scripts/go/check-no-excuse-rules.sh +173 -0
- package/plugins/litclaude/skills/programming/scripts/go/new-project.py +138 -0
- package/plugins/litclaude/skills/programming/scripts/go/templates/.editorconfig +13 -0
- package/plugins/litclaude/skills/programming/scripts/go/templates/.golangci.yml +95 -0
- package/plugins/litclaude/skills/programming/scripts/go/templates/AGENTS.md.tmpl +24 -0
- package/plugins/litclaude/skills/programming/scripts/go/templates/README.md.tmpl +12 -0
- package/plugins/litclaude/skills/programming/scripts/go/templates/Taskfile.yml +40 -0
- package/plugins/litclaude/skills/programming/scripts/go/templates/ci.yml +37 -0
- package/plugins/litclaude/skills/programming/scripts/go/templates/config.go +24 -0
- package/plugins/litclaude/skills/programming/scripts/go/templates/gitignore +15 -0
- package/plugins/litclaude/skills/programming/scripts/go/templates/main.go.tmpl +22 -0
- package/plugins/litclaude/skills/programming/scripts/go/templates/run.go +15 -0
- package/plugins/litclaude/skills/programming/scripts/python/check-no-excuse-rules.py +687 -0
- package/plugins/litclaude/skills/programming/scripts/python/new-project.py +172 -0
- package/plugins/litclaude/skills/programming/scripts/python/new-script.py +116 -0
- package/plugins/litclaude/skills/programming/scripts/rust/check-no-excuse-rules.py +296 -0
- package/plugins/litclaude/skills/programming/scripts/rust/check-no-excuse-rules.sh +158 -0
- package/plugins/litclaude/skills/programming/scripts/rust/new-project.py +175 -0
- package/plugins/litclaude/skills/programming/scripts/typescript/check-no-excuse-rules.ts +282 -0
- package/plugins/litclaude/skills/programming/scripts/typescript/new-project.ts +177 -0
- package/plugins/litclaude/skills/refactor/SKILL.md +73 -0
- package/plugins/litclaude/skills/remove-ai-slops/SKILL.md +52 -0
- package/plugins/litclaude/skills/review-work/SKILL.md +331 -0
- package/plugins/litclaude/skills/rules/SKILL.md +66 -0
- package/plugins/litclaude/skills/start-work/SKILL.md +132 -0
- package/scripts/audit-plan-checkboxes.mjs +37 -0
- package/scripts/doctor.mjs +41 -0
- package/scripts/inspect-agent-tools.mjs +27 -0
- package/scripts/postinstall.mjs +50 -0
- package/scripts/qa-claude-plugin-smoke.sh +60 -0
- package/scripts/qa-portable-install.sh +136 -0
- package/scripts/validate-plugin.mjs +72 -0
|
@@ -0,0 +1,285 @@
|
|
|
1
|
+
# PydanticAI Reference (v1.x, 2026)
|
|
2
|
+
|
|
3
|
+
> Canonical patterns for wiring PydanticAI agents. Target: production usage, late-2025 / 2026.
|
|
4
|
+
> Source: [ai.pydantic.dev](https://ai.pydantic.dev) and [pydantic/pydantic-ai@`cad9569`](https://github.com/pydantic/pydantic-ai/blob/cad956910079737ea0886b50cef15777208f92e6).
|
|
5
|
+
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
## 1. Agent Constructor
|
|
9
|
+
|
|
10
|
+
```python
|
|
11
|
+
from pydantic_ai import Agent
|
|
12
|
+
|
|
13
|
+
agent = Agent(
|
|
14
|
+
'openai:gpt-5.2', # model (str | Model | None)
|
|
15
|
+
output_type=MyOutputModel, # structured output type; default=str
|
|
16
|
+
instructions='You are a...', # static or callable instructions
|
|
17
|
+
system_prompt='Be concise.', # static system prompt(s)
|
|
18
|
+
deps_type=MyDeps, # dependency type for type-checking only
|
|
19
|
+
name='my-agent', # optional, inferred from var name if omitted
|
|
20
|
+
retries=1, # default retries for tools + output validation
|
|
21
|
+
output_retries=None, # override retries for output validation only
|
|
22
|
+
tools=[my_tool], # list of Tool objects or plain functions
|
|
23
|
+
defer_model_check=False, # set True to skip env-var check at init time
|
|
24
|
+
end_strategy='early', # 'early' | 'graceful' | 'exhaustive'
|
|
25
|
+
)
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
**Breaking change (v1.88.0)**: `result_type` was renamed to `output_type`. Use `output_type`.
|
|
29
|
+
|
|
30
|
+
---
|
|
31
|
+
|
|
32
|
+
## 2. Model Strings
|
|
33
|
+
|
|
34
|
+
Format: `provider:model-name`. The framework infers the provider from the prefix.
|
|
35
|
+
|
|
36
|
+
| Provider prefix | Example |
|
|
37
|
+
|---|---|
|
|
38
|
+
| `openai:` | `'openai:gpt-5.2'`, `'openai:gpt-4o'` |
|
|
39
|
+
| `anthropic:` | `'anthropic:claude-sonnet-4-6'`, `'anthropic:claude-opus-4-1'` |
|
|
40
|
+
| `google-gla:` | `'google-gla:gemini-3-flash-preview'` |
|
|
41
|
+
| `google-vertex:` | `'google-vertex:gemini-3-pro-preview'` |
|
|
42
|
+
| `bedrock:` | `'bedrock:anthropic.claude-sonnet-4-6'` |
|
|
43
|
+
| `xai:` / `grok:` | `'xai:grok-3'`, `'grok:grok-3-fast'` |
|
|
44
|
+
| `deepseek:` | `'deepseek:deepseek-chat'` |
|
|
45
|
+
| `cohere:` | `'cohere:command-r-08-2024'` |
|
|
46
|
+
| `gateway/...` | `'gateway/openai:gpt-5.2'` (PydanticAI Gateway) |
|
|
47
|
+
|
|
48
|
+
Model can also be omitted at construction and passed per-run: `agent.run(prompt, model='openai:gpt-5.2')`.
|
|
49
|
+
|
|
50
|
+
---
|
|
51
|
+
|
|
52
|
+
## 3. Tools
|
|
53
|
+
|
|
54
|
+
### Decorator syntax
|
|
55
|
+
|
|
56
|
+
```python
|
|
57
|
+
from pydantic_ai import Agent, RunContext
|
|
58
|
+
|
|
59
|
+
agent = Agent('openai:gpt-5.2', deps_type=str)
|
|
60
|
+
|
|
61
|
+
@agent.tool # default: receives RunContext as first arg
|
|
62
|
+
async def greet(ctx: RunContext[str], name: str) -> str:
|
|
63
|
+
return f"Hello {ctx.deps}, {name}!"
|
|
64
|
+
|
|
65
|
+
@agent.tool_plain # no context needed
|
|
66
|
+
async def roll_dice(sides: int) -> int:
|
|
67
|
+
import random
|
|
68
|
+
return random.randint(1, sides)
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
### `RunContext[Deps]`
|
|
72
|
+
|
|
73
|
+
First parameter of `@agent.tool` functions. Carries:
|
|
74
|
+
|
|
75
|
+
- `ctx.deps` — the dependency instance
|
|
76
|
+
- `ctx.model` — the model being used
|
|
77
|
+
- `ctx.usage` — token usage so far
|
|
78
|
+
- `ctx.messages` — conversation history
|
|
79
|
+
- `ctx.retry` / `ctx.max_retries` — current retry count
|
|
80
|
+
- `ctx.agent` — the running agent instance
|
|
81
|
+
|
|
82
|
+
Use `@agent.tool_plain` when the tool does **not** need any of the above.
|
|
83
|
+
|
|
84
|
+
---
|
|
85
|
+
|
|
86
|
+
## 4. Structured Output
|
|
87
|
+
|
|
88
|
+
Pass a Pydantic `BaseModel` (or `bool`, `int`, `list[str]`, etc.) as `output_type`. The result is accessed via `.output`.
|
|
89
|
+
|
|
90
|
+
```python
|
|
91
|
+
from pydantic import BaseModel
|
|
92
|
+
from pydantic_ai import Agent
|
|
93
|
+
|
|
94
|
+
class City(BaseModel):
|
|
95
|
+
name: str
|
|
96
|
+
country: str
|
|
97
|
+
population_millions: float
|
|
98
|
+
|
|
99
|
+
agent = Agent('openai:gpt-5.2', output_type=City)
|
|
100
|
+
result = agent.run_sync('Tell me about Tokyo')
|
|
101
|
+
print(result.output) # City(name='Tokyo', country='Japan', ...)
|
|
102
|
+
print(result.output.name) # 'Tokyo'
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
**Note**: `result.data` was renamed; the canonical accessor is `result.output`.
|
|
106
|
+
|
|
107
|
+
---
|
|
108
|
+
|
|
109
|
+
## 5. Async vs Sync
|
|
110
|
+
|
|
111
|
+
| Method | Mode | Returns |
|
|
112
|
+
|---|---|---|
|
|
113
|
+
| `await agent.run(prompt, ...)` | async | `AgentRunResult[OutputDataT]` |
|
|
114
|
+
| `agent.run_sync(prompt, ...)` | sync | `AgentRunResult[OutputDataT]` |
|
|
115
|
+
| `async with agent.run_stream(prompt, ...) as response:` | async streaming | `StreamedRunResult` |
|
|
116
|
+
|
|
117
|
+
```python
|
|
118
|
+
# Sync
|
|
119
|
+
result = agent.run_sync('What is the capital of Italy?')
|
|
120
|
+
print(result.output)
|
|
121
|
+
|
|
122
|
+
# Async
|
|
123
|
+
result = await agent.run('What is the capital of France?')
|
|
124
|
+
print(result.output)
|
|
125
|
+
|
|
126
|
+
# Streaming
|
|
127
|
+
async with agent.run_stream('What is the capital of the UK?') as response:
|
|
128
|
+
async for text in response.stream_text():
|
|
129
|
+
print(text, end='')
|
|
130
|
+
# After streaming finishes:
|
|
131
|
+
print(response.output)
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
`run_sync()` is a convenience wrapper over `loop.run_until_complete(self.run(...))`. Do not use it inside an active async context.
|
|
135
|
+
|
|
136
|
+
---
|
|
137
|
+
|
|
138
|
+
## 6. Dependencies
|
|
139
|
+
|
|
140
|
+
Use a `@dataclass` container, pass the **type** to `deps_type`, and pass an **instance** to `deps` at run time.
|
|
141
|
+
|
|
142
|
+
```python
|
|
143
|
+
from dataclasses import dataclass
|
|
144
|
+
import httpx
|
|
145
|
+
from pydantic_ai import Agent, RunContext
|
|
146
|
+
|
|
147
|
+
@dataclass
|
|
148
|
+
class Deps:
|
|
149
|
+
api_key: str
|
|
150
|
+
http_client: httpx.AsyncClient
|
|
151
|
+
|
|
152
|
+
agent = Agent(
|
|
153
|
+
'openai:gpt-5.2',
|
|
154
|
+
deps_type=Deps,
|
|
155
|
+
)
|
|
156
|
+
|
|
157
|
+
@agent.tool
|
|
158
|
+
async def fetch_data(ctx: RunContext[Deps], endpoint: str) -> str:
|
|
159
|
+
r = await ctx.deps.http_client.get(
|
|
160
|
+
endpoint,
|
|
161
|
+
headers={'Authorization': f'Bearer {ctx.deps.api_key}'},
|
|
162
|
+
)
|
|
163
|
+
r.raise_for_status()
|
|
164
|
+
return r.text
|
|
165
|
+
|
|
166
|
+
async def main():
|
|
167
|
+
async with httpx.AsyncClient() as client:
|
|
168
|
+
deps = Deps(api_key='sk-...', http_client=client)
|
|
169
|
+
result = await agent.run('Get /users', deps=deps)
|
|
170
|
+
print(result.output)
|
|
171
|
+
```
|
|
172
|
+
|
|
173
|
+
---
|
|
174
|
+
|
|
175
|
+
## 7. Error Types & Retrying from a Tool
|
|
176
|
+
|
|
177
|
+
```python
|
|
178
|
+
from pydantic_ai import Agent, ModelRetry, UnexpectedModelBehavior, capture_run_messages
|
|
179
|
+
|
|
180
|
+
agent = Agent('openai:gpt-5.2', retries=3)
|
|
181
|
+
|
|
182
|
+
@agent.tool_plain
|
|
183
|
+
def calc_volume(size: int) -> int:
|
|
184
|
+
if size == 42:
|
|
185
|
+
return size ** 3
|
|
186
|
+
raise ModelRetry('Please try again with size 42.')
|
|
187
|
+
|
|
188
|
+
with capture_run_messages() as messages:
|
|
189
|
+
try:
|
|
190
|
+
result = agent.run_sync('Get the volume of a box with size 6.')
|
|
191
|
+
except UnexpectedModelBehavior as e:
|
|
192
|
+
print('Error:', e) # "Tool 'calc_volume' exceeded max retries count of 3"
|
|
193
|
+
print('Cause:', e.__cause__) # ModelRetry('Please try again...')
|
|
194
|
+
print('Messages:', messages)
|
|
195
|
+
```
|
|
196
|
+
|
|
197
|
+
- **`ModelRetry`** — raise from a tool, output validator, or capability hook to ask the model to retry.
|
|
198
|
+
- **`UnexpectedModelBehavior`** — raised when the retry limit is exceeded or the model API returns an unrecoverable error.
|
|
199
|
+
- **`capture_run_messages()`** — context manager that records all messages exchanged during a run for debugging.
|
|
200
|
+
|
|
201
|
+
---
|
|
202
|
+
|
|
203
|
+
## 8. Logfire Integration
|
|
204
|
+
|
|
205
|
+
One-line setup if the `logfire` extra is installed (included in the default `pydantic-ai` package):
|
|
206
|
+
|
|
207
|
+
```python
|
|
208
|
+
import logfire
|
|
209
|
+
|
|
210
|
+
logfire.configure() # reads token from .logfire directory
|
|
211
|
+
logfire.instrument_pydantic_ai() # auto-traces all agent runs
|
|
212
|
+
```
|
|
213
|
+
|
|
214
|
+
Alternatively, set `instrument=True` on the agent:
|
|
215
|
+
|
|
216
|
+
```python
|
|
217
|
+
agent = Agent('openai:gpt-5.2', instrument=True)
|
|
218
|
+
```
|
|
219
|
+
|
|
220
|
+
---
|
|
221
|
+
|
|
222
|
+
## 9. Minimal Complete Snippets
|
|
223
|
+
|
|
224
|
+
### (a) Basic agent with structured output
|
|
225
|
+
|
|
226
|
+
```python
|
|
227
|
+
from pydantic import BaseModel
|
|
228
|
+
from pydantic_ai import Agent
|
|
229
|
+
|
|
230
|
+
class City(BaseModel):
|
|
231
|
+
name: str
|
|
232
|
+
country: str
|
|
233
|
+
|
|
234
|
+
agent = Agent('openai:gpt-5.2', output_type=City)
|
|
235
|
+
result = agent.run_sync('Tell me about Paris')
|
|
236
|
+
print(result.output) # City(name='Paris', country='France')
|
|
237
|
+
```
|
|
238
|
+
|
|
239
|
+
### (b) Agent with tools and dependencies
|
|
240
|
+
|
|
241
|
+
```python
|
|
242
|
+
from dataclasses import dataclass
|
|
243
|
+
from pydantic_ai import Agent, RunContext
|
|
244
|
+
|
|
245
|
+
@dataclass
|
|
246
|
+
class Deps:
|
|
247
|
+
api_key: str
|
|
248
|
+
|
|
249
|
+
agent = Agent('openai:gpt-5.2', deps_type=Deps)
|
|
250
|
+
|
|
251
|
+
@agent.tool
|
|
252
|
+
async def get_secret(ctx: RunContext[Deps], code: str) -> str:
|
|
253
|
+
if code == '1234':
|
|
254
|
+
return f'secret-for-{ctx.deps.api_key}'
|
|
255
|
+
return 'wrong code'
|
|
256
|
+
|
|
257
|
+
result = agent.run_sync('My code is 1234', deps=Deps(api_key='sk-abc'))
|
|
258
|
+
print(result.output)
|
|
259
|
+
```
|
|
260
|
+
|
|
261
|
+
### (c) Async streaming
|
|
262
|
+
|
|
263
|
+
```python
|
|
264
|
+
import anyio
|
|
265
|
+
from pydantic_ai import Agent
|
|
266
|
+
|
|
267
|
+
agent = Agent('openai:gpt-5.2')
|
|
268
|
+
|
|
269
|
+
async def main() -> None:
|
|
270
|
+
async with agent.run_stream('Write a haiku about Python') as response:
|
|
271
|
+
async for text in response.stream_text():
|
|
272
|
+
print(text, end='')
|
|
273
|
+
print('\n---')
|
|
274
|
+
print('Final:', response.output)
|
|
275
|
+
|
|
276
|
+
anyio.run(main)
|
|
277
|
+
```
|
|
278
|
+
|
|
279
|
+
---
|
|
280
|
+
|
|
281
|
+
## Version Notes
|
|
282
|
+
|
|
283
|
+
- **V1** reached API stability in September 2025. Breaking changes are reserved for V2 (earliest April 2026).
|
|
284
|
+
- **v1.88.0** renamed `result_type` → `output_type` and `result_tool_name` / `result_tool_description` were removed. Use `output_type`.
|
|
285
|
+
- The canonical accessor for run results is `result.output` (not `result.data`).
|
|
@@ -0,0 +1,232 @@
|
|
|
1
|
+
# Strict pyproject.toml (basedpyright + ruff + uv)
|
|
2
|
+
|
|
3
|
+
The canonical "super strict but sane" config for modern Python projects. Copy-paste, then add your own dependencies.
|
|
4
|
+
|
|
5
|
+
## Bootstrap
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
# Application
|
|
9
|
+
uv init --app myproject
|
|
10
|
+
cd myproject
|
|
11
|
+
|
|
12
|
+
# Library (publishable to PyPI)
|
|
13
|
+
uv init --lib mylibrary
|
|
14
|
+
cd mylibrary
|
|
15
|
+
|
|
16
|
+
# Add dev tools
|
|
17
|
+
uv add --dev basedpyright ruff pytest
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
`uv init` creates `pyproject.toml`, `.python-version`, and `src/` layout. Replace its `pyproject.toml` `[tool.*]` sections with the block below.
|
|
21
|
+
|
|
22
|
+
## The full pyproject.toml
|
|
23
|
+
|
|
24
|
+
```toml
|
|
25
|
+
[project]
|
|
26
|
+
name = "myproject"
|
|
27
|
+
version = "0.1.0"
|
|
28
|
+
description = "..."
|
|
29
|
+
readme = "README.md"
|
|
30
|
+
requires-python = ">=3.13"
|
|
31
|
+
dependencies = []
|
|
32
|
+
|
|
33
|
+
[dependency-groups]
|
|
34
|
+
dev = [
|
|
35
|
+
"basedpyright>=1.21",
|
|
36
|
+
"ruff>=0.8",
|
|
37
|
+
"pytest>=8",
|
|
38
|
+
"pytest-cov>=5",
|
|
39
|
+
]
|
|
40
|
+
|
|
41
|
+
# ─────────────────────────────────────────────────────────────────
|
|
42
|
+
# basedpyright - typeCheckingMode = "all" sets every report flag to error
|
|
43
|
+
# Source: https://docs.basedpyright.com/latest/configuration/config-files/
|
|
44
|
+
# ─────────────────────────────────────────────────────────────────
|
|
45
|
+
[tool.basedpyright]
|
|
46
|
+
typeCheckingMode = "all"
|
|
47
|
+
pythonVersion = "3.13"
|
|
48
|
+
pythonPlatform = "All" # default in basedpyright; explicit for clarity
|
|
49
|
+
include = ["src", "tests"]
|
|
50
|
+
exclude = ["**/__pycache__", "**/.venv", "**/build", "**/dist"]
|
|
51
|
+
|
|
52
|
+
# Strict enforcement extras (most are already "error" under "all" mode,
|
|
53
|
+
# but listing them explicitly documents the intent)
|
|
54
|
+
reportUnusedCallResult = "warning" # flag ignored return values
|
|
55
|
+
reportUnnecessaryTypeIgnoreComment = "error" # stale type: ignore comments must die
|
|
56
|
+
reportUnusedVariable = "error" # unused variables are errors
|
|
57
|
+
reportMissingParameterType = "error" # every parameter must have a type
|
|
58
|
+
reportMissingReturnType = "error" # every function must declare its return type
|
|
59
|
+
reportPrivateUsage = "error" # respect _private convention
|
|
60
|
+
|
|
61
|
+
# Optional: gradual adoption baseline
|
|
62
|
+
# baselineFile = "./.basedpyright/baseline.json"
|
|
63
|
+
|
|
64
|
+
# ─────────────────────────────────────────────────────────────────
|
|
65
|
+
# ruff - select = ["ALL"] enables every rule, then we ignore the
|
|
66
|
+
# small set that conflicts with the formatter or is not useful.
|
|
67
|
+
# Source: https://docs.astral.sh/ruff/linter/#rule-selection
|
|
68
|
+
# ─────────────────────────────────────────────────────────────────
|
|
69
|
+
[tool.ruff]
|
|
70
|
+
target-version = "py313"
|
|
71
|
+
line-length = 88 # ruff/black default; 100 or 120 also fine
|
|
72
|
+
src = ["src", "tests"]
|
|
73
|
+
|
|
74
|
+
[tool.ruff.lint]
|
|
75
|
+
select = ["ALL"]
|
|
76
|
+
ignore = [
|
|
77
|
+
# Formatter conflicts (ruff itself tells you to ignore these)
|
|
78
|
+
"COM812", # missing trailing comma
|
|
79
|
+
"ISC001", # implicit string concat
|
|
80
|
+
# Docstyle conflicts (pick D211 over D203, D212 over D213)
|
|
81
|
+
"D203",
|
|
82
|
+
"D213",
|
|
83
|
+
# Project-specific noise
|
|
84
|
+
"CPY001", # missing copyright notice
|
|
85
|
+
"FBT001", # boolean positional arg in def
|
|
86
|
+
"FBT002", # boolean positional default in def
|
|
87
|
+
"TD002", # missing TODO author
|
|
88
|
+
"TD003", # missing TODO link
|
|
89
|
+
"FIX002", # line contains TODO (TODOs are allowed)
|
|
90
|
+
]
|
|
91
|
+
fixable = ["ALL"]
|
|
92
|
+
unfixable = []
|
|
93
|
+
|
|
94
|
+
[tool.ruff.lint.per-file-ignores]
|
|
95
|
+
"tests/**/*.py" = [
|
|
96
|
+
"S101", # `assert` is the entire point of pytest
|
|
97
|
+
"ARG", # unused args (fixtures appear unused)
|
|
98
|
+
"PLR2004", # magic numbers in test data
|
|
99
|
+
"SLF001", # tests need access to private members
|
|
100
|
+
"D", # docstrings not required in tests
|
|
101
|
+
]
|
|
102
|
+
"scripts/**/*.py" = [
|
|
103
|
+
"T201", # `print` allowed in scripts
|
|
104
|
+
"INP001", # implicit namespace package
|
|
105
|
+
]
|
|
106
|
+
|
|
107
|
+
[tool.ruff.lint.pydocstyle]
|
|
108
|
+
convention = "google" # or "numpy" / "pep257"
|
|
109
|
+
|
|
110
|
+
[tool.ruff.lint.flake8-bugbear]
|
|
111
|
+
# typer / fastapi rely on call-as-default for parameter metadata.
|
|
112
|
+
# Without this, ruff B008 ("function call in default") fires on every typer/fastapi route.
|
|
113
|
+
extend-immutable-calls = [
|
|
114
|
+
"typer.Argument",
|
|
115
|
+
"typer.Option",
|
|
116
|
+
"fastapi.Depends",
|
|
117
|
+
"fastapi.Query",
|
|
118
|
+
"fastapi.Path",
|
|
119
|
+
"fastapi.Body",
|
|
120
|
+
"fastapi.Header",
|
|
121
|
+
"fastapi.Cookie",
|
|
122
|
+
"fastapi.File",
|
|
123
|
+
"fastapi.Form",
|
|
124
|
+
]
|
|
125
|
+
|
|
126
|
+
[tool.ruff.format]
|
|
127
|
+
quote-style = "double"
|
|
128
|
+
indent-style = "space"
|
|
129
|
+
docstring-code-format = true
|
|
130
|
+
docstring-code-line-length = "dynamic"
|
|
131
|
+
|
|
132
|
+
# ─────────────────────────────────────────────────────────────────
|
|
133
|
+
# pytest
|
|
134
|
+
# ─────────────────────────────────────────────────────────────────
|
|
135
|
+
[tool.pytest.ini_options]
|
|
136
|
+
minversion = "8.0"
|
|
137
|
+
testpaths = ["tests"]
|
|
138
|
+
addopts = [
|
|
139
|
+
"-ra",
|
|
140
|
+
"--strict-config",
|
|
141
|
+
"--strict-markers",
|
|
142
|
+
]
|
|
143
|
+
filterwarnings = ["error"]
|
|
144
|
+
|
|
145
|
+
# ─────────────────────────────────────────────────────────────────
|
|
146
|
+
# coverage
|
|
147
|
+
# ─────────────────────────────────────────────────────────────────
|
|
148
|
+
[tool.coverage.run]
|
|
149
|
+
source = ["src"]
|
|
150
|
+
branch = true
|
|
151
|
+
|
|
152
|
+
[tool.coverage.report]
|
|
153
|
+
exclude_lines = [
|
|
154
|
+
"pragma: no cover",
|
|
155
|
+
"if TYPE_CHECKING:",
|
|
156
|
+
"if typing.TYPE_CHECKING:",
|
|
157
|
+
"raise NotImplementedError",
|
|
158
|
+
"@(abc\\.)?abstractmethod",
|
|
159
|
+
]
|
|
160
|
+
```
|
|
161
|
+
|
|
162
|
+
## Why these settings
|
|
163
|
+
|
|
164
|
+
### basedpyright `typeCheckingMode = "all"`
|
|
165
|
+
|
|
166
|
+
basedpyright's modes, strictest first:
|
|
167
|
+
|
|
168
|
+
| Mode | Behavior |
|
|
169
|
+
|---|---|
|
|
170
|
+
| `"all"` | Every diagnostic at `error` |
|
|
171
|
+
| `"recommended"` | Same rules; less severe ones at `warning`; `failOnWarnings = true` makes CI still fail |
|
|
172
|
+
| `"strict"` | pyright's strict mode |
|
|
173
|
+
| `"standard"` | Default |
|
|
174
|
+
| `"basic"` / `"off"` | Loose / disabled |
|
|
175
|
+
|
|
176
|
+
`"all"` enables basedpyright-exclusive rules pyright lacks: `reportImplicitOverride`, `reportImplicitStringConcatenation`, `reportIncompatibleUnannotatedOverride`, `reportUnannotatedClassAttribute`. No need to opt-in to additional flags.
|
|
177
|
+
|
|
178
|
+
`pythonPlatform = "All"` is basedpyright's default (better than pyright's host-OS default) - it errors on platform-specific imports that fail on other OSes.
|
|
179
|
+
|
|
180
|
+
### ruff `select = ["ALL"]`
|
|
181
|
+
|
|
182
|
+
The official docs say *"Use ALL with discretion. Enabling ALL will implicitly enable new rules whenever you upgrade."* For a strict skill that is the intended behavior - every new ruff rule should be considered an error until you justify ignoring it.
|
|
183
|
+
|
|
184
|
+
The minimal ignore set:
|
|
185
|
+
|
|
186
|
+
| Rule | Reason |
|
|
187
|
+
|---|---|
|
|
188
|
+
| `COM812`, `ISC001` | Conflict with `ruff format` (ruff itself documents this) |
|
|
189
|
+
| `D203` vs `D211`, `D213` vs `D212` | Mutually-exclusive docstring conventions; pick the modern one |
|
|
190
|
+
| `CPY001` | Most projects don't need a copyright header on every file |
|
|
191
|
+
| `FBT001`, `FBT002` | Boolean flags are ergonomic for CLI/typer; ban makes typer awkward |
|
|
192
|
+
| `TD002`, `TD003`, `FIX002` | TODOs without a JIRA link are fine in solo / internal code |
|
|
193
|
+
|
|
194
|
+
`ANN101` and `ANN102` were **removed in ruff 0.8.0** (Nov 2024). Do NOT include them in `ignore` - ruff errors on unknown rule codes.
|
|
195
|
+
|
|
196
|
+
`per-file-ignores` for `tests/**` is the standard pattern from real-world repos like `community-of-python/auto-typing-final` and `Preston-Landers/concurrent-log-handler`.
|
|
197
|
+
|
|
198
|
+
## CI gate
|
|
199
|
+
|
|
200
|
+
```bash
|
|
201
|
+
# In CI, fail on any violation:
|
|
202
|
+
uv run basedpyright
|
|
203
|
+
uv run ruff check
|
|
204
|
+
uv run ruff format --check
|
|
205
|
+
uv run pytest
|
|
206
|
+
```
|
|
207
|
+
|
|
208
|
+
A single `make ci` target combining the four works fine.
|
|
209
|
+
|
|
210
|
+
## Enforcement summary
|
|
211
|
+
|
|
212
|
+
The config above, combined with `scripts/check-no-excuse-rules.py`, enforces:
|
|
213
|
+
|
|
214
|
+
| What | How |
|
|
215
|
+
|---|---|
|
|
216
|
+
| Exhaustive match | basedpyright `all` mode + `assert_never` |
|
|
217
|
+
| No `Any` | basedpyright `all` mode + script `cast-any` rule |
|
|
218
|
+
| Ignored return values | `reportUnusedCallResult = "warning"` |
|
|
219
|
+
| Immutable default | Script `mutable-dataclass` + `missing-slots` rules |
|
|
220
|
+
| No null surprise | basedpyright strict `None` analysis |
|
|
221
|
+
| Constants are const | basedpyright catches `Final` reassignment |
|
|
222
|
+
| Unused variables | `reportUnusedVariable = "error"` |
|
|
223
|
+
|
|
224
|
+
## Sources
|
|
225
|
+
|
|
226
|
+
- basedpyright modes: <https://docs.basedpyright.com/latest/configuration/config-files/#type-check-diagnostics-settings>
|
|
227
|
+
- basedpyright `"all"` vs `"recommended"`: <https://docs.basedpyright.com/latest/configuration/config-files/#recommended-and-all>
|
|
228
|
+
- basedpyright better defaults: <https://docs.basedpyright.com/latest/benefits-over-pyright/better-defaults/>
|
|
229
|
+
- ruff rule selection: <https://docs.astral.sh/ruff/linter/#rule-selection>
|
|
230
|
+
- ruff ANN101/ANN102 removed: <https://github.com/astral-sh/ruff/pull/14384>
|
|
231
|
+
- Real-world ALL config: <https://github.com/community-of-python/auto-typing-final/blob/main/pyproject.toml>
|
|
232
|
+
- PEP 735 dependency-groups: <https://peps.python.org/pep-0735/>
|