Skip to content

[Bug][integrations][java] OpenAI, Azure OpenAI and Anthropic native schemas list annotated enums by Java name #1246

Closed

Description

@weiqingy

Search before asking

  • I searched in the issues and found nothing similar.

Description

Since #1215, a Java agent's output schema is sent to the provider natively. The OpenAI, Azure OpenAI and Anthropic connections build that schema through their SDKs' structured-output builders (toNativeResponseFormat in OpenAICompletionsConnection and AzureOpenAIChatModelConnection, toNativeOutputConfig in AnthropicChatModelConnection). Both SDKs configure victools with a bare JacksonModule(). They ignore @JsonProperty on enum constants and @JsonValue methods, so the schema lists the Java constant names.

When an enum's Jackson values differ from its names, the model follows the schema. The reply then fails to parse in ChatModelAction.generateStructuredOutput with InvalidFormatException, reported as a model-output parse error.

Since #1120, the other Java providers (Ollama, Gemini, Bedrock, watsonx) use PojoJsonSchemaGenerator, which lists the Jackson values. As a result, the same enum needs opposite annotations depending on the provider:

Enum OpenAI / Azure / Anthropic Ollama / Gemini / Bedrock / watsonx
@JsonProperty on every constant ["POSITIVE", ...], fails to parse ["positive", ...], works
@JsonValue returning a String ["POSITIVE", ...], fails to parse ["positive", ...], works
No annotations ["POSITIVE", ...], works ["POSITIVE", ...], works

Upgrading the SDKs does not help. The latest openai-java (4.80.0) and anthropic-java (2.71.0) still use a bare JacksonModule().

Proposed fix: build these three schemas with PojoJsonSchemaGenerator as well. Then one enum rule holds for every Java provider, and it matches Python, where pydantic also lists enum values. Two differences need handling so the requests stay valid:

  • OpenAI strict mode needs every property in required and additionalProperties: false. Today the shared generator leaves Optional fields out of required and adds no additionalProperties. So it needs a way to require every field (an Optional field stays nullable), plus FORBIDDEN_ADDITIONAL_PROPERTIES_BY_DEFAULT.
  • Anthropic also needs additionalProperties: false.

One visible side effect: properties come out in declaration order instead of alphabetical order, the same as the other providers and Python.

Each of the three connections gets an enum round-trip test, like the existing derivedSchemaFollowsJacksonEnumValues tests.

How to reproduce

enum Sentiment {
    @JsonProperty("positive") POSITIVE,
    @JsonProperty("negative") NEGATIVE
}

class Result {
    public String summary;
    public Sentiment sentiment;
}

Use Result.class as the output schema of a Java agent on OpenAI, Azure OpenAI or Anthropic. The request carries "sentiment": {"type": "string", "enum": ["POSITIVE", "NEGATIVE"]}. A reply that follows it, for example {"summary": "ok", "sentiment": "POSITIVE"}, fails new ObjectMapper().readValue(reply, Result.class).

No credentials are needed to see the schema. The SDK builder the connections call returns it offline:

ChatCompletionCreateParams.builder()
        .model(ChatModel.of(""))
        .addUserMessage("")
        .responseFormat(Result.class, JsonSchemaLocalValidation.NO)
        .build()
        .rawParams()
        .responseFormat();

Version and environment

main at 6d0a261d (after #1215), openai-java 4.8.0, anthropic-java 2.12.0, Java 11.

Are you willing to submit a PR?

  • I'm willing to submit a PR!
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

bug[Issue Type] Something isn't working as expected.fixVersion/0.4.0priority/majorDefault priority of the PR or issue.

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions