fastapi-streams 0.1.0__tar.gz → 0.3.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {fastapi_streams-0.1.0 → fastapi_streams-0.3.0}/CHANGELOG.md +93 -0
- {fastapi_streams-0.1.0 → fastapi_streams-0.3.0}/PKG-INFO +6 -5
- {fastapi_streams-0.1.0 → fastapi_streams-0.3.0}/README.md +5 -4
- {fastapi_streams-0.1.0 → fastapi_streams-0.3.0}/docs/README.md +2 -1
- {fastapi_streams-0.1.0 → fastapi_streams-0.3.0}/docs/clients.md +32 -0
- fastapi_streams-0.3.0/docs/codegen.md +232 -0
- fastapi_streams-0.3.0/docs/native-transports.md +49 -0
- fastapi_streams-0.3.0/docs/releasing.md +13 -0
- {fastapi_streams-0.1.0 → fastapi_streams-0.3.0}/examples/agent_runs/errors.py +1 -1
- {fastapi_streams-0.1.0 → fastapi_streams-0.3.0}/examples/agent_runs/generated_python/agent_runs/api.py +32 -7
- {fastapi_streams-0.1.0 → fastapi_streams-0.3.0}/examples/agent_runs/generated_python/models.py +3 -1
- {fastapi_streams-0.1.0 → fastapi_streams-0.3.0}/examples/agent_runs/generated_python/runtime.py +91 -2
- fastapi_streams-0.3.0/examples/native_proxy.py +99 -0
- fastapi_streams-0.3.0/examples/rest_codegen.py +76 -0
- {fastapi_streams-0.1.0 → fastapi_streams-0.3.0}/fastapi_streams/__init__.py +1 -1
- {fastapi_streams-0.1.0 → fastapi_streams-0.3.0}/fastapi_streams/codegen/cli.py +75 -2
- fastapi_streams-0.3.0/fastapi_streams/codegen/diagnostics.py +333 -0
- {fastapi_streams-0.1.0 → fastapi_streams-0.3.0}/fastapi_streams/codegen/documentation.py +16 -1
- {fastapi_streams-0.1.0 → fastapi_streams-0.3.0}/fastapi_streams/codegen/ir.py +61 -8
- {fastapi_streams-0.1.0 → fastapi_streams-0.3.0}/fastapi_streams/codegen/python.py +30 -3
- {fastapi_streams-0.1.0 → fastapi_streams-0.3.0}/fastapi_streams/codegen/templates/python/api.py.j2 +21 -2
- {fastapi_streams-0.1.0/tests/fixtures/generated_python → fastapi_streams-0.3.0/fastapi_streams/codegen/templates/python}/runtime.py +91 -2
- {fastapi_streams-0.1.0 → fastapi_streams-0.3.0}/pyproject.toml +1 -1
- {fastapi_streams-0.1.0 → fastapi_streams-0.3.0}/scripts/codegen.py +7 -0
- {fastapi_streams-0.1.0 → fastapi_streams-0.3.0}/scripts/smoke_package.py +77 -9
- {fastapi_streams-0.1.0 → fastapi_streams-0.3.0}/tests/codegen/test_documentation.py +3 -1
- fastapi_streams-0.3.0/tests/codegen/test_native_contract.py +56 -0
- fastapi_streams-0.3.0/tests/codegen/test_request_validation.py +226 -0
- fastapi_streams-0.3.0/tests/codegen/test_scalar_constraints.py +65 -0
- fastapi_streams-0.3.0/tests/codegen/test_stream_validation_details.py +82 -0
- fastapi_streams-0.3.0/tests/codegen/test_structured_diagnostics.py +209 -0
- fastapi_streams-0.3.0/tests/codegen/test_unknown_events.py +83 -0
- fastapi_streams-0.3.0/tests/fixtures/agent_runs.typescript.diagnostics.json +20 -0
- {fastapi_streams-0.1.0 → fastapi_streams-0.3.0}/tests/fixtures/generated_python/fixtures/api.py +20 -3
- {fastapi_streams-0.1.0/fastapi_streams/codegen/templates/python → fastapi_streams-0.3.0/tests/fixtures/generated_python}/runtime.py +91 -2
- {fastapi_streams-0.1.0 → fastapi_streams-0.3.0}/tests/integration/test_end_to_end.py +8 -8
- fastapi_streams-0.1.0/docs/codegen.md +0 -118
- fastapi_streams-0.1.0/docs/releasing.md +0 -160
- fastapi_streams-0.1.0/fastapi_streams/codegen/diagnostics.py +0 -79
- {fastapi_streams-0.1.0 → fastapi_streams-0.3.0}/.gitattributes +0 -0
- {fastapi_streams-0.1.0 → fastapi_streams-0.3.0}/.github/workflows/ci.yml +0 -0
- {fastapi_streams-0.1.0 → fastapi_streams-0.3.0}/.github/workflows/compatibility.yml +0 -0
- {fastapi_streams-0.1.0 → fastapi_streams-0.3.0}/.github/workflows/release.yml +0 -0
- {fastapi_streams-0.1.0 → fastapi_streams-0.3.0}/.gitignore +0 -0
- {fastapi_streams-0.1.0 → fastapi_streams-0.3.0}/.pre-commit-config.yaml +0 -0
- {fastapi_streams-0.1.0 → fastapi_streams-0.3.0}/AGENTS.md +0 -0
- {fastapi_streams-0.1.0 → fastapi_streams-0.3.0}/LICENSE +0 -0
- {fastapi_streams-0.1.0 → fastapi_streams-0.3.0}/docs/architecture.md +0 -0
- {fastapi_streams-0.1.0 → fastapi_streams-0.3.0}/docs/development.md +0 -0
- {fastapi_streams-0.1.0 → fastapi_streams-0.3.0}/docs/quickstart.md +0 -0
- {fastapi_streams-0.1.0 → fastapi_streams-0.3.0}/examples/__init__.py +0 -0
- {fastapi_streams-0.1.0 → fastapi_streams-0.3.0}/examples/agent_runs/__init__.py +0 -0
- {fastapi_streams-0.1.0 → fastapi_streams-0.3.0}/examples/agent_runs/consume.py +0 -0
- {fastapi_streams-0.1.0 → fastapi_streams-0.3.0}/examples/agent_runs/consume.ts +0 -0
- {fastapi_streams-0.1.0 → fastapi_streams-0.3.0}/examples/agent_runs/errors.ts +0 -0
- {fastapi_streams-0.1.0 → fastapi_streams-0.3.0}/examples/agent_runs/generated/.fastapi-streams.json +0 -0
- {fastapi_streams-0.1.0 → fastapi_streams-0.3.0}/examples/agent_runs/generated/agent-runs/api.ts +0 -0
- {fastapi_streams-0.1.0 → fastapi_streams-0.3.0}/examples/agent_runs/generated/agent-runs/index.ts +0 -0
- {fastapi_streams-0.1.0 → fastapi_streams-0.3.0}/examples/agent_runs/generated/client.ts +0 -0
- {fastapi_streams-0.1.0 → fastapi_streams-0.3.0}/examples/agent_runs/generated/index.ts +0 -0
- {fastapi_streams-0.1.0 → fastapi_streams-0.3.0}/examples/agent_runs/generated/transport/errors.ts +0 -0
- {fastapi_streams-0.1.0 → fastapi_streams-0.3.0}/examples/agent_runs/generated/transport/sse.ts +0 -0
- {fastapi_streams-0.1.0 → fastapi_streams-0.3.0}/examples/agent_runs/generated/types.ts +0 -0
- {fastapi_streams-0.1.0 → fastapi_streams-0.3.0}/examples/agent_runs/generated_python/.fastapi-streams.json +0 -0
- {fastapi_streams-0.1.0 → fastapi_streams-0.3.0}/examples/agent_runs/generated_python/__init__.py +0 -0
- {fastapi_streams-0.1.0 → fastapi_streams-0.3.0}/examples/agent_runs/generated_python/agent_runs/__init__.py +0 -0
- {fastapi_streams-0.1.0 → fastapi_streams-0.3.0}/examples/agent_runs/generated_python/client.py +0 -0
- {fastapi_streams-0.1.0 → fastapi_streams-0.3.0}/examples/agent_runs/generated_python/py.typed +0 -0
- {fastapi_streams-0.1.0 → fastapi_streams-0.3.0}/fastapi_streams/codegen/__init__.py +0 -0
- {fastapi_streams-0.1.0 → fastapi_streams-0.3.0}/fastapi_streams/codegen/enums.py +0 -0
- {fastapi_streams-0.1.0 → fastapi_streams-0.3.0}/fastapi_streams/codegen/names.py +0 -0
- {fastapi_streams-0.1.0 → fastapi_streams-0.3.0}/fastapi_streams/codegen/openapi.py +0 -0
- {fastapi_streams-0.1.0 → fastapi_streams-0.3.0}/fastapi_streams/codegen/schemas/LICENSE +0 -0
- {fastapi_streams-0.1.0 → fastapi_streams-0.3.0}/fastapi_streams/codegen/schemas/README.md +0 -0
- {fastapi_streams-0.1.0 → fastapi_streams-0.3.0}/fastapi_streams/codegen/schemas/openapi-3.2.json +0 -0
- {fastapi_streams-0.1.0 → fastapi_streams-0.3.0}/fastapi_streams/codegen/templates/errors.ts +0 -0
- {fastapi_streams-0.1.0 → fastapi_streams-0.3.0}/fastapi_streams/codegen/templates/python/client.py.j2 +0 -0
- {fastapi_streams-0.1.0 → fastapi_streams-0.3.0}/fastapi_streams/codegen/templates/sse.ts +0 -0
- {fastapi_streams-0.1.0 → fastapi_streams-0.3.0}/fastapi_streams/codegen/templates/typescript/api.ts.j2 +0 -0
- {fastapi_streams-0.1.0 → fastapi_streams-0.3.0}/fastapi_streams/codegen/templates/typescript/client.ts.j2 +0 -0
- {fastapi_streams-0.1.0 → fastapi_streams-0.3.0}/fastapi_streams/codegen/templating.py +0 -0
- {fastapi_streams-0.1.0 → fastapi_streams-0.3.0}/fastapi_streams/codegen/typescript.py +0 -0
- {fastapi_streams-0.1.0 → fastapi_streams-0.3.0}/fastapi_streams/codegen/writer.py +0 -0
- {fastapi_streams-0.1.0 → fastapi_streams-0.3.0}/fastapi_streams/py.typed +0 -0
- {fastapi_streams-0.1.0 → fastapi_streams-0.3.0}/fastapi_streams/router.py +0 -0
- {fastapi_streams-0.1.0 → fastapi_streams-0.3.0}/package-lock.json +0 -0
- {fastapi_streams-0.1.0 → fastapi_streams-0.3.0}/package.json +0 -0
- {fastapi_streams-0.1.0 → fastapi_streams-0.3.0}/scripts/check_package.py +0 -0
- {fastapi_streams-0.1.0 → fastapi_streams-0.3.0}/scripts/check_release.py +0 -0
- {fastapi_streams-0.1.0 → fastapi_streams-0.3.0}/scripts/test_compat.py +0 -0
- {fastapi_streams-0.1.0 → fastapi_streams-0.3.0}/tests/__init__.py +0 -0
- {fastapi_streams-0.1.0 → fastapi_streams-0.3.0}/tests/codegen/helpers.py +0 -0
- {fastapi_streams-0.1.0 → fastapi_streams-0.3.0}/tests/codegen/test_additional_properties.py +0 -0
- {fastapi_streams-0.1.0 → fastapi_streams-0.3.0}/tests/codegen/test_codegen.py +0 -0
- {fastapi_streams-0.1.0 → fastapi_streams-0.3.0}/tests/codegen/test_contract_boundaries.py +0 -0
- {fastapi_streams-0.1.0 → fastapi_streams-0.3.0}/tests/codegen/test_diagnostics.py +0 -0
- {fastapi_streams-0.1.0 → fastapi_streams-0.3.0}/tests/codegen/test_discriminators.py +0 -0
- {fastapi_streams-0.1.0 → fastapi_streams-0.3.0}/tests/codegen/test_error_contracts.py +0 -0
- {fastapi_streams-0.1.0 → fastapi_streams-0.3.0}/tests/codegen/test_field_names.py +0 -0
- {fastapi_streams-0.1.0 → fastapi_streams-0.3.0}/tests/codegen/test_http_backends.py +0 -0
- {fastapi_streams-0.1.0 → fastapi_streams-0.3.0}/tests/codegen/test_intersections.py +0 -0
- {fastapi_streams-0.1.0 → fastapi_streams-0.3.0}/tests/codegen/test_literal_fields.py +0 -0
- {fastapi_streams-0.1.0 → fastapi_streams-0.3.0}/tests/codegen/test_nested_models.py +0 -0
- {fastapi_streams-0.1.0 → fastapi_streams-0.3.0}/tests/codegen/test_python_client.py +0 -0
- {fastapi_streams-0.1.0 → fastapi_streams-0.3.0}/tests/codegen/test_python_typing.py +0 -0
- {fastapi_streams-0.1.0 → fastapi_streams-0.3.0}/tests/codegen/test_required_properties.py +0 -0
- {fastapi_streams-0.1.0 → fastapi_streams-0.3.0}/tests/codegen/test_schema_matrix.py +0 -0
- {fastapi_streams-0.1.0 → fastapi_streams-0.3.0}/tests/contracts/test_openapi.py +0 -0
- {fastapi_streams-0.1.0 → fastapi_streams-0.3.0}/tests/fixtures/README.md +0 -0
- {fastapi_streams-0.1.0 → fastapi_streams-0.3.0}/tests/fixtures/__init__.py +0 -0
- {fastapi_streams-0.1.0 → fastapi_streams-0.3.0}/tests/fixtures/agent_runs.openapi.json +0 -0
- {fastapi_streams-0.1.0 → fastapi_streams-0.3.0}/tests/fixtures/compat-client.ts +0 -0
- {fastapi_streams-0.1.0 → fastapi_streams-0.3.0}/tests/fixtures/generated/.fastapi-streams.json +0 -0
- {fastapi_streams-0.1.0 → fastapi_streams-0.3.0}/tests/fixtures/generated/client.ts +0 -0
- {fastapi_streams-0.1.0 → fastapi_streams-0.3.0}/tests/fixtures/generated/fixtures/api.ts +0 -0
- {fastapi_streams-0.1.0 → fastapi_streams-0.3.0}/tests/fixtures/generated/fixtures/index.ts +0 -0
- {fastapi_streams-0.1.0 → fastapi_streams-0.3.0}/tests/fixtures/generated/index.ts +0 -0
- {fastapi_streams-0.1.0 → fastapi_streams-0.3.0}/tests/fixtures/generated/transport/errors.ts +0 -0
- {fastapi_streams-0.1.0 → fastapi_streams-0.3.0}/tests/fixtures/generated/transport/sse.ts +0 -0
- {fastapi_streams-0.1.0 → fastapi_streams-0.3.0}/tests/fixtures/generated/types.ts +0 -0
- {fastapi_streams-0.1.0 → fastapi_streams-0.3.0}/tests/fixtures/generated_python/.fastapi-streams.json +0 -0
- {fastapi_streams-0.1.0 → fastapi_streams-0.3.0}/tests/fixtures/generated_python/__init__.py +0 -0
- {fastapi_streams-0.1.0 → fastapi_streams-0.3.0}/tests/fixtures/generated_python/client.py +0 -0
- {fastapi_streams-0.1.0 → fastapi_streams-0.3.0}/tests/fixtures/generated_python/fixtures/__init__.py +0 -0
- {fastapi_streams-0.1.0 → fastapi_streams-0.3.0}/tests/fixtures/generated_python/models.py +0 -0
- {fastapi_streams-0.1.0 → fastapi_streams-0.3.0}/tests/fixtures/generated_python/py.typed +0 -0
- {fastapi_streams-0.1.0 → fastapi_streams-0.3.0}/tests/fixtures/integration_app.py +0 -0
- {fastapi_streams-0.1.0 → fastapi_streams-0.3.0}/tests/fixtures/models.openapi.json +0 -0
- {fastapi_streams-0.1.0 → fastapi_streams-0.3.0}/tests/fixtures/models_app.py +0 -0
- {fastapi_streams-0.1.0 → fastapi_streams-0.3.0}/tests/router/test_router.py +0 -0
- {fastapi_streams-0.1.0 → fastapi_streams-0.3.0}/tests/test_release.py +0 -0
- {fastapi_streams-0.1.0 → fastapi_streams-0.3.0}/tests/typescript/errors.test.ts +0 -0
- {fastapi_streams-0.1.0 → fastapi_streams-0.3.0}/tests/typescript/live-client.ts +0 -0
- {fastapi_streams-0.1.0 → fastapi_streams-0.3.0}/tests/typescript/models.test.ts +0 -0
- {fastapi_streams-0.1.0 → fastapi_streams-0.3.0}/tests/typescript/narrowing.ts +0 -0
- {fastapi_streams-0.1.0 → fastapi_streams-0.3.0}/tests/typescript/sse.test.ts +0 -0
- {fastapi_streams-0.1.0 → fastapi_streams-0.3.0}/tests/typescript/tsconfig.json +0 -0
- {fastapi_streams-0.1.0 → fastapi_streams-0.3.0}/tests/typing/client_usage.py +0 -0
|
@@ -3,6 +3,99 @@
|
|
|
3
3
|
All notable changes and consumer upgrade steps are recorded here. Entries marked
|
|
4
4
|
unreleased describe prepared source versions that have not been published.
|
|
5
5
|
|
|
6
|
+
## 0.3.0 - 2026-10-18
|
|
7
|
+
|
|
8
|
+
### Breaking changes
|
|
9
|
+
|
|
10
|
+
- Regenerated Python SDKs validate path, query and header parameters at the
|
|
11
|
+
method call, before serialization and HTTP I/O. Invalid types or supported
|
|
12
|
+
scalar constraints now raise Pydantic `ValidationError` locally. Regenerate
|
|
13
|
+
SDKs, fix rejected arguments, and catch request validation around the method
|
|
14
|
+
call rather than only around stream iteration. Request bodies retain their
|
|
15
|
+
existing validation timing; HTTP still starts when iteration begins.
|
|
16
|
+
- TypeScript generation now diagnoses scalar constraints it cannot enforce.
|
|
17
|
+
Review the selected target's diagnostics before promoting warnings to errors.
|
|
18
|
+
Existing known limitations can be accepted by exact diagnostic ID with a
|
|
19
|
+
review reason; do not suppress the warning category globally.
|
|
20
|
+
|
|
21
|
+
### Added
|
|
22
|
+
|
|
23
|
+
- Cache generated parameter, body and event TypeAdapters. Keep valid parameter
|
|
24
|
+
encoding, integer representations of number parameters, query array explosion,
|
|
25
|
+
optional omission and JSON body aliases unchanged.
|
|
26
|
+
- Source-located diagnostics include JSON Pointer, operation ID, method/path,
|
|
27
|
+
keyword/value, target and enforcement limitation. Referenced and inline schemas
|
|
28
|
+
are located in the original document, with each affected operation identified.
|
|
29
|
+
- Add `--diagnostics FILE`, `--strict` and `--allow-diagnostics FILE` to generation
|
|
30
|
+
and drift checks. Reports are deterministic JSON with stable identities;
|
|
31
|
+
malformed, duplicate and stale allowances fail before generated files change.
|
|
32
|
+
Repository drift checks use strict mode with reviewed TypeScript limitations.
|
|
33
|
+
- Generated Python `StreamParseError` exposes `operation_id`, `category`
|
|
34
|
+
(`invalid_json` or `invalid_event_schema`) and `validation_errors`. Structured
|
|
35
|
+
details exclude input values and validator context. Keep `.data`, exception
|
|
36
|
+
chaining and the existing constructor; raw data and chained causes can contain
|
|
37
|
+
sensitive values and are never logged automatically by the SDK.
|
|
38
|
+
- Add request/diagnostic/error regression tests for both Python HTTP backends and
|
|
39
|
+
installed-wheel consumers, plus a documented validation support matrix.
|
|
40
|
+
|
|
41
|
+
### Upgrade from 0.2.0
|
|
42
|
+
|
|
43
|
+
1. Upgrade the generator to 0.3.0 and regenerate with your existing HTTP backend.
|
|
44
|
+
2. Validate consumer call sites, including invalid requests, before adoption.
|
|
45
|
+
Ordinary Pydantic coercion applies; valid numeric serialization is preserved.
|
|
46
|
+
3. Generate JSON diagnostics and review exact allowances before enabling strict
|
|
47
|
+
CI. Changed locations, constraints, values, operations or targets need review.
|
|
48
|
+
4. Read stream failure categories and structured details through public fields.
|
|
49
|
+
Keep raw payloads and full exception chains out of routine logs.
|
|
50
|
+
|
|
51
|
+
## 0.2.0 - 2026-10-18
|
|
52
|
+
|
|
53
|
+
- Source preparation passed workspace CI, independent wheel matrices and the
|
|
54
|
+
package release dry run. See the [release preparation evidence](../../docs/releases.md#verified-follow-up-preparation-2026-10-18).
|
|
55
|
+
|
|
56
|
+
### Added
|
|
57
|
+
|
|
58
|
+
- Preserve supported string patterns/lengths and numeric bounds/multiples in the
|
|
59
|
+
codegen IR and generated Python Pydantic annotations, including nested and
|
|
60
|
+
referenced scalars. Diagnose unsupported constraints and regex expressions.
|
|
61
|
+
- Add `RequestOptions(unknown_events="ignore")` to generated Python SDKs. It
|
|
62
|
+
skips unknown string tags in a top-level discriminated event union; the default
|
|
63
|
+
remains an error. Malformed known events, missing tags and invalid JSON fail.
|
|
64
|
+
Both HTTPX backends retain their validation and transport behavior.
|
|
65
|
+
- Add tested contract-first native proxy and private OpenAPI 3.1 REST-codegen
|
|
66
|
+
recipes. The authoritative OpenAPI 3.2 artifact remains unchanged.
|
|
67
|
+
|
|
68
|
+
### Breaking generated-code changes
|
|
69
|
+
|
|
70
|
+
- Regenerated Python models enforce scalar rules that 0.1.0 silently dropped.
|
|
71
|
+
Fix invalid payloads before adopting the new SDK. Unsupported constraints emit
|
|
72
|
+
`UnsupportedConstraintWarning`; constraints alongside enum/const/composition
|
|
73
|
+
and unsupported Python regex patterns fail explicitly. Move supported scalar
|
|
74
|
+
constraints onto scalar branches where possible. This is not full JSON Schema
|
|
75
|
+
validation. TypeScript remains static-only.
|
|
76
|
+
- Regenerate committed SDKs and run consumer validation tests. Pass HTTPX2
|
|
77
|
+
generation/check options consistently if used. No wire names change.
|
|
78
|
+
|
|
79
|
+
### Upgrade from 0.1.0
|
|
80
|
+
|
|
81
|
+
1. Update `fastapi-streams[codegen]` to 0.2.0 and regenerate both SDKs from the
|
|
82
|
+
authoritative OpenAPI document. Keep the selected Python HTTP backend on
|
|
83
|
+
generation and check commands.
|
|
84
|
+
2. Fix data violating preserved scalar constraints and review all unsupported-rule
|
|
85
|
+
diagnostics. Re-run consuming client validation and stream tests.
|
|
86
|
+
3. Keep the default unknown-event error policy, or opt in per request only where
|
|
87
|
+
the upstream intentionally emits additional top-level discriminator values.
|
|
88
|
+
|
|
89
|
+
### Release verification
|
|
90
|
+
|
|
91
|
+
- Extend installed-wheel HTTPX/HTTPX2 smoke consumers to verify scalar constraints,
|
|
92
|
+
default unknown-event rejection, opt-in skipping and malformed known events.
|
|
93
|
+
|
|
94
|
+
### Documentation
|
|
95
|
+
|
|
96
|
+
- Fix PyPI installation instructions and supported-scope wording. Replace the
|
|
97
|
+
predecessor release guide with the workspace release process.
|
|
98
|
+
|
|
6
99
|
## 0.1.0 - 2026-10-17
|
|
7
100
|
|
|
8
101
|
### Added
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: fastapi-streams
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.3.0
|
|
4
4
|
Summary: Typed FastAPI streams with generated TypeScript and Python clients.
|
|
5
5
|
Project-URL: Documentation, https://github.com/mathisarends/canon/tree/main/packages/fastapi-streams/docs
|
|
6
6
|
Project-URL: Source, https://github.com/mathisarends/canon/tree/main/packages/fastapi-streams
|
|
@@ -45,10 +45,10 @@ A CI check catches drift between the Python app, the contract, and generated cod
|
|
|
45
45
|
|
|
46
46
|
## 1. Decorate your FastAPI endpoint
|
|
47
47
|
|
|
48
|
-
Install from
|
|
48
|
+
Install from PyPI with the optional codegen extra:
|
|
49
49
|
|
|
50
50
|
```sh
|
|
51
|
-
uv add 'fastapi-streams[codegen]
|
|
51
|
+
uv add 'fastapi-streams[codegen]'
|
|
52
52
|
```
|
|
53
53
|
|
|
54
54
|
Save this as `app.py`:
|
|
@@ -236,7 +236,8 @@ and regenerating committed clients.
|
|
|
236
236
|
| [Development](docs/development.md) | Tests, supported scope, and roadmap. |
|
|
237
237
|
| [Releasing](docs/releasing.md) | Tag-based releases and credential-free PyPI publishing. |
|
|
238
238
|
|
|
239
|
-
|
|
240
|
-
contract for your existing REST SDK. See the
|
|
239
|
+
The current release supports GET/POST JSON SSE streams. Ordinary REST endpoints
|
|
240
|
+
stay in the contract for your existing REST SDK. See the
|
|
241
|
+
[supported scope](docs/development.md#supported-scope-and-roadmap).
|
|
241
242
|
|
|
242
243
|
Python 3.12+ · FastAPI 0.143.3+ · Pydantic v2 · [MIT](LICENSE)
|
|
@@ -27,10 +27,10 @@ A CI check catches drift between the Python app, the contract, and generated cod
|
|
|
27
27
|
|
|
28
28
|
## 1. Decorate your FastAPI endpoint
|
|
29
29
|
|
|
30
|
-
Install from
|
|
30
|
+
Install from PyPI with the optional codegen extra:
|
|
31
31
|
|
|
32
32
|
```sh
|
|
33
|
-
uv add 'fastapi-streams[codegen]
|
|
33
|
+
uv add 'fastapi-streams[codegen]'
|
|
34
34
|
```
|
|
35
35
|
|
|
36
36
|
Save this as `app.py`:
|
|
@@ -218,7 +218,8 @@ and regenerating committed clients.
|
|
|
218
218
|
| [Development](docs/development.md) | Tests, supported scope, and roadmap. |
|
|
219
219
|
| [Releasing](docs/releasing.md) | Tag-based releases and credential-free PyPI publishing. |
|
|
220
220
|
|
|
221
|
-
|
|
222
|
-
contract for your existing REST SDK. See the
|
|
221
|
+
The current release supports GET/POST JSON SSE streams. Ordinary REST endpoints
|
|
222
|
+
stay in the contract for your existing REST SDK. See the
|
|
223
|
+
[supported scope](docs/development.md#supported-scope-and-roadmap).
|
|
223
224
|
|
|
224
225
|
Python 3.12+ · FastAPI 0.143.3+ · Pydantic v2 · [MIT](LICENSE)
|
|
@@ -7,9 +7,10 @@ TypeScript Fetch and Python HTTPX clients from the same definitions.
|
|
|
7
7
|
| --- | --- |
|
|
8
8
|
| [Quickstart](quickstart.md) | Run the complete example and define your first stream. |
|
|
9
9
|
| [Contracts and codegen](codegen.md) | Export OpenAPI, generate both clients, and detect drift in CI. |
|
|
10
|
+
| [Native transports](native-transports.md) | Existing SSE proxies, contract-first generation and private REST codegen views. |
|
|
10
11
|
| [Client usage](clients.md) | Named event values, typed iteration, authentication, cancellation, and errors. |
|
|
11
12
|
| [Development](development.md) | Local checks, test coverage, supported features, and roadmap. |
|
|
12
|
-
| [Releasing](releasing.md) |
|
|
13
|
+
| [Releasing](releasing.md) | Workspace release preparation, PyPI publishing, and recovery. |
|
|
13
14
|
| [Architecture](architecture.md) | Server integration, shared IR, generation, and transport boundaries. |
|
|
14
15
|
|
|
15
16
|
Start with the [quickstart](quickstart.md), then use the
|
|
@@ -121,6 +121,38 @@ remain ordinary members of the user-defined union.
|
|
|
121
121
|
|
|
122
122
|
## HTTP error contracts
|
|
123
123
|
|
|
124
|
+
Python request parameters and JSON bodies are validated when calling the method;
|
|
125
|
+
catch Pydantic `ValidationError` around that call. Network and event errors occur
|
|
126
|
+
while iterating. See the [validation matrix and CI policy](codegen.md#validation-support-matrix).
|
|
127
|
+
|
|
128
|
+
Generated Python `StreamParseError` provides these public fields:
|
|
129
|
+
|
|
130
|
+
| Field | Meaning |
|
|
131
|
+
| --- | --- |
|
|
132
|
+
| `operation_id` | Source OpenAPI operation ID |
|
|
133
|
+
| `category` | `invalid_json` for malformed JSON; `invalid_event_schema` for invalid event shapes/constraints |
|
|
134
|
+
| `validation_errors` | Pydantic error dictionaries with `type`, `loc`, `msg`; input values, context and URLs are excluded |
|
|
135
|
+
| `data` | Original SSE data payload, retained for explicit inspection |
|
|
136
|
+
|
|
137
|
+
For errors raised by generated operations, category and operation ID are always
|
|
138
|
+
set. The existing `StreamParseError(data)` constructor remains valid; manually
|
|
139
|
+
constructed errors can have `None` provenance/category and empty details.
|
|
140
|
+
The original Pydantic `ValidationError` remains chained as `__cause__`.
|
|
141
|
+
|
|
142
|
+
```python
|
|
143
|
+
try:
|
|
144
|
+
async for event in client.runs.run_events(run_id="run_abc"):
|
|
145
|
+
handle(event)
|
|
146
|
+
except StreamParseError as error:
|
|
147
|
+
record_failure(error.operation_id, error.category, error.validation_errors)
|
|
148
|
+
```
|
|
149
|
+
|
|
150
|
+
The SDK does not log event payloads or exceptions automatically. Raw `.data` and
|
|
151
|
+
the full chained exception may contain sensitive values; log only reviewed fields.
|
|
152
|
+
Even structured messages and field names should follow the consumer's logging
|
|
153
|
+
policy. Malformed known events still fail with `unknown_events="ignore"`; only
|
|
154
|
+
valid JSON objects with unknown string discriminator tags can be skipped.
|
|
155
|
+
|
|
124
156
|
The SDK works with ordinary OpenAPI response declarations, including
|
|
125
157
|
`application/json`, `application/problem+json`, and other declared response media
|
|
126
158
|
types. No error registry or particular problem envelope is required. Declare
|
|
@@ -0,0 +1,232 @@
|
|
|
1
|
+
# Contracts and client generation
|
|
2
|
+
|
|
3
|
+
[Documentation](README.md) · [Project](../README.md)
|
|
4
|
+
|
|
5
|
+
## Export and generate
|
|
6
|
+
|
|
7
|
+
```sh
|
|
8
|
+
uv run fastapi-streams schema \
|
|
9
|
+
--app examples.agent_runs:app --output tests/fixtures/agent_runs.openapi.json
|
|
10
|
+
|
|
11
|
+
uv run fastapi-streams generate \
|
|
12
|
+
--schema tests/fixtures/agent_runs.openapi.json --language typescript --output frontend/generated
|
|
13
|
+
|
|
14
|
+
uv run fastapi-streams generate \
|
|
15
|
+
--schema tests/fixtures/agent_runs.openapi.json --language python --output myapp/generated
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
No running server is needed. Export imports the application. The codegen extra
|
|
19
|
+
adds Jinja and JSON Schema validation; importing the server package needs neither.
|
|
20
|
+
|
|
21
|
+
The exporter copies native FastAPI output, promotes compatible 3.1 output to
|
|
22
|
+
OpenAPI 3.2.0, updates the declared OAS dialect when necessary, and validates the
|
|
23
|
+
official bundled OpenAPI structural schema, Draft 2020-12 Schema Objects and
|
|
24
|
+
local references. Incompatible contracts fail explicitly. The application's
|
|
25
|
+
cached document and ordinary REST endpoints remain intact.
|
|
26
|
+
|
|
27
|
+
The generator extracts application data from
|
|
28
|
+
`itemSchema.properties.data.contentSchema`. Only stream operations and models
|
|
29
|
+
reachable from their parameters, bodies, events and error responses are emitted.
|
|
30
|
+
Operations use the first tag as their group, defaulting to `streams`.
|
|
31
|
+
|
|
32
|
+
Generated method documentation carries each operation's `summary`, `description`,
|
|
33
|
+
and `deprecated` marker alongside language-specific SDK behavior. Describe
|
|
34
|
+
replay, retention, and server job lifetime explicitly on your endpoint; the
|
|
35
|
+
generator does not infer these business guarantees. Python early exits should
|
|
36
|
+
use `contextlib.aclosing`; TypeScript `for await` cleanup closes its reader.
|
|
37
|
+
|
|
38
|
+
TypeScript has no runtime dependencies and uses Fetch-compatible APIs. The
|
|
39
|
+
example uses ES modules and is tested on Node 24. Generated Python packages
|
|
40
|
+
require Python 3.12+, `httpx>=0.28` and `pydantic>=2.11.2` (`uv add httpx pydantic`).
|
|
41
|
+
|
|
42
|
+
HTTPX is the default. To generate a Python client using HTTPX2 instead, opt in
|
|
43
|
+
when generating and checking it:
|
|
44
|
+
|
|
45
|
+
```sh
|
|
46
|
+
uv run fastapi-streams generate \
|
|
47
|
+
--schema tests/fixtures/agent_runs.openapi.json --language python \
|
|
48
|
+
--python-http-client httpx2 --output myapp/generated
|
|
49
|
+
|
|
50
|
+
uv run fastapi-streams check \
|
|
51
|
+
--schema tests/fixtures/agent_runs.openapi.json --language python \
|
|
52
|
+
--python-http-client httpx2 --output myapp/generated
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
Install `httpx2>=2.13.1,<3` and `pydantic>=2.11.2` in that consumer. The
|
|
56
|
+
generated package needs only the selected HTTP library; installing HTTPX2 does
|
|
57
|
+
not change an existing HTTPX client. The Python generator API accepts the same
|
|
58
|
+
choice as `generate_python(contract, http_client="httpx2")`. Pass the choice to
|
|
59
|
+
every `check` or `generate --check` call; omitting it checks HTTPX output.
|
|
60
|
+
`--python-http-client` requires `--language python`.
|
|
61
|
+
|
|
62
|
+
REST operations remain available in the contract for an existing SDK generator
|
|
63
|
+
such as Orval or HeyAPI. Keep REST and streaming SDKs in separate output
|
|
64
|
+
directories. Reusing external generated model imports is a future feature.
|
|
65
|
+
|
|
66
|
+
## Event discriminators
|
|
67
|
+
|
|
68
|
+
A Python default allows omitting a field when constructing a model. It does not
|
|
69
|
+
automatically make the field required in JSON Schema. The generator respects
|
|
70
|
+
`required` and warns with `OptionalDiscriminatorWarning` when a stream event
|
|
71
|
+
union uses an optional discriminator, including nested unions. An absent tag may
|
|
72
|
+
prevent variant selection; TypeScript's static types do not validate incoming JSON.
|
|
73
|
+
|
|
74
|
+
For **output-only** event models whose serializer always emits default fields,
|
|
75
|
+
configure the output schema to describe that behavior:
|
|
76
|
+
|
|
77
|
+
```python
|
|
78
|
+
from pydantic import BaseModel, ConfigDict
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
class StreamEvent(BaseModel):
|
|
82
|
+
model_config = ConfigDict(
|
|
83
|
+
json_schema_mode_override="serialization",
|
|
84
|
+
json_schema_serialization_defaults_required=True,
|
|
85
|
+
)
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
Inherit event variants from this model, or apply the configuration to an existing
|
|
89
|
+
output-only base. It marks all serialized default fields required, not only the
|
|
90
|
+
discriminator. Avoid applying it globally to shared request models: input and
|
|
91
|
+
output requiredness can differ. Alternatively, declare the tag without a default
|
|
92
|
+
and pass it explicitly. Regenerate the schema and clients after changing models.
|
|
93
|
+
|
|
94
|
+
Warnings are advisory and do not change fields or defaults. Python callers can
|
|
95
|
+
filter or promote this category using the standard `warnings` module; the category
|
|
96
|
+
is available from `fastapi_streams.codegen.diagnostics`.
|
|
97
|
+
|
|
98
|
+
## Check drift in CI
|
|
99
|
+
|
|
100
|
+
```sh
|
|
101
|
+
uv run fastapi-streams check \
|
|
102
|
+
--app examples.agent_runs:app --schema tests/fixtures/agent_runs.openapi.json \
|
|
103
|
+
--language typescript --output examples/agent_runs/generated
|
|
104
|
+
|
|
105
|
+
uv run fastapi-streams check \
|
|
106
|
+
--app examples.agent_runs:app --schema tests/fixtures/agent_runs.openapi.json \
|
|
107
|
+
--language python --output examples/agent_runs/generated_python
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
`check` imports the app, compares its contract, validates the stored schema and
|
|
111
|
+
compares generated files without writing. Omit `--app` for a contract supplied
|
|
112
|
+
by another service. `schema --check` and `generate --check` are also supported.
|
|
113
|
+
Exit codes: **0** current, **1** drift, **2** invalid input.
|
|
114
|
+
|
|
115
|
+
Generation is deterministic. A manifest tracks owned files and cleans up stale
|
|
116
|
+
generated files without deleting neighboring handwritten files. Use a dedicated
|
|
117
|
+
output directory for each language. Unsafe paths and generated-name collisions
|
|
118
|
+
fail explicitly.
|
|
119
|
+
|
|
120
|
+
## Runtime validation and unknown variants
|
|
121
|
+
|
|
122
|
+
Python validates path, query and header parameters and JSON request bodies at the
|
|
123
|
+
method call, before serialization or HTTP I/O. Invalid arguments raise Pydantic
|
|
124
|
+
`ValidationError` immediately, even if the returned iterator is never consumed.
|
|
125
|
+
HTTP starts when iteration begins. Optional parameters supplied as `None` are
|
|
126
|
+
omitted, preserving existing omission semantics. Pydantic's normal coercion
|
|
127
|
+
applies; this is not Pydantic strict mode. Valid scalar encoding, integer values
|
|
128
|
+
of number parameters (`0` stays `0`), query array explosion and body aliases are
|
|
129
|
+
preserved. Adapters are cached in each generated API class.
|
|
130
|
+
|
|
131
|
+
Python validates generated event models and request bodies with Pydantic. Supported
|
|
132
|
+
scalar constraints are `pattern`, `minLength`, `maxLength`, `minimum`, `maximum`,
|
|
133
|
+
`exclusiveMinimum`, `exclusiveMaximum`, and `multipleOf`. They survive nested
|
|
134
|
+
models, arrays, nullable types and referenced scalar aliases. Patterns use
|
|
135
|
+
Pydantic's Rust regex engine; unsupported expressions fail generation. Numeric
|
|
136
|
+
multiples follow Pydantic's floating-point tolerance. This is not a complete JSON
|
|
137
|
+
Schema validator. Formats, collection/object limits and other unsupported rules
|
|
138
|
+
emit `UnsupportedConstraintWarning`; promote it to an error in CI when required.
|
|
139
|
+
Constraints alongside enum/const or composition fail explicitly; constraints on
|
|
140
|
+
scalar branches are supported. TypeScript provides static shapes and does not
|
|
141
|
+
validate incoming JSON or enforce scalar constraints at runtime.
|
|
142
|
+
|
|
143
|
+
Python event unions are closed by default: unknown variants raise
|
|
144
|
+
`StreamParseError`. For an upstream that emits more variants than your declared
|
|
145
|
+
subset, explicitly choose a per-request policy:
|
|
146
|
+
|
|
147
|
+
```python
|
|
148
|
+
from generated import RequestOptions
|
|
149
|
+
|
|
150
|
+
options = RequestOptions(unknown_events="ignore")
|
|
151
|
+
async for event in client.runs.run_events(run_id="run_abc", options=options):
|
|
152
|
+
...
|
|
153
|
+
```
|
|
154
|
+
|
|
155
|
+
Ignoring requires a top-level discriminated union with known string tags. Only
|
|
156
|
+
valid JSON objects with an unknown string discriminator are skipped. Invalid
|
|
157
|
+
JSON, missing/non-string discriminators and malformed known variants still fail;
|
|
158
|
+
unknown nested variants inside a known event fail too. The option adds no retry,
|
|
159
|
+
reconnect or replay. TypeScript continues to forward JSON-decoded values without
|
|
160
|
+
runtime union validation. Regenerate existing Python SDKs to obtain the option.
|
|
161
|
+
|
|
162
|
+
## Validation support matrix
|
|
163
|
+
|
|
164
|
+
| SDK target / boundary | Runtime shape validation | Supported scalar constraints | Timing |
|
|
165
|
+
| --- | --- | --- | --- |
|
|
166
|
+
| Python path/query/header | Yes | Yes, including referenced scalars and query array items | Method call, before serialization |
|
|
167
|
+
| Python JSON request body | Yes | Yes, including nested values | Method call |
|
|
168
|
+
| Python SSE event data | Yes | Yes, including nested values | Each event during iteration |
|
|
169
|
+
| Python declared HTTP error bodies | Explicit consumer TypeAdapter | When explicitly validated | Consumer-controlled |
|
|
170
|
+
| TypeScript parameters/body/events | Static types; events are JSON-decoded | No runtime enforcement | Generation diagnoses limitations |
|
|
171
|
+
|
|
172
|
+
Unsupported formats and collection/object constraints remain diagnostics rather
|
|
173
|
+
than implicit runtime validators. Optional `None` query/header parameters mean
|
|
174
|
+
omission, not JSON null. Server-side validation remains necessary for every client.
|
|
175
|
+
|
|
176
|
+
## Source diagnostics and CI policy
|
|
177
|
+
|
|
178
|
+
`generate`, `generate --check` and `check` accept these options:
|
|
179
|
+
|
|
180
|
+
```sh
|
|
181
|
+
uv run fastapi-streams check --schema openapi.json --language python \
|
|
182
|
+
--output generated --diagnostics reports/python.json --strict \
|
|
183
|
+
--allow-diagnostics reviewed-python.json
|
|
184
|
+
```
|
|
185
|
+
|
|
186
|
+
Omit `--allow-diagnostics` when no limitations are accepted. The JSON report
|
|
187
|
+
(`version: 1`, `diagnostics: [...]`) includes each diagnostic's `id`, `code`,
|
|
188
|
+
`pointer`, `keyword`, `value`, `target`, `operation_id`, `method`, `path`, `message`
|
|
189
|
+
and `accepted` status. Pointers are RFC 6901 paths into the original document,
|
|
190
|
+
including inline schemas and referenced component definitions. Shared definitions
|
|
191
|
+
identify each affected operation; unused components do not generate warnings.
|
|
192
|
+
SSE framing metadata is outside generated event-model validation.
|
|
193
|
+
|
|
194
|
+
`unsupported-constraint` identifies an unenforced rule for the selected target;
|
|
195
|
+
TypeScript also diagnoses Python-supported scalar constraints because TypeScript
|
|
196
|
+
has no runtime schema validator. Python's `optional-discriminator` diagnoses event
|
|
197
|
+
selection risks without changing field requiredness. Human-readable warnings
|
|
198
|
+
contain the same location, keyword/value, operation and limitation as the report.
|
|
199
|
+
|
|
200
|
+
Without `--strict`, unaccepted diagnostics remain warnings. With it, any
|
|
201
|
+
unaccepted diagnostic fails with exit code 2 before generated files change; drift
|
|
202
|
+
still uses exit code 1. Reports are written even on strict-policy failure. Keep
|
|
203
|
+
the report outside generated output and separate from schema/allowance inputs.
|
|
204
|
+
|
|
205
|
+
Review the report and explicitly record an allowance, for example:
|
|
206
|
+
|
|
207
|
+
```json
|
|
208
|
+
{
|
|
209
|
+
"allow": [
|
|
210
|
+
{
|
|
211
|
+
"id": "unsupported-constraint:<copy the exact ID from the report>",
|
|
212
|
+
"reason": "Reviewed upstream UUID validation; client remains a string."
|
|
213
|
+
}
|
|
214
|
+
]
|
|
215
|
+
}
|
|
216
|
+
```
|
|
217
|
+
|
|
218
|
+
IDs include code, source pointer, keyword/value, target and operation context;
|
|
219
|
+
message wording and schema filename are excluded. Changed constraints or contexts
|
|
220
|
+
need a new review. Empty reasons, duplicate IDs, malformed files and stale
|
|
221
|
+
allowances fail explicitly. Keep separate policies for each contract/target.
|
|
222
|
+
Repository drift checks use strict mode with reviewed TypeScript allowances.
|
|
223
|
+
|
|
224
|
+
Python generator API users can collect the same objects with
|
|
225
|
+
`build_ir(document, target="python", diagnostics=items)` where `items` is an empty
|
|
226
|
+
list. Omitting `diagnostics` emits warnings; the default target is Python. Select
|
|
227
|
+
the matching target explicitly before calling the corresponding generator.
|
|
228
|
+
|
|
229
|
+
## Existing native streams and mixed REST generation
|
|
230
|
+
|
|
231
|
+
See the [native transport and mixed-codegen recipe](native-transports.md) when
|
|
232
|
+
an existing generator, native server or byte-preserving proxy owns the stream.
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
# Native SSE transports and mixed client generation
|
|
2
|
+
|
|
3
|
+
An authoritative OpenAPI 3.2 contract can describe an existing SSE endpoint.
|
|
4
|
+
`fastapi-streams generate --schema` does not require `StreamRouter`, a running
|
|
5
|
+
server or replacement of the application's stream implementation.
|
|
6
|
+
|
|
7
|
+
The executable [native proxy example](../examples/native_proxy.py) declares an
|
|
8
|
+
explicit event union through `itemSchema.properties.data.contentSchema` and
|
|
9
|
+
registers its referenced models. Its `StreamingResponse` forwards upstream raw
|
|
10
|
+
bytes, status and selected response headers. Keepalives and unknown event frames
|
|
11
|
+
are not decoded or reserialized. The response closes the upstream connection;
|
|
12
|
+
the FastAPI lifespan owns the HTTP client. Application authentication, allowed
|
|
13
|
+
headers, run identity, recovery and replay remain application responsibilities.
|
|
14
|
+
The declared 200 contract must match the reviewed upstream; other upstream status
|
|
15
|
+
codes are preserved and generated SDKs apply their usual HTTP error handling.
|
|
16
|
+
|
|
17
|
+
From this package directory:
|
|
18
|
+
|
|
19
|
+
```console
|
|
20
|
+
uv run fastapi-streams schema --app examples.native_proxy:app --output openapi.json
|
|
21
|
+
uv run fastapi-streams generate --schema openapi.json --language python --python-http-client httpx2 --output generated/sse
|
|
22
|
+
uv run fastapi-streams check --schema openapi.json --language python --python-http-client httpx2 --output generated/sse
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
Keep `openapi.json` as the single authoritative artifact for REST and SSE. If an
|
|
26
|
+
existing REST generator accepts only OpenAPI 3.1, derive a private codegen view:
|
|
27
|
+
|
|
28
|
+
```console
|
|
29
|
+
uv run python -m examples.rest_codegen openapi.json build/rest.openapi.json
|
|
30
|
+
uv run python -m examples.rest_codegen openapi.json build/rest.openapi.json --check
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
The [adaptation example](../examples/rest_codegen.py) deep-copies the contract,
|
|
34
|
+
sets the private view to 3.1 and replaces each inline JSON SSE `itemSchema` with
|
|
35
|
+
a media `schema` referencing decoded event data. This keeps event models reachable
|
|
36
|
+
for REST generators that use reachability or schema-tag filtering. That schema
|
|
37
|
+
is a private model-discovery aid, not the SSE wire body. Filter SSE operations out
|
|
38
|
+
of ordinary HTTP method generation and configure your generator's model/tag
|
|
39
|
+
selection explicitly. All components and REST operations remain available.
|
|
40
|
+
|
|
41
|
+
This recipe handles ordinary REST operations and inline JSON SSE envelopes. It is
|
|
42
|
+
not a general OpenAPI 3.2 downgrade: review additional 3.2 features against the
|
|
43
|
+
chosen REST generator. Unsupported SSE envelope shapes fail explicitly. Never
|
|
44
|
+
publish the private view as the service contract or edit it by hand. Generate it
|
|
45
|
+
from the authoritative artifact before REST generation/drift checks; generate
|
|
46
|
+
and check the SSE SDK directly from the authoritative artifact. Keep HTTP and
|
|
47
|
+
SSE outputs in separate directories. Run both clients' consumer tests.
|
|
48
|
+
|
|
49
|
+
[Back to code generation](codegen.md)
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
# Releasing fastapi-streams
|
|
2
|
+
|
|
3
|
+
Use the [workspace release process](../../../docs/releases.md). Only root
|
|
4
|
+
workflows execute in this repository; nested workflows and the predecessor
|
|
5
|
+
package-local release script are historical configuration.
|
|
6
|
+
|
|
7
|
+
Releases use independent `fastapi-streams-vX.Y.Z` tags and the root lockfile.
|
|
8
|
+
Prepare a new version, update the changelog and generated clients, pass the
|
|
9
|
+
workspace and installed-artifact checks, and wait for CI on the exact commit.
|
|
10
|
+
Run the root release workflow with `dry_run=true` before publication. A real
|
|
11
|
+
publication requires explicit authorization.
|
|
12
|
+
|
|
13
|
+
[Back to documentation](README.md)
|
|
@@ -30,7 +30,7 @@ async def consume_with_errors(client: Client, run_id: str) -> None:
|
|
|
30
30
|
print("Unexpected HTTP response", error.status, error.details)
|
|
31
31
|
raise
|
|
32
32
|
except StreamParseError as error:
|
|
33
|
-
print("Invalid event data", error.
|
|
33
|
+
print("Invalid event data", error.operation_id, error.category)
|
|
34
34
|
raise
|
|
35
35
|
except TransportError as error:
|
|
36
36
|
print("Stream transport failed", str(error))
|
|
@@ -6,10 +6,17 @@ from collections.abc import AsyncGenerator
|
|
|
6
6
|
from pydantic import TypeAdapter
|
|
7
7
|
|
|
8
8
|
from .. import models as Models
|
|
9
|
-
from ..runtime import RequestOptions, Transport, append_query, encode_path
|
|
9
|
+
from ..runtime import RequestOptions, Transport, append_query, encode_path, validate_parameter as _validate_parameter
|
|
10
10
|
|
|
11
11
|
|
|
12
12
|
class AgentRunsApi:
|
|
13
|
+
_stream_prompt_run_id_adapter = TypeAdapter(str)
|
|
14
|
+
_stream_prompt_body_adapter = TypeAdapter(Models.RunRequest)
|
|
15
|
+
_stream_prompt_event_adapter = TypeAdapter(Models.RunEvent)
|
|
16
|
+
_stream_run_events_run_id_adapter = TypeAdapter(str)
|
|
17
|
+
_stream_run_events_delay_adapter = TypeAdapter(Models.StreamRunEventsDelay)
|
|
18
|
+
_stream_run_events_event_adapter = TypeAdapter(Models.RunEvent)
|
|
19
|
+
|
|
13
20
|
def __init__(self, transport: Transport) -> None:
|
|
14
21
|
self._transport = transport
|
|
15
22
|
|
|
@@ -33,18 +40,26 @@ class AgentRunsApi:
|
|
|
33
40
|
|
|
34
41
|
Declared HTTP response bodies: StreamPromptErrorBody (422 application/json). HttpError.details may also contain undeclared JSON, text, or no body; validate it before accessing application fields.
|
|
35
42
|
|
|
43
|
+
Path, query and header parameters and JSON bodies are validated with Pydantic at the method call, before serialization or HTTP I/O. Optional None parameters are omitted. Pydantic's normal type coercion applies; invalid arguments raise ValidationError.
|
|
44
|
+
|
|
36
45
|
Use contextlib.aclosing for deterministic cleanup when exiting iteration early. Task cancellation closes the response and propagates normally.
|
|
37
46
|
|
|
38
|
-
Events are validated with Pydantic.
|
|
47
|
+
Events are validated with Pydantic against generated shapes, variants and supported scalar constraints (string pattern/length, numeric bounds/multiples). Unsupported source constraints are diagnosed during generation; this is not full JSON Schema validation.
|
|
48
|
+
|
|
49
|
+
Unknown event variants fail by default. For a top-level discriminated union, RequestOptions(unknown_events='ignore') skips unknown string tags. Malformed known events, missing tags and invalid JSON still fail.
|
|
50
|
+
|
|
51
|
+
StreamParseError exposes operation_id, category (invalid_json or invalid_event_schema), and validation_errors without input values. Its data and chained cause may contain sensitive content; the SDK does not log them automatically.
|
|
39
52
|
"""
|
|
53
|
+
run_id = _validate_parameter(self._stream_prompt_run_id_adapter, run_id)
|
|
40
54
|
query: list[tuple[str, str]] = []
|
|
41
55
|
headers: dict[str, str] = {}
|
|
42
|
-
adapter =
|
|
56
|
+
adapter = self._stream_prompt_body_adapter
|
|
43
57
|
content = adapter.dump_json(adapter.validate_python(body), by_alias=True, exclude_unset=True)
|
|
44
58
|
return self._transport.stream(
|
|
45
59
|
'/v1/runs/' + encode_path(run_id) + '/events',
|
|
46
60
|
'POST',
|
|
47
|
-
|
|
61
|
+
self._stream_prompt_event_adapter,
|
|
62
|
+
operation_id='stream_prompt',
|
|
48
63
|
query=query,
|
|
49
64
|
headers=headers,
|
|
50
65
|
body=content,
|
|
@@ -55,7 +70,7 @@ class AgentRunsApi:
|
|
|
55
70
|
self,
|
|
56
71
|
*,
|
|
57
72
|
run_id: str,
|
|
58
|
-
delay:
|
|
73
|
+
delay: Models.StreamRunEventsDelay | None = None,
|
|
59
74
|
options: RequestOptions | None = None,
|
|
60
75
|
) -> AsyncGenerator[Models.RunEvent, None]:
|
|
61
76
|
"""
|
|
@@ -71,17 +86,27 @@ class AgentRunsApi:
|
|
|
71
86
|
|
|
72
87
|
Declared HTTP response bodies: StreamRunEventsErrorBody (422 application/json). HttpError.details may also contain undeclared JSON, text, or no body; validate it before accessing application fields.
|
|
73
88
|
|
|
89
|
+
Path, query and header parameters and JSON bodies are validated with Pydantic at the method call, before serialization or HTTP I/O. Optional None parameters are omitted. Pydantic's normal type coercion applies; invalid arguments raise ValidationError.
|
|
90
|
+
|
|
74
91
|
Use contextlib.aclosing for deterministic cleanup when exiting iteration early. Task cancellation closes the response and propagates normally.
|
|
75
92
|
|
|
76
|
-
Events are validated with Pydantic.
|
|
93
|
+
Events are validated with Pydantic against generated shapes, variants and supported scalar constraints (string pattern/length, numeric bounds/multiples). Unsupported source constraints are diagnosed during generation; this is not full JSON Schema validation.
|
|
94
|
+
|
|
95
|
+
Unknown event variants fail by default. For a top-level discriminated union, RequestOptions(unknown_events='ignore') skips unknown string tags. Malformed known events, missing tags and invalid JSON still fail.
|
|
96
|
+
|
|
97
|
+
StreamParseError exposes operation_id, category (invalid_json or invalid_event_schema), and validation_errors without input values. Its data and chained cause may contain sensitive content; the SDK does not log them automatically.
|
|
77
98
|
"""
|
|
99
|
+
run_id = _validate_parameter(self._stream_run_events_run_id_adapter, run_id)
|
|
100
|
+
if delay is not None:
|
|
101
|
+
delay = _validate_parameter(self._stream_run_events_delay_adapter, delay)
|
|
78
102
|
query: list[tuple[str, str]] = []
|
|
79
103
|
headers: dict[str, str] = {}
|
|
80
104
|
append_query(query, 'delay', delay, explode=True)
|
|
81
105
|
return self._transport.stream(
|
|
82
106
|
'/v1/runs/' + encode_path(run_id) + '/events',
|
|
83
107
|
'GET',
|
|
84
|
-
|
|
108
|
+
self._stream_run_events_event_adapter,
|
|
109
|
+
operation_id='stream_run_events',
|
|
85
110
|
query=query,
|
|
86
111
|
headers=headers,
|
|
87
112
|
options=options,
|
{fastapi_streams-0.1.0 → fastapi_streams-0.3.0}/examples/agent_runs/generated_python/models.py
RENAMED
|
@@ -38,7 +38,7 @@ class RunFailed(BaseModel):
|
|
|
38
38
|
|
|
39
39
|
class RunRequest(BaseModel):
|
|
40
40
|
model_config = ConfigDict(populate_by_name=True)
|
|
41
|
-
prompt: str
|
|
41
|
+
prompt: Annotated[str, Field(max_length=500, min_length=1)]
|
|
42
42
|
|
|
43
43
|
class RunStarted(BaseModel):
|
|
44
44
|
model_config = ConfigDict(populate_by_name=True)
|
|
@@ -49,6 +49,8 @@ type StreamPromptError = HTTPValidationError
|
|
|
49
49
|
|
|
50
50
|
type StreamPromptErrorBody = StreamPromptError
|
|
51
51
|
|
|
52
|
+
type StreamRunEventsDelay = Annotated[float, Field(le=1, ge=0)]
|
|
53
|
+
|
|
52
54
|
type StreamRunEventsError = HTTPValidationError
|
|
53
55
|
|
|
54
56
|
type StreamRunEventsErrorBody = StreamRunEventsError
|