Skip to content

Typed ProviderMetadata enum + drop the google-fc- magic-string coupling #57

Description

@yuanhao

Deferred follow-ups from #33 (items 4 and 5). Items 1–3 shipped in #55 / v0.9.0.

1. Replace Option<serde_json::Value> with a typed ProviderMetadata enum

Content::ToolCall.provider_metadata is an untyped bag with no schema, no namespacing, and no compile-time link between metadata shape and producing provider. Suggested shape (from #33):

#[derive(Clone, Debug, Serialize, Deserialize)]
#[serde(tag = "provider", rename_all = "snake_case")]
pub enum ProviderMetadata {
    Gemini { thought_signature: String },
    // Anthropic { cache_control: ... },
    // OpenAi { reasoning_id: String },
}

Implementation notes:

  • Needs a serde back-compat path for persisted messages that carry the current raw-JSON shape (untagged fallback or a migration).
  • Both google.rs paths change (capture in the stream loop, echo in content_to_google_parts / build_request_body).
  • Since v0.9.0, Content::ToolCall is #[non_exhaustive] and constructed via Content::tool_call_with_metadata(), so this migration is no longer a hard downstream break — it can ship in any minor release.

2. Drop the google-fc- magic-string coupling

content_to_google_parts and build_request_body detect synthetic tool-call IDs via id.starts_with("google-fc-"). Fold a was_synthetic marker into the typed metadata (rides along naturally with item 1) instead of coupling formatter and serializer through a literal prefix.

When to do this

Trigger: the moment a second provider needs provider_metadata (e.g. OpenAI reasoning IDs) — do both items together before shipping that provider, since that is when the key-collision risk becomes real. Until then this is not urgent.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions