Skip to content

fix(java): replace reflection-based SSE discrimination with IR-driven discriminatorContext#14184

Closed
jsklan wants to merge 11 commits intomainfrom
jsklan/java-sdk-sse-no-reflection-v2
Closed

fix(java): replace reflection-based SSE discrimination with IR-driven discriminatorContext#14184
jsklan wants to merge 11 commits intomainfrom
jsklan/java-sdk-sse-no-reflection-v2

Conversation

@jsklan
Copy link
Copy Markdown
Contributor

@jsklan jsklan commented Mar 27, 2026

Description

The Java SDK generator used runtime reflection (SseEventParser.java) to determine where SSE discriminator fields live — protocol envelope vs data payload. This was slow for LLM inference workflows and produced incorrect results: the heuristic checked if the discriminant wire value matched SSE envelope field names, which failed when a data-context union used "event" as its discriminant property (3 endpoints were misclassified).

TypeScript and Go generators already use the IR's discriminatorContext field correctly. This PR brings Java in line.

Changes Made

  • SseDiscriminationAnalyzer: Replaced SSE_ENVELOPE_FIELDS heuristic with unionDeclaration.getDiscriminatorContext() from the IR — checks for UnionDiscriminatorContext.PROTOCOL vs absent/data
  • Stream.java template: Added SseDiscriminatorContext enum (PROTOCOL/DATA), unified fromSse() factory methods with Javadoc to disambiguate overloads, inlined protocol-level envelope construction with safe JSON data handling
  • AbstractHttpResponseParserGenerator: Generates Stream.fromSse(..., Stream.SseDiscriminatorContext.PROTOCOL/DATA) using JavaPoet type-safe $L formatting instead of string concatenation
  • Deleted SseEventParser.java: Entire reflection-based utility removed (228 lines) — created solely for this purpose, no other consumers
  • Bug fix: 3 endpoints (/stream/data-context, /stream/no-context, /stream/data-context-with-envelope-schema) now correctly classified as DATA instead of PROTOCOL

Updates since last revision

  • Applied spotless formatting fixes to AbstractHttpResponseParserGenerator.java and SseDiscriminationAnalyzer.java (resolved check CI failure)
  • Fixed double-serialization bug in parseProtocolLevelUnion: added parseData() helper that deserializes SSE data as JSON only for structured types ({/[ prefixed), returning raw strings for everything else. This prevents both double-escaping of JSON objects and misinterpretation of JSON primitives ("42", "true", "null") as non-string types
  • Added Javadoc to all fromSse() overloads to disambiguate the 3-arg streamTerminator variant from the 4-arg discriminatorProperty variant
  • Added clarifying comment in iterator() explaining that DATA-level discrimination intentionally uses plain SSEIterator (Jackson handles it via the in-payload discriminator)
  • Replaced string concatenation in CodeBlock.of format strings with JavaPoet $L parameters for type-safe code generation
  • Updated seed test snapshot for server-sent-events-openapi/with-wire-tests

Testing

  • Seed test server-sent-events-openapi passes with correct endpoint classification
  • Java generator compiles and tests pass (./gradlew test)
  • Monorepo compiles clean (pnpm compile)
  • ./gradlew check -x test passes locally (spotless clean)

Human Review Checklist

  • Verify parseData() only parses {/[-prefixed strings as JSON — raw string data (including valid JSON primitives like 42, true, null) is now kept as-is. Confirm this is acceptable for all protocol-level SSE union variants in practice
  • Confirm seed snapshot at seed/java-sdk/server-sent-events-openapi/with-wire-tests/ matches expected generator output (snapshot was manually edited to match template changes — re-running the seed test would confirm exact alignment)
  • The fromSse(Class, Reader, String) and fromSse(Class, Reader, String, SseDiscriminatorContext) overloads share a String third param with different semantics (streamTerminator vs discriminatorProperty). Javadoc disambiguates, and all callers are generated code, but confirm this is acceptable vs. a structural API change
  • Note: one java-sdk CI shard previously failed due to transient Maven Central 403 — unrelated to code changes; the required seed-test-results (java-sdk) aggregation check passed

Link to Devin session: https://app.devin.ai/sessions/5ebf2a4e3f58482386f8c34b2bd24aa4


Open with Devin

@github-actions
Copy link
Copy Markdown
Contributor

🌱 Seed Test Selector

Select languages to run seed tests for:

  • Python
  • TypeScript
  • Java
  • Go
  • Ruby
  • C#
  • PHP
  • Swift
  • Rust
  • OpenAPI
  • Postman

How to use: Click the ⋯ menu above → "Edit" → check the boxes you want → click "Update comment". Tests will run automatically and snapshots will be committed to this PR.

@fern-support fern-support changed the title Jsklan/java sdk sse no reflection v2 fix(java): replace reflection-based SSE discrimination with IR-driven discriminatorContext Mar 27, 2026
@jsklan jsklan marked this pull request as ready for review March 27, 2026 01:52
Copy link
Copy Markdown

@claude claude Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Claude Code Review

This repository is configured for manual code reviews. Comment @claude review to trigger a review and subscribe this PR to future pushes, or @claude review once for a one-time review.

Tip: disable this comment in your organization's Code Review settings.

devin-ai-integration[bot]

This comment was marked as resolved.

@fern-support fern-support marked this pull request as draft March 27, 2026 13:16
@jsklan jsklan closed this Mar 30, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Development

Successfully merging this pull request may close these issues.

1 participant