Skip to content

fix: add additionalProperties: false to structured output schemas for Databricks compatibility#2308

Merged
sfc-gh-jreini merged 3 commits intotruera:mainfrom
debu-sinha:fix/databricks-structured-outputs-additionalProperties
Dec 11, 2025
Merged

fix: add additionalProperties: false to structured output schemas for Databricks compatibility#2308
sfc-gh-jreini merged 3 commits intotruera:mainfrom
debu-sinha:fix/databricks-structured-outputs-additionalProperties

Conversation

@debu-sinha
Copy link
Copy Markdown
Contributor

@debu-sinha debu-sinha commented Dec 11, 2025

Summary

This PR fixes a compatibility issue with Databricks AI Gateway that prevents TruLens structured outputs from working with Databricks model serving endpoints.

Fixes: #2307

cc: @joshreini1 @piotrm0 @sfc-gh-dkurokawa

Problem

Databricks model serving endpoints require additionalProperties: false in JSON schemas for structured outputs (when using response_format). Without this property, TruLens feedback evaluations fail with:

BadRequestError: Invalid schema for response_format 'ChainOfThoughtResponse':
In context=(), 'additionalProperties' is required to be supplied and to be false.

This affects users who want to use TruLens with:

  • Databricks AI Gateway
  • LiteLLM with Databricks endpoints
  • Any provider that requires strict JSON schema validation

Solution

Add model_config = ConfigDict(extra="forbid") to Pydantic models in output_schemas.py. This causes Pydantic to include additionalProperties: false in the generated JSON schema.

Code Change

# src/feedback/trulens/feedback/output_schemas.py

from pydantic import BaseModel
from pydantic import ConfigDict  # Added
from pydantic import Field


class BaseFeedbackResponse(BaseModel):
    """
    A base model for feedback responses.
    """

    model_config = ConfigDict(extra="forbid")  # Added - enables additionalProperties: false

    score: int = Field(description="The score based on the given criteria.")


class ChainOfThoughtResponse(BaseModel):
    """
    A model to represent the response from a Chain of Thought (COT) evaluation.
    """

    model_config = ConfigDict(extra="forbid")  # Added - enables additionalProperties: false

    criteria: str = Field(description="The criteria for the evaluation.")
    supporting_evidence: str = Field(
        description="Supporting evidence for the score, detailing the reasoning step by step."
    )
    score: int = Field(description="The score based on the given criteria.")

Schema Before (without the fix):

{
  "properties": {
    "criteria": {"description": "The criteria for the evaluation.", "title": "Criteria", "type": "string"},
    "supporting_evidence": {"description": "Supporting evidence...", "title": "Supporting Evidence", "type": "string"},
    "score": {"description": "The score based on the given criteria.", "title": "Score", "type": "integer"}
  },
  "required": ["criteria", "supporting_evidence", "score"],
  "title": "ChainOfThoughtResponse",
  "type": "object"
}

Schema After (with the fix):

{
  "additionalProperties": false,
  "properties": {
    "criteria": {"description": "The criteria for the evaluation.", "title": "Criteria", "type": "string"},
    "supporting_evidence": {"description": "Supporting evidence...", "title": "Supporting Evidence", "type": "string"},
    "score": {"description": "The score based on the given criteria.", "title": "Score", "type": "integer"}
  },
  "required": ["criteria", "supporting_evidence", "score"],
  "title": "ChainOfThoughtResponse",
  "type": "object"
}

Changes

File Change
src/feedback/trulens/feedback/output_schemas.py Added model_config = ConfigDict(extra="forbid") to both models
tests/unit/test_output_schemas.py Added 5 new unit tests to verify schema and runtime behavior

Testing

# Run new unit tests
poetry run pytest tests/unit/test_output_schemas.py -v
# Result: 5 passed

# Run existing OpenAI capability tests
TEST_OPTIONAL=1 poetry run pytest tests/unit/providers/test_openai_capabilities.py -v
# Result: 11 passed

Test Verification

>>> from trulens.feedback.output_schemas import ChainOfThoughtResponse
>>> import json
>>> print(json.dumps(ChainOfThoughtResponse.model_json_schema(), indent=2))
{
  "additionalProperties": false,  # <-- Now included!
  "properties": {...},
  "required": [...],
  "title": "ChainOfThoughtResponse",
  "type": "object"
}

Backward Compatibility

This change is backward compatible:

  • JSON schema output changes from implicit to explicit additionalProperties: false
  • Runtime behavior now rejects extra fields at validation time (stricter validation is generally safe)
  • No API or interface changes
  • Existing code that uses these models correctly will continue to work

Related Issues

… Databricks compatibility

This fix enables TruLens structured outputs to work with Databricks AI Gateway
and other providers that require strict JSON schema validation.

## Problem
Databricks model serving endpoints require `additionalProperties: false` in
JSON schemas for structured outputs (response_format). Without this, TruLens
feedback evaluations fail with:
```
Invalid schema for response_format 'ChainOfThoughtResponse':
In context=(), 'additionalProperties' is required to be supplied and to be false.
```

## Solution
Add `model_config = ConfigDict(extra="forbid")` to Pydantic models in
`output_schemas.py`. This causes Pydantic to include `additionalProperties: false`
in the generated JSON schema, which satisfies Databricks' requirements.

## Changes
- Modified `BaseFeedbackResponse` and `ChainOfThoughtResponse` models
- Added comprehensive unit tests to verify schema generation and runtime behavior
- All existing tests pass (11/11 OpenAI capability tests)

## Backward Compatibility
This change is backward compatible:
- JSON schema output changes from implicit to explicit `additionalProperties: false`
- Runtime behavior now rejects extra fields (stricter validation is generally safe)
- No API or interface changes

Fixes: truera#2307

Signed-off-by: Debu Sinha <debusinha2009@gmail.com>
Signed-off-by: debu-sinha <debusinha2009@gmail.com>
@sfc-gh-jreini
Copy link
Copy Markdown
Contributor

Thanks again for contributing!

Can you update the API tests by running make write-api from root in this branch? That should get the last tests passing

Signed-off-by: debu-sinha <debusinha2009@gmail.com>
@debu-sinha
Copy link
Copy Markdown
Contributor Author

@sfc-gh-jreini I've set up Python 3.11 locally and ran make write-api. The golden files have been updated and pushed. The changes are just version bumps from 2.4.2 to 2.5.0 in the API golden file.

Signed-off-by: debu-sinha <debusinha2009@gmail.com>
Copy link
Copy Markdown
Contributor

@sfc-gh-nvytla sfc-gh-nvytla left a comment

Choose a reason for hiding this comment

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

lgtm 👍

Copy link
Copy Markdown
Contributor

@sfc-gh-jreini sfc-gh-jreini left a comment

Choose a reason for hiding this comment

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

works well on my side for non-dbrx providers 👍

@sfc-gh-jreini sfc-gh-jreini merged commit 3d15b6f into truera:main Dec 11, 2025
6 of 7 checks passed
@sfc-gh-jreini sfc-gh-jreini mentioned this pull request Dec 11, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size:L This PR changes 100-499 lines, ignoring generated files.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants