Skip to content

feat: add structured JSON output support for Anthropic provider - #3475

Merged
JanCizmar merged 2 commits into
mainfrom
jancizmar/anthropic-json-output-config
Feb 19, 2026
Merged

feat: add structured JSON output support for Anthropic provider#3475
JanCizmar merged 2 commits into
mainfrom
jancizmar/anthropic-json-output-config

Conversation

@JanCizmar

@JanCizmar JanCizmar commented Feb 18, 2026

Copy link
Copy Markdown
Member

Summary

  • Add output_config with json_schema format to Anthropic API requests when shouldOutputJson=true and config.format="json_schema", mirroring the existing OpenAI structured output support
  • The schema enforces output and contextDescription string fields, matching the OpenAI implementation
  • The existing prompt-based JSON hint ("Return valid JSON and only JSON!") is retained as a fallback

Test plan

  • Unit tests verify output_config is included when both shouldOutputJson and format="json_schema" are set
  • Unit tests verify output_config is omitted when format is not json_schema
  • Unit tests verify output_config is omitted when shouldOutputJson is false
  • Manual test with Anthropic API to confirm structured JSON responses

Summary by CodeRabbit

  • New Features

    • Added optional JSON Schema–formatted output for Anthropic translations; schema is included in requests only when JSON output is enabled and the provider is configured for json_schema.
  • Tests

    • Added unit tests validating when the JSON Schema output is included or omitted based on configuration and parameters.

Use Anthropic's output_config.format API field to guarantee valid JSON
when shouldOutputJson=true and config.format="json_schema", matching
the existing OpenAI structured output behavior.
@coderabbitai

coderabbitai Bot commented Feb 18, 2026

Copy link
Copy Markdown
Contributor
📝 Walkthrough

Walkthrough

Adds conditional JSON schema output configuration to Anthropic API requests. Introduces OutputConfig and OutputFormat data structures to encapsulate output format specifications and schema for "output" and "contextDescription". Adds unit tests validating conditional inclusion of output_config based on shouldOutputJson and provider format.

Changes

Cohort / File(s) Summary
Anthropic API Output Configuration
ee/backend/app/src/main/kotlin/io/tolgee/ee/component/llm/AnthropicApiService.kt
Adds optional output_config field to RequestBody with conditional population when shouldOutputJson is true and provider format is "json_schema". Adds public OutputConfig and OutputFormat classes; output_config is annotated to be omitted when null. Defines JSON schema for output and contextDescription.
AnthropicApiService Tests
ee/backend/tests/src/test/kotlin/io/tolgee/ee/unit/AnthropicApiServiceTest.kt
New unit tests capturing outgoing request body to assert output_config presence and schema when shouldOutputJson=true and format=json_schema, and its absence otherwise. Adds test scaffolding including a capturing RestTemplate and stub HTTP response.

Sequence Diagram(s)

mermaid
sequenceDiagram
participant Service as AnthropicApiService
participant Client as RestTemplate/HTTP Client
participant API as Anthropic API
Service->>Service: build RequestBody (include output_config if shouldOutputJson && format=="json_schema")
Service->>Client: send HTTP request with RequestBody
Client->>API: POST /v1/complete (request body forwarded)
API-->>Client: response JSON
Client-->>Service: return response

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Poem

🐰 A tiny config hops into the frame,
With output and context, JSON knows its name,
When flags align just right and the format's true,
The rabbit sends schema, tidy, through and through. 🥕

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately and concisely describes the main change: adding structured JSON output support for the Anthropic provider, which is the primary focus of this changeset.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch jancizmar/anthropic-json-output-config

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

@JanCizmar
JanCizmar marked this pull request as ready for review February 18, 2026 16:33

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🧹 Nitpick comments (2)
ee/backend/tests/src/test/kotlin/io/tolgee/ee/unit/AnthropicApiServiceTest.kt (1)

47-63: Consider using a JSON assertion library for cleaner, type-safe assertions.

The multiple @Suppress("UNCHECKED_CAST") annotations indicate this could benefit from a JSON-path or structured assertion approach. This would improve readability and reduce type-casting noise.

💡 Alternative using JsonPath (optional)

You could use JsonPath assertions for cleaner type-safe access:

import com.jayway.jsonpath.JsonPath

// Then in the test:
val json = JsonPath.parse(capturedRequestBody!!)
assertThat(json.read<String>("$.output_config.format.type")).isEqualTo("json_schema")
assertThat(json.read<String>("$.output_config.format.schema.type")).isEqualTo("object")
assertThat(json.read<List<String>>("$.output_config.format.schema.required"))
    .containsExactly("output", "contextDescription")

This is purely optional - the current approach works fine for test code.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@ee/backend/tests/src/test/kotlin/io/tolgee/ee/unit/AnthropicApiServiceTest.kt`
around lines 47 - 63, Replace the manual Map casts and multiple
`@Suppress`("UNCHECKED_CAST") usages in AnthropicApiServiceTest (the assertions
around bodyMap, output_config, format, schema, properties) with a JSON-path
based assertion: parse the captured request JSON (e.g., with
JsonPath.parse(capturedRequestBody!!)) and then use json.read with explicit
paths like "$.output_config.format.type", "$.output_config.format.schema.type",
"$.output_config.format.schema.required" and
"$.output_config.format.schema.additionalProperties" to assert values and
required array contents; remove the unsafe casts and suppressions and update
assertions to use the typed reads from JsonPath for clearer, type-safe checks.
ee/backend/app/src/main/kotlin/io/tolgee/ee/component/llm/AnthropicApiService.kt (1)

119-121: Stale comment references AzureCognitive instead of Anthropic.

The comment incorrectly states "Data structure for mapping the AzureCognitive JSON response objects" but this is the Anthropic API service.

📝 Proposed fix
   /**
-   * Data structure for mapping the AzureCognitive JSON response objects.
+   * Data structure for mapping the Anthropic JSON response objects.
    */
   companion object {
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@ee/backend/app/src/main/kotlin/io/tolgee/ee/component/llm/AnthropicApiService.kt`
around lines 119 - 121, The comment above the response-mapping data structure in
AnthropicApiService.kt incorrectly mentions "AzureCognitive"; update that
Javadoc/KDoc to refer to Anthropic (e.g., "Data structure for mapping the
Anthropic JSON response objects" or similar) so the comment matches the
class/file purpose (AnthropicApiService and its response data classes).
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In
`@ee/backend/app/src/main/kotlin/io/tolgee/ee/component/llm/AnthropicApiService.kt`:
- Around line 119-121: The comment above the response-mapping data structure in
AnthropicApiService.kt incorrectly mentions "AzureCognitive"; update that
Javadoc/KDoc to refer to Anthropic (e.g., "Data structure for mapping the
Anthropic JSON response objects" or similar) so the comment matches the
class/file purpose (AnthropicApiService and its response data classes).

In
`@ee/backend/tests/src/test/kotlin/io/tolgee/ee/unit/AnthropicApiServiceTest.kt`:
- Around line 47-63: Replace the manual Map casts and multiple
`@Suppress`("UNCHECKED_CAST") usages in AnthropicApiServiceTest (the assertions
around bodyMap, output_config, format, schema, properties) with a JSON-path
based assertion: parse the captured request JSON (e.g., with
JsonPath.parse(capturedRequestBody!!)) and then use json.read with explicit
paths like "$.output_config.format.type", "$.output_config.format.schema.type",
"$.output_config.format.schema.required" and
"$.output_config.format.schema.additionalProperties" to assert values and
required array contents; remove the unsafe casts and suppressions and update
assertions to use the typed reads from JsonPath for clearer, type-safe checks.

@JanCizmar
JanCizmar requested a review from dkrizan February 18, 2026 17:08
dkrizan
dkrizan previously approved these changes Feb 19, 2026
Comment thread ee/backend/tests/src/test/kotlin/io/tolgee/ee/unit/AnthropicApiServiceTest.kt Outdated
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🧹 Nitpick comments (1)
ee/backend/tests/src/test/kotlin/io/tolgee/ee/unit/AnthropicApiServiceTest.kt (1)

67-78: Consider testing with a non-null, non-json_schema format value.

The test verifies behavior when format = null, but the test name suggests any non-json_schema format. Adding a test case with an explicit format like "text" would strengthen coverage and ensure the production code handles both null and other format values correctly.

💡 Optional: Additional test case
`@Test`
fun `omits output_config when format is different value`() {
  val config = createConfig(format = "text")
  val params = createParams(shouldOutputJson = true)
  val restTemplate = createCapturingRestTemplate()

  service.translate(params, config, restTemplate)

  val bodyMap = objectMapper.readValue<Map<String, Any>>(capturedRequestBody!!)

  assertThat(bodyMap).doesNotContainKey("output_config")
}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@ee/backend/tests/src/test/kotlin/io/tolgee/ee/unit/AnthropicApiServiceTest.kt`
around lines 67 - 78, Add a second unit test that mirrors `omits output_config
when format is not json_schema` but uses a non-null, non-json_schema format
(e.g., "text") so we verify both null and explicit non-json_schema values are
handled; use the same helpers (`createConfig(format = "text")`,
`createParams(shouldOutputJson = true)`, `createCapturingRestTemplate()`), call
`service.translate(params, config, restTemplate)`, parse `capturedRequestBody`
with `objectMapper.readValue`, and assert the resulting map does not contain the
"output_config" key.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In
`@ee/backend/tests/src/test/kotlin/io/tolgee/ee/unit/AnthropicApiServiceTest.kt`:
- Around line 67-78: Add a second unit test that mirrors `omits output_config
when format is not json_schema` but uses a non-null, non-json_schema format
(e.g., "text") so we verify both null and explicit non-json_schema values are
handled; use the same helpers (`createConfig(format = "text")`,
`createParams(shouldOutputJson = true)`, `createCapturingRestTemplate()`), call
`service.translate(params, config, restTemplate)`, parse `capturedRequestBody`
with `objectMapper.readValue`, and assert the resulting map does not contain the
"output_config" key.

@JanCizmar
JanCizmar merged commit 64fcaec into main Feb 19, 2026
37 of 38 checks passed
@JanCizmar
JanCizmar deleted the jancizmar/anthropic-json-output-config branch February 19, 2026 12:32
TolgeeMachine added a commit that referenced this pull request Feb 19, 2026
# [3.161.0](v3.160.0...v3.161.0) (2026-02-19)

### Features

* add LLM provider fallback configuration ([#3477](#3477)) ([b56ad5d](b56ad5d))
* add structured JSON output support for Anthropic provider ([#3475](#3475)) ([64fcaec](64fcaec))
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