|
1 | 1 | {"id":"openapi-generator-dpd","title":"Hybrid string-or-object discriminated unions deserialize-fail","description":"When an anyOf/oneOf union contains a discriminator AND a non-object branch (e.g. string-enum like ToolChoiceOptions), the generator emits a tagged enum that cannot deserialize the string form. Real-world hit: OpenAI ToolChoiceParam returns 'auto' in Response.tool_choice, but generated type is #[serde(tag=\"type\")] enum with no untagged String variant. Need to fall back to #[serde(untagged)] when the union mixes string/scalar branches with tagged-object branches, OR add a String fallback variant before the tagged variants.","notes":"Live repro: `ToolChoiceParam` from openai.yaml line 52518. anyOf has 8 branches; the first (`ToolChoiceOptions`) is a string-enum (\"none\"|\"auto\"|\"required\"), the rest are objects with discriminator propertyName=type. Generator emits `#[serde(tag=\"type\")] enum ToolChoiceParam { ToolChoiceOptions(ToolChoiceOptions), ... }` which cannot deserialize the string \"auto\" because serde tries to read a \"type\" field from a JSON string. Fix: when an anyOf/oneOf branch is a non-object schema (string/number/etc), the generator must emit `#[serde(untagged)]` with the scalar branch first OR add a String variant before the tagged variants. Hit on real OpenAI Responses API `Response.tool_choice` field.","status":"closed","priority":1,"issue_type":"bug","assignee":"James Lal","owner":"james@littlebearlabs.io","created_at":"2026-05-10T23:47:35Z","created_by":"James Lal","updated_at":"2026-05-11T00:12:46Z","started_at":"2026-05-10T23:54:10Z","closed_at":"2026-05-11T00:12:46Z","close_reason":"Fixed in src/analysis.rs: (dpd) analyze_oneof_union now downgrades to untagged when any branch is non-object — verified live against OpenAI Response.tool_choice='auto' which now deserializes as ToolChoiceParam::ToolChoiceOptions(Auto). (bgo) merge_schema_into_properties now ORs in is_nullable_pattern() for allOf-merged props — verified live against OpenAI Response.incomplete_details which is now Option\u003cResponseIncompleteDetails\u003e and deserializes null cleanly. All 4 smoke tests (OpenAI+Anthropic, sync+stream) pass.","dependency_count":0,"dependent_count":0,"comment_count":0} |
| 2 | +{"id":"openapi-generator-76h","title":"Add ByteStrategy::Base64UrlUnpadded for format: byte","description":"Add a new ByteStrategy variant emitting RFC 7515 §2 url-safe unpadded base64 in the inlined base64_serde helper. Needed by gpu-cli portal-schema work (JWK/DPoP thumbprints, sealing pubkeys) so format: byte fields can round-trip without falling back to plain String + spec-side documentation. Spec-global config knob, not per-field.","status":"closed","priority":2,"issue_type":"feature","assignee":"James Lal","owner":"james@littlebearlabs.io","created_at":"2026-05-16T17:58:36Z","created_by":"James Lal","updated_at":"2026-05-16T18:29:10Z","started_at":"2026-05-16T17:58:50Z","closed_at":"2026-05-16T18:29:10Z","close_reason":"Shipped on worktree-base64url-unpadded (commit afc8c88). 16 typed_scalars_test, 23 type_mapping unit tests, 51-binary cargo test suite all green; clippy --lib clean.","dependency_count":0,"dependent_count":0,"comment_count":0} |
2 | 3 | {"id":"openapi-generator-9ek","title":"[Server P6] Snapshot tests + integration smoke test against openai.yaml","description":"End-to-end validation of server codegen.\n\nSnapshot coverage:\n- createChatCompletion alone (canonical case, both non-streaming and SSE variants)\n- createEmbedding alone (single non-streaming op, different tag)\n- tag:Files (multi-op single tag)\n- Mixed: createChatCompletion + tag:Embeddings (multi-tag router)\n\nFor each: snapshot the full generated tree (server/api.rs, server/router.rs, server/errors.rs, models.rs).\n\nIntegration smoke test:\n- Compile a small consumer crate under tests/conformance/server/ that has a hand-written impl ChatApi for TestState returning canned responses.\n- Boot the router on a random port via tokio::spawn.\n- Hit POST /v1/chat/completions with a sample body, assert response shape.\n- Hit with stream=true, assert SSE event sequence.\n\nDoc updates:\n- README section 'Server codegen' with the 3-command DevX loop (list → add → generate).\n- Example in examples/openai_server.rs showing the user-side impl + main().","design":"Tests live alongside existing snapshot tests. Snapshots in src/snapshots/. Integration test uses reqwest (already a dep) to hit the in-process server.","acceptance_criteria":"- [ ] insta snapshots for all four scenarios above, reviewed and accepted.\n- [ ] Integration smoke test passes in CI.\n- [ ] Regenerating after a spec rename produces a clear error (manually tested).\n- [ ] README and example present.","notes":"Canonical test cases (per user direction 2026-05-10):\n- OpenAI Responses API: operationId 'createResponse' in specs/openai.yaml (POST /v1/responses). Streaming via stream:true in body.\n- Anthropic Messages API: operationId 'messages_post' in specs/anthropic.yaml (POST /v1/messages). Streaming via stream:true in body.\n\nBoth are the production drivers for server codegen — every phase should keep these two working as the bar for 'done'.","status":"open","priority":2,"issue_type":"task","owner":"james@littlebearlabs.io","created_at":"2026-05-11T01:00:24Z","created_by":"James Lal","updated_at":"2026-05-11T01:44:50Z","dependencies":[{"issue_id":"openapi-generator-9ek","depends_on_id":"openapi-generator-cdx","type":"blocks","created_at":"2026-05-10T19:00:37Z","created_by":"James Lal","metadata":"{}"}],"dependency_count":1,"dependent_count":1,"comment_count":0} |
3 | 4 | {"id":"openapi-generator-cdx","title":"[Server P5] Router factory + extractors + model pruning","description":"Wire the traits from P4 to an Axum Router and prune models.\n\nRouter factory (combined when ops span multiple tags):\n pub fn router\u003cC, E\u003e(chat: C, embeddings: E) -\u003e axum::Router\n where C: ChatApi + Clone, E: EmbeddingsApi + Clone, ...\n\nSingle tag collapses to single generic. Per-operation Axum handler wraps trait call, performs JSON/path/query extraction.\n\nExtractors:\n- BearerAuth (when securityScheme=bearer is used by picked ops)\n- ApiKeyHeader (when securityScheme=apiKey in header)\n- Typed wrappers for required headers/query/path parameters\n\nModel pruning:\n- Compute transitive reachability from picked operations' request/response types.\n- Emit only reachable schemas in models.rs (the rest are not generated for server even if client gen would emit them).\n- If client gen is ALSO enabled, models.rs is the union of both reachability sets.\n\nStreaming:\n- text/event-stream responses → variant carries Sse\u003cBoxStream\u003c'static, Result\u003cEvent, Infallible\u003e\u003e\u003e.\n- Conditional streaming (stream:bool in body) → both Ok(...) and OkStream(Sse\u003c...\u003e) variants.","design":"Extends src/server_generator.rs with router emission. Reuses streaming.rs SSE infrastructure. Model pruning reuses (or factors out) the reachability walk already in client_generator.rs. New files emitted: generated/server/{mod,api,router,extractors,errors}.rs.","acceptance_criteria":"- [ ] Generated router compiles and serves picked endpoints.\n- [ ] Path/query/header parameters extracted with correct types.\n- [ ] Streaming endpoint (createChatCompletion with stream=true) returns SSE.\n- [ ] models.rs contains only types reachable from picked ops when only server gen is enabled.\n- [ ] Round-trip: hit the endpoint with a request body, get back the expected response shape.\n- [ ] No unused imports or dead code warnings in generated server/.","notes":"P5 partial — router factory emitted in P4 commit. Remaining for full P5: typed extractors for query/header params, multi-trait combined router, model pruning to picked-ops reachability. Current state generates working code for canonical specs (createResponse + messages_post) end-to-end.","status":"open","priority":2,"issue_type":"feature","owner":"james@littlebearlabs.io","created_at":"2026-05-11T01:00:16Z","created_by":"James Lal","updated_at":"2026-05-11T02:41:31Z","dependencies":[{"issue_id":"openapi-generator-cdx","depends_on_id":"openapi-generator-jih","type":"blocks","created_at":"2026-05-10T19:00:37Z","created_by":"James Lal","metadata":"{}"}],"dependency_count":1,"dependent_count":2,"comment_count":0} |
4 | 5 | {"id":"openapi-generator-jih","title":"[Server P4] Trait + typed response-enum emitter","description":"Emit the trait-and-response-enum core of server codegen.\n\nFor each picked operation:\n- A trait method on the per-tag trait (or single ServerApi trait if untagged).\n- A response enum: one variant per documented status code, variant payload = documented body type.\n- An IntoResponse impl mapping each variant → (StatusCode, Json).\n\nTrait shape:\n #[axum::async_trait]\n pub trait ChatApi: Send + Sync + 'static {\n async fn create_chat_completion(\n \u0026self,\n auth: BearerAuth,\n body: CreateChatCompletionRequest,\n ) -\u003e CreateChatCompletionResponse;\n }\n\nResponse enum:\n pub enum CreateChatCompletionResponse {\n Ok(ChatCompletionResponse),\n BadRequest(ErrorResponse),\n TooManyRequests(ErrorResponse),\n }\n\nTrait grouping: one trait per tag in operation order. Operations without a tag land on a default 'ServerApi' trait.","design":"New module src/server_generator.rs paralleling client_generator.rs. Reuses type_mapping.rs for body/parameter types. Reuses analysis.rs response metadata. Status-code → variant name mapping via heck (200 Ok, 400 BadRequest, 404 NotFound, default Default, etc.).","acceptance_criteria":"- [ ] Generated trait method signature matches design.\n- [ ] Response enum has one variant per status documented in the operation's responses map.\n- [ ] Default response (responses.default) → variant 'Default'.\n- [ ] IntoResponse impl returns correct StatusCode + Json body per variant.\n- [ ] No #[validate(...)] attrs, no validator dep usage (project rule).\n- [ ] Compiles against axum 0.7 (or 0.8 if upgrading) and serde_json.\n- [ ] Snapshot test for trait+enums emitted from createChatCompletion.","status":"closed","priority":2,"issue_type":"feature","assignee":"James Lal","owner":"james@littlebearlabs.io","created_at":"2026-05-11T01:00:09Z","created_by":"James Lal","updated_at":"2026-05-11T02:41:23Z","started_at":"2026-05-11T02:13:54Z","closed_at":"2026-05-11T02:41:23Z","close_reason":"P4 trait+response-enum emitter + P5-lite router factory shipped together. Emits server/{mod,api,errors,router}.rs. Trait per tag with snake_case methods; typed response enums with status-code variants + IntoResponse impl; SSE OkStream variant when supports_streaming; ServerEventStream alias for the SSE payload. Router factory takes T: Trait+Clone, wires routes via axum::routing::*, handles JSON+Path extraction. Both example crates exercise unary+SSE branches; cargo test passes for both.","dependencies":[{"issue_id":"openapi-generator-jih","depends_on_id":"openapi-generator-lyo","type":"blocks","created_at":"2026-05-10T19:00:36Z","created_by":"James Lal","metadata":"{}"}],"dependency_count":1,"dependent_count":2,"comment_count":0} |
|
0 commit comments