Skip to content

[Needs review] Update google provider types - #592

Draft
github-actions[bot] wants to merge 1 commit into
mainfrom
update-google-provider-types-9efdbacd-43064989572
Draft

github-actions[bot] wants to merge 1 commit into
mainfrom
update-google-provider-types-9efdbacd-43064989572

Conversation

@github-actions

Copy link
Copy Markdown
Contributor

Automated update of Lingua provider types.

Provider: google

Publication mode: needs-review draft

Continuation required

This draft preserves the automation's partial work. One or more validation or advisory checks did not complete successfully; continue from this branch instead of restarting the provider update.

Feedback: comment /bt good or /bt bad to log review feedback to the Braintrust trace.

Human decisions required

The agent completed every unblocked item. These decisions require human input before the update can be finished:

  • FunctionResponsePart.fileData / FunctionResponseFileData / FunctionResponseBlob.displayName (google-function-response-media-parts)
    • Question: What is the canonical, non-lossy universal representation for media carried in a Google FunctionResponse.parts (inline bytes or fileData URI, plus mimeType and displayName) together with the {"$ref": "DISPLAY_NAME"} indirection that FunctionResponse.response uses to point at it? Specifically: should Lingua (a) reject any functionResponse that carries parts with an explicit unsupported-mapping error until a universal contract exists, (b) add a universal tool-result media representation that preserves the payload and the name-based linkage as a unit, or (c) resolve the $ref inline so that the universal tool-result output embeds the media directly?
    • Evidence: crates/lingua/src/providers/google/convert.rs:439-458 imports a functionResponse by reading only name, id, and response; FunctionResponse.parts (crates/lingua/src/providers/google/generated.rs:397) is never read. crates/lingua/src/providers/google/convert.rs:708-713 exports with ..Default::default(), so parts is never written. Because convert.rs:441-443 copies fr.response verbatim into ToolResultContentPart.output while the parts carrying the referent are discarded, a Google request with a multimodal function response transforms to Anthropic or chat-completions today with no error and emits a structurally valid but semantically broken tool result whose $ref points at nothing. This spec update makes the problem materially worse by adding a second referenceable carrier: FunctionResponsePart.fileData with the new FunctionResponseFileData { display_name, file_uri, mime_type } (generated.rs:435,452-468) and FunctionResponseBlob.display_name (generated.rs:481-482), both documented as referenceable via {"$ref": "DISPLAY_NAME"} from FunctionResponse.response. crates/lingua/src/universal/message.rs:241-249 shows ToolResultContentPart is { tool_call_id, tool_name, output: Value, custom_tool_call, caller, provider_options } with no media member, and crates/lingua/src/providers/anthropic/convert.rs:490-510 shows the Anthropic importer flattens its tool-result blocks into an opaque Value, so no provider-neutral tool-result media contract exists to reuse. AGENTS.md:19 and AGENTS.md:65 both forbid the current partial conversion.
    • Recommended option: Take option (a) now: extend the Google provider-only rejection so that any functionResponse whose parts array is non-empty returns ConvertError::UnsupportedMapping { from: "Google functionResponse.parts", to: "Lingua universal message" } on the request and response import paths, and leave native google->google passthrough byte-preserving. This is the smallest change that stops the dangling-$ref silent loss immediately, it matches the existing explicit-rejection precedent at convert.rs:60-76, it requires no universal model change, and it does not foreclose option (b) later. Option (b) should be pursued as a separate, deliberately designed universal tool-result media contract covering Anthropic, OpenAI, and Google together, not as a Google-driven addition.
    • Alternatives:
      • Option (b): add a universal tool-result media part (for example a ToolResultContentPart media list carrying { data | uri, media_type, name }) and map Google's parts onto it, preserving display_name as the member name. Upside: genuinely portable multimodal tool results, and the chat-completions and Anthropic importers could later converge on it. Downside: the $ref indirection has no analogue in Anthropic or OpenAI tool results, so the universal model must either keep the raw $ref string (which only Google can resolve, making the representation provider-coupled) or resolve it; it also expands the universal surface based on one provider's encoding, and it requires payload cases plus regenerated transform snapshots for every provider pair.
      • Option (c): resolve the $ref at import time by substituting the referenced part's media into FunctionResponse.response, then map the result onto an existing universal shape. Upside: no new universal type, and the output becomes self-contained. Downside: this is a lossy, semantics-changing rewrite of the provider's payload (the distinction between an inline payload and a named reference is destroyed, the export direction cannot reconstruct the original, and an unresolvable or duplicated display_name has no defined behavior); it contradicts AGENTS.md:19 and AGENTS.md:27.
      • Option (d): leave the current behavior and accept the silent drop. Rejected: it produces a dangling $ref in the transformed output, which is a correctness bug rather than a documented limitation, and it directly violates AGENTS.md:65.
    • Likely files:
      • crates/lingua/src/providers/google/convert.rs
      • crates/lingua/src/providers/google/generated.rs
      • crates/lingua/src/providers/google/adapter.rs
      • crates/lingua/src/universal/message.rs
      • crates/lingua/tests/google_server_tool_call.rs
      • crates/lingua/tests/openai_function_call_output_tool_name.rs
      • payloads/cases/params.ts
      • crates/coverage-report/src/requests_expected_differences.json
    • Validation commands:
      • cargo test -p lingua --lib providers::google
      • cargo test -p lingua --test google_server_tool_call
      • cargo test -p lingua --test openai_function_call_output_tool_name
      • cargo test -p lingua --test import_fixtures
      • make test-payloads
      • cargo test -p coverage-report --test cross_provider_test cross_provider_transformations_have_no_unexpected_failures
      • make typed-boundary-check
      • make typed-boundary-check-branch BASE=main

Provider-only changes

These native wire features are accepted and passed through unchanged, but cross-provider transformation is intentionally unsupported:

  • FunctionResponsePart.mediaProcessing (google-function-response-part-media-processing)
    • Native contract: mediaProcessing anywhere inside a Google part, including nested under functionResponse.parts, must be accepted by the generated types, preserved byte-for-byte on google->google passthrough, and rejected with ConvertError::UnsupportedMapping naming mediaProcessing when transformed to any other provider format, on the request, response, and streaming paths alike.
    • Scope decision: mediaProcessing selects how Google's own model consumes video inside provider-hosted inference; AGENTIC is explicitly model-driven dynamic navigation. It has no meaning outside Google and no target provider has an equivalent knob. The repository already treats the top-level field this way at convert.rs:63, so this item only extends the existing provider-only boundary to the newly reachable nesting level.
  • ToolType.MEDIA_PROCESSING (google-tool-type-media-processing)
    • Native contract: Both halves of a Google server-tool exchange must be handled consistently: toolCall and toolResponse, for every toolType including MEDIA_PROCESSING, must be accepted by the generated types, preserved byte-for-byte on google->google passthrough, and rejected with ConvertError::UnsupportedMapping naming the offending field when transformed to any other provider format.
    • Scope decision: ToolCall and ToolResponse with a ToolType describe Google-hosted search, maps, URL context, file search, and now media processing: provider-executed invisible tools whose results are produced by Google infrastructure. These are not caller-defined function tools and have no portable contract; the repository already classifies the toolCall half this way at convert.rs:67. This item extends the same boundary to the toolResponse half that the new variant makes reachable.
  • AudioTranscriptionConfig.sourceText (google-audio-transcription-source-text)
    • Native contract: A Google request whose generationConfig.audioTranscriptionConfig carries sourceText together with mode SMART must parse into the generated types, be detected as Google format, pass through google->google byte-for-byte, and be rejected with ConvertError::UnsupportedMapping naming generationConfig.audioTranscriptionConfig on any cross-provider transform.
    • Scope decision: sourceText configures Google's hosted SMART transcription pass, changing whether the candidate carries a complete replacement or an append delta. It is provider-executed audio understanding with no target-provider equivalent, and it is already inside an established provider-only boundary; this item keeps the native type current and pins the boundary with the new field present rather than widening it.

Validation

  • ./pipelines/generate-provider-types.sh google: success
  • make generate-types PROVIDER=google: success
  • Braintrust workflow trace: success
  • Claude integration plan: success
  • Structured plan validation: success
  • Human design blockers: true
  • Claude focused implementation: success
  • Immutable plan revalidation: success
  • Initial post-implementation Rust regeneration: success
  • Initial provider update path policy: success
  • Initial provider semantic policy: success
  • Initial post-implementation TypeScript regeneration: success
  • Initial formatting: success
  • Initial focused provider tests: success
  • Initial conditional generator tests: success
  • Initial build: failure
  • Initial clippy: failure
  • Bounded Claude mechanical repair: success
  • Effective mechanical validation source: post-repair
  • Effective mechanical validation: failure
  • make lingua-wasm: skipped
  • Unblocked payload capture cases: ``
  • Live capture (OpenAI): skipped
  • Live capture (Anthropic): skipped
  • Live capture (Google): skipped
  • Payload cross-provider transform capture: skipped
  • Payload fixture sync: skipped
  • make test-payloads: skipped
  • make typed-boundary-check: skipped
  • cargo test -p coverage-report --test cross_provider_test cross_provider_transformations_have_no_unexpected_failures: skipped
  • Claude read-only verification: skipped
  • Verification report validation: skipped
  • Verification verdict:
  • Recoverable binary patch archive: success
  • Patch artifact upload: success

Only safely scoped patches with a locally archived binary diff are published. Any incomplete AI phase or failed deterministic check produces a needs-review draft so work can continue from the PR branch.

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.

1 participant