Skip to content

refactor(core): derive Responses/chat known-field lists from struct tags#485

Merged
SantiagoDePolonia merged 1 commit into
mainfrom
refactor/responses-field-collapse
Jul 5, 2026
Merged

refactor(core): derive Responses/chat known-field lists from struct tags#485
SantiagoDePolonia merged 1 commit into
mainfrom
refactor/responses-field-collapse

Conversation

@SantiagoDePolonia

@SantiagoDePolonia SantiagoDePolonia commented Jul 5, 2026

Copy link
Copy Markdown
Contributor

Summary

The biggest remaining shotgun-surgery site from the architecture review (doc in #472, §3): the Responses API field set was maintained by hand in ~8 places. Adding one field meant editing three 25-field struct twins, two known-field string lists, two raw decode structs with field-by-field copies, a 25-field marshal struct, and a 24-field provider-side reduction — and a missed site silently dropped the field or double-emitted it via ExtraFields.

After this PR the field set is written once per type and everything else derives:

  • jsonFieldNames derives the known-field lists from json tags at package init, so extractUnknownJSONFields can never drift from the type definitions.
  • Decoding goes through an embedded type alias (with Input shadowed as json.RawMessage for the string-or-array union), so new typed fields are populated by the JSON package automatically — the triple-entry raw structs and per-field copies are deleted.
  • ResponseCompactRequest is now type ResponseCompactRequest ResponseInputTokensRequest — the byte-identical 25-field twin is gone.
  • The full→utility reduction lives next to the types (ResponsesRequest.InputTokensRequest() / .CompactRequest()); the openai provider keeps stripping the gateway-only provider hint at its boundary, unchanged on the wire.
  • Same alias treatment for ChatRequest decode (its field list was triple-entry too).

The safety net

Two new guard tests pin the contract mechanically: the utility field set must equal the full set minus the streaming controls, and a reflection-filled ResponsesRequest asserts the reduction copies every field. The next person who adds a Responses field gets a failing test naming the missed spot, not a silent drop.

User-visible impact

None — wire behavior is unchanged. The existing round-trip, unknown-field-preservation, and input-union tests pass untouched, which is the strongest evidence the alias decode is byte-equivalent.

Testing

Full go build ./... and go test ./...; go test -race on core + openai + server; golangci-lint 0 issues; pre-commit make test-race on the commit.

🤖 Generated with Claude Code

Summary by CodeRabbit

  • New Features

    • Improved support for request formats that preserve extra, unrecognized fields.
    • Added streamlined conversion to smaller request variants for token-related and compact response workflows.
  • Bug Fixes

    • Better handling of nested input data and unknown fields when reading and writing requests.
    • More consistent behavior when request objects are empty or unset, including safer nil handling.
  • Tests

    • Added coverage to verify request field consistency and data preservation during conversion.

The Responses API field set was hand-maintained in ~8 places (three
25-field struct twins, known-field string lists, raw decode structs with
per-field copy, a 25-field marshal struct, and a 24-field provider-side
reduction); adding one field was shotgun surgery, and a missed site meant
the field silently dropped or double-emitted.

- jsonFieldNames derives known-field lists from json tags at package init,
  so extractUnknownJSONFields can never drift from the type definitions.
- Decode goes through an embedded type alias (Input shadowed as RawMessage
  for the union), so new typed fields are populated automatically — the
  triple-entry raw structs and field-by-field copies are gone.
- ResponseCompactRequest is now defined as ResponseInputTokensRequest (one
  written field set); the responseUtilityRequestJSON bridge and its two
  25-field copies are deleted.
- The full->utility reduction moves next to the types as
  ResponsesRequest.InputTokensRequest()/CompactRequest(); the openai
  provider strips the gateway-only provider hint at its boundary as before.
- Guard tests pin the contract: utility field set == full set minus stream
  controls, and a reflection-filled request asserts the reduction copies
  every field, so the next added field fails a test instead of vanishing.

Net -220 lines in the codecs. Wire behavior unchanged (existing round-trip
and unknown-field tests pass untouched).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@coderabbitai

coderabbitai Bot commented Jul 5, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: 08d826b4-f9e5-4cb7-aca3-81513cb57cd2

📥 Commits

Reviewing files that changed from the base of the PR and between 54b3429 and fb43e21.

📒 Files selected for processing (6)
  • internal/core/chat_json.go
  • internal/core/json_fields.go
  • internal/core/responses.go
  • internal/core/responses_field_parity_test.go
  • internal/core/responses_json.go
  • internal/providers/openai/compatible_provider.go

📝 Walkthrough

Walkthrough

Adds a reflection-based jsonFieldNames helper for deriving known JSON field lists from struct tags, applies it to ChatRequest and ResponsesRequest unmarshaling, and replaces ResponseCompactRequest with an alias derived via new InputTokensRequest()/CompactRequest() methods, with corresponding provider and test updates.

Changes

JSON field derivation and request refactor

Layer / File(s) Summary
Reflection-based JSON field helper
internal/core/json_fields.go
Adds jsonFieldNames to derive JSON member names from struct tags via reflection, handling "-" omission and anonymous embedded fields.
ChatRequest unmarshal rework
internal/core/chat_json.go
Derives chatRequestFields at init and rewrites ChatRequest.UnmarshalJSON to decode via an alias and extract unknowns using the derived list.
ResponsesRequest utility contracts
internal/core/responses.go
Redefines ResponseCompactRequest as an alias of ResponseInputTokensRequest; adds InputTokensRequest() and CompactRequest() methods with nil-safety and ExtraFields cloning.
Responses JSON decoding centralization
internal/core/responses_json.go
Adds shared responsesExtrasAndInput helper and known-field lists; reworks UnmarshalJSON/MarshalJSON for ResponsesRequest, ResponseInputTokensRequest, and ResponseCompactRequest around alias embedding.
Provider wiring and parity tests
internal/providers/openai/compatible_provider.go, internal/core/responses_field_parity_test.go
Updates provider helpers to call InputTokensRequest()/CompactRequest() directly; adds tests verifying field parity and full-field copying behavior.

Estimated code review effort: 3 (Moderate) | ~25 minutes

Sequence Diagram(s)

sequenceDiagram
  participant Provider as compatible_provider.go
  participant Req as ResponsesRequest
  participant ITR as InputTokensRequest
  participant CR as CompactRequest

  Provider->>Req: req.InputTokensRequest()
  Req->>ITR: copy fields, clone ExtraFields
  ITR-->>Provider: return *ResponseInputTokensRequest

  Provider->>Req: req.CompactRequest()
  Req->>ITR: InputTokensRequest()
  ITR-->>CR: convert to ResponseCompactRequest
  CR-->>Provider: return *ResponseCompactRequest
Loading

Possibly related PRs

  • ENTERPILOT/GoModel#353: Refactors the same Responses/Chat JSON handling and utility-request translation logic in internal/core/responses_json.go/responses.go and OpenAI compatible_provider.go.

Poem

A rabbit hopped through tags and fields,
Reflection's magic, quietly it yields,
No more copying, field by field by hand,
Just one alias, elegantly planned. 🐇
Hop, decode, and clone with glee—
Compact and tokens, derived so free!

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately captures the main refactor: deriving Responses/chat known-field lists from struct tags.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch refactor/responses-field-collapse

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@codecov-commenter

Copy link
Copy Markdown

⚠️ Please install the 'codecov app svg image' to ensure uploads and comments are reliably processed by Codecov.

Codecov Report

❌ Patch coverage is 86.02151% with 13 lines in your changes missing coverage. Please review.

Files with missing lines Patch % Lines
internal/core/json_fields.go 58.82% 4 Missing and 3 partials ⚠️
internal/core/responses_json.go 78.57% 3 Missing and 3 partials ⚠️

📢 Thoughts on this report? Let us know!

@greptile-apps

greptile-apps Bot commented Jul 5, 2026

Copy link
Copy Markdown

Confidence Score: 5/5

Safe to merge with low risk.

The refactor is focused on JSON decoding, unknown-field preservation, and Responses utility reductions. Guard tests cover field parity and copy coverage. No correctness or security issues were identified in the changed paths.

No files require special attention.

T-Rex T-Rex Logs

What T-Rex did

  • Ran the focused tests for core responses and OpenAI guard checks; the tests reported parity and field preservation across scenarios.
  • Ran package-level tests for internal/core and internal/providers/openai; the tests completed with PASS and exit code 0.
  • Ran the race tests on the core package; the race suite completed with PASS before the timeout.
  • Attempted the OpenAI race tests; the run failed due to a build issue with sqlite compilation, resulting in exit code 1.

View all artifacts

T-Rex Ran code and verified through T-Rex

Sequence Diagram

%%{init: {'theme': 'neutral'}}%%
sequenceDiagram
participant Client
participant Core as core request JSON
participant Provider as OpenAI-compatible provider
participant Upstream as Upstream API

Client->>Core: Send chat/Responses JSON
Core->>Core: Decode typed fields via alias
Core->>Core: Derive known fields from json tags
Core->>Core: Preserve unknown members in ExtraFields
Provider->>Core: Reduce ResponsesRequest to utility request
Core-->>Provider: InputTokensRequest/CompactRequest without stream fields
Provider->>Provider: Clear gateway-only provider hint
Provider->>Upstream: Forward utility request body
Loading
%%{init: {'theme': 'base', 'themeVariables': {"darkMode": true, "background": "#0d1117", "primaryColor": "#21262d", "primaryTextColor": "#e6edf3", "primaryBorderColor": "#8b949e", "lineColor": "#8b949e", "textColor": "#e6edf3", "edgeLabelBackground": "#161b22", "actorBkg": "#21262d", "actorBorder": "#8b949e", "actorTextColor": "#e6edf3", "actorLineColor": "#8b949e", "signalColor": "#8b949e", "signalTextColor": "#e6edf3", "noteBkgColor": "#373320", "noteBorderColor": "#d4a72c", "noteTextColor": "#f0e6c0", "labelBoxBkgColor": "#21262d", "labelBoxBorderColor": "#8b949e", "labelTextColor": "#e6edf3", "loopTextColor": "#e6edf3", "activationBkgColor": "#30363d", "activationBorderColor": "#8b949e"}}}%%
sequenceDiagram
participant Client
participant Core as core request JSON
participant Provider as OpenAI-compatible provider
participant Upstream as Upstream API

Client->>Core: Send chat/Responses JSON
Core->>Core: Decode typed fields via alias
Core->>Core: Derive known fields from json tags
Core->>Core: Preserve unknown members in ExtraFields
Provider->>Core: Reduce ResponsesRequest to utility request
Core-->>Provider: InputTokensRequest/CompactRequest without stream fields
Provider->>Provider: Clear gateway-only provider hint
Provider->>Upstream: Forward utility request body
Loading

Reviews (1): Last reviewed commit: "refactor(core): derive Responses/chat kn..." | Re-trigger Greptile

@SantiagoDePolonia SantiagoDePolonia merged commit daa836c into main Jul 5, 2026
21 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants