Skip to content

fix(ai): follow-up fixes for the AI API update before release - #426

Merged
andrii-bodnar merged 6 commits into
masterfrom
fix/ai-followups
Nov 4, 2026
Merged

andrii-bodnar merged 6 commits into
masterfrom
fix/ai-followups

Conversation

@katerina20

@katerina20 katerina20 commented Oct 28, 2026 •

Copy link
Copy Markdown
Collaborator

Follow-up to #424, resolving the findings of its post-merge audit. Checked against the official Crowdin OpenAPI specs (full YAML, all four editions).

Release baseline: SDK 1.37.0 has been released, and it includes #424. This PR keeps both source and binary compatibility with the published 1.37.0 artifact; nothing released is removed or changed.

Changes

1. Deprecated fields kept (compatible with 1.37.0)

  • AiPromptCompletionResponse.AiPromptCompletionData.eta and ProjectAiSettings.preTranslationAiPromptId are deprecated in the API. They stay, marked @Deprecated, as released in 1.37.0.
  • AiSetting.preTranslationAiPromptId (user/organization AI settings) is not deprecated in the API and is unchanged.

2. setTools(Map): 1.37.0 behavior kept, documented

  • Behavior: setTools(Map) / getTools() are the Lombok accessors, exactly as released in 1.37.0. The map is stored and returned by getTools(), but it is not sent to the API.
  • Javadoc: the deprecated tools field now says so explicitly and points to setAiTools(List).
  • Sending tools: use the typed setAiTools(List<AiTool>), which is sent as the documented tools array, [{"tool": {"type": "function", "function": {...}}}].
  • No conversion: an earlier revision of this PR converted the map into aiTools and threw IllegalArgumentException for other shapes. That code was removed to keep the class simple. The legacy map never produced a valid request in a released version: 1.36.0 sent it as an object where the API requires an array, and 1.37.0 doesn't send it.

3. AI Gateway: new aiGateway*Json methods

The docs describe the gateway response as "Response from the AI provider (format depends on provider and endpoint)".

  • Unchanged: the five released aiGateway*Raw methods keep their exact 1.37.0 signatures (Map<String, Object>) and behaviour. They return a JSON object response as a Map; other formats throw HttpException, as before. Their Javadoc now points to the new methods.
  • New: aiGatewayGetJson, aiGatewayPostJson, aiGatewayPutJson, aiGatewayPatchJson and aiGatewayDeleteJson return any JSON value:
Body Result
JSON object Map<String, Object>
JSON array List<Object>
string / number / boolean String / Number / Boolean
JSON null, empty or blank body null
not JSON HttpException with the raw body in getHttpResponse()

They use new names, so there are no overloads and no null-ambiguity.

  • Legacy methods: the deprecated aiGatewayGet/Post/Put/Patch/Delete are unchanged.
  • Core: the HTTP client is not changed.

4. Provider secrets kept out of toString()

  • Excluded fields: @ToString.Exclude on Credentials.apiKey, headers, serviceAccountKey, key and Credentials.Deployment.apiKey. Printing Credentials, AiProvider or AiProviderRequest no longer shows them.
  • Unchanged: JSON serialization, equals/hashCode and the getters/setters.
  • Documentation: the class Javadoc lists the 11 documented provider credential variants and their required fields.

5. learningRateMultiplier edge cases

  • Legacy getter: the deprecated Long getLearningRateMultiplier() still truncates finite values towards zero. For NaN, infinite or out-of-Long-range values it now returns null, instead of clamping or returning 0.
  • Request setter: FineTuningJobRequest.Hyperparameters.setLearningRateMultiplierValue() rejects NaN/Infinity, which would otherwise be sent as the invalid JSON strings "NaN"/"Infinity".

6. Documentation

Javadoc warnings on the legacy accessors added in #424 are fixed.

Approved behaviour changes (relative to 1.37.0)

  1. Gateway responses: a flexible Object return type is offered through the new aiGateway*Json methods. The released aiGateway*Raw methods keep Map.
  2. Credentials.toString() no longer includes secret values.

setTools(Map) is not a behavior change: it behaves exactly as in 1.37.0 (the same code run against the published 1.37.0 classes and this branch prints identical output).

Also:

  • the deprecated getLearningRateMultiplier() returns null for non-finite or out-of-range values;
  • setLearningRateMultiplierValue() rejects NaN/Infinity.

Validation

  • Full build (./gradlew build): 739 tests, 0 failures, on both JDK 17 and JDK 8.
  • Javadoc on JDK 8: no errors, and no warnings in the changed AI code.
  • AI tests: 94 passed.
    • AiGatewayRawHttpTest (8) runs the real ApacheHttpClient and JacksonJsonTransformer against a local HTTP server. It covers every JSON response type, empty/blank bodies, non-JSON, error status and 204 for the Json methods, and the released behaviour of the Map-returning Raw methods (objects work; arrays, scalars, empty and non-JSON bodies throw HttpException). It also checks HTTP methods, Crowdin and Enterprise paths, request bodies and the Authorization header, and the unchanged legacy methods.
    • AIApiTest: the released Raw tests run unchanged, plus mock tests for all five Json methods. The eta and project preTranslationAiPromptId assertions are back.
    • AiPromptCompletionRequestToolsTest (5) covers:
      • setTools(Map) stores the map and sends no tools (documented, unexpected, empty and null maps).
      • getTools() returns the stored map.
      • setAiTools(List) sends the documented array.
      • Exactly one tools property is written when both are set.
    • CredentialsTest (3), FineTuningHyperparametersTest (5).
  • Public signatures against the published 1.37.0 artifact (JitPack crowdin-api-client-java-1.37.0.jar): 0 removed or changed, and no null-ambiguous overloads.
  • OpenAPI contracts: the AI request and response models were compared recursively with the schemas. No missing non-deprecated fields.
  • The branch has the latest master (1.37.0) merged in.

Remaining risks

  • 204: an HTTP 204 response to the gateway methods (Raw and Json) fails in the shared transport (ApacheHttpClient reads a missing entity). This affects every typed SDK call. It's documented in the Javadoc and covered by a test, and needs a separate core fix.
  • Streaming and non-JSON gateway responses aren't supported.
  • setTools(Map) still drops its value silently, as in 1.37.0. This is documented in the Javadoc; use setAiTools(List).
  • Null map values: null values inside request maps (for example {"stop": null}) are still dropped by the SDK-wide JSON settings. This is existing behaviour.
  • No live API test: the gateway was tested against a local stub server, not the live Crowdin API.

🤖 Generated with Claude Code

katerina20 and others added 6 commits October 27, 2026 21:48
Exclude apiKey, headers, serviceAccountKey, the Custom AI key and the
Microsoft Foundry deployment apiKey from the Lombok-generated toString()
of Credentials and Credentials.Deployment, so logging an AiProvider or
AiProviderRequest no longer prints secrets. JSON serialization, equals
and the public getters/setters are unchanged.

Document the supported provider credential variants and their required
fields from the official OpenAPI.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
The deprecated Long getLearningRateMultiplier() now returns null for
NaN, infinite and out-of-range values instead of clamping them to
Long.MAX_VALUE/MIN_VALUE or returning 0; finite values within range are
still truncated towards zero, matching Config.getMin()/getMax().

FineTuningJobRequest.Hyperparameters.setLearningRateMultiplierValue()
rejects NaN and infinite values, which would otherwise be sent as the
invalid JSON strings "NaN"/"Infinity".

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…turn raw gateway bodies as Object

- remove AiPromptCompletionData.eta and ProjectAiSettings.preTranslationAiPromptId,
  added in #424 but deprecated in the API (AiSetting.preTranslationAiPromptId,
  which is not deprecated, stays)
- the deprecated setTools(Map) converts the documented tool shapes
  ({"tool": {...}} or {"type": "function", "function": {...}}) into the
  tools array instead of silently dropping the value; any other structure
  is rejected with IllegalArgumentException
- aiGateway*Raw return Object (Map, List, String, Number, Boolean or null)
  because the gateway response format depends on the provider; an empty
  body is null and a non-JSON body raises HttpException. Legacy
  aiGateway* methods are unchanged
- fill in missing Javadoc tags

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
SDK 1.37.0 was released with #424, so this follow-up must not change its
public API:

- keep AiPromptCompletionData.eta and ProjectAiSettings.preTranslationAiPromptId
  as @deprecated (both are deprecated in the API) instead of removing them
- keep the five aiGateway*Raw methods with their released Map<String, Object>
  signatures and behaviour (JSON object responses only)
- add aiGatewayGetJson/PostJson/PutJson/PatchJson/DeleteJson returning any
  JSON value (Map, List, String, Number, Boolean or null; empty body is null);
  they have new names, so no overload ambiguity is introduced

The credentials toString, setTools(Map) and learningRateMultiplier fixes
are unchanged.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Remove the legacy tools map conversion and its helpers (toAiTools, toTool,
requireOnlyKeys, invalidTools). setTools(Map) / getTools() are Lombok
accessors again: the map is stored but not sent, exactly as in 1.37.0, and
the documented tools array is sent through setAiTools(List).

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
@andrii-bodnar
andrii-bodnar merged commit dfb2da7 into master Nov 4, 2026
3 checks passed
@andrii-bodnar
andrii-bodnar deleted the fix/ai-followups branch November 4, 2026 07:27
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