Help us shape the future of Go! Take the 2026 Go Developer Survey

translate

package module
v0.0.4 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Oct 23, 2026 License: MIT Imports: 13 Imported by: 0

README

llm-translate

Zero-dependency Go library for bidirectional protocol translation between the Anthropic Messages API, OpenAI Chat Completions, and the OpenAI Responses API.

Features

  • Anthropic ↔ OpenAI Request & Response: Translation of messages, tool definitions, tool results, system prompts and vision content.
  • SSE Stream Translation: Real-time translation between OpenAI chunk deltas and Anthropic SSE events (message_start, content_block_start, content_block_delta, message_delta, message_stop).
  • OpenAI Responses ↔ Chat: Bidirectional translation for newer OpenAI Responses API backends to standard Chat completions.
  • Loss reports: every *WithReport conversion returns a machine-readable list of what the target surface could not represent.
  • Typed wire codecs: package wire decodes and re-encodes all three surfaces without losing unknown or vendor fields.
  • Pure Standard Library: Zero CGO, zero external dependencies.

Conversions support finite mappings, not every feature of each API. Consult loss reports and fidelity assessments for losses and unknowns. The wire round-trip guarantee applies to decoding and re-encoding the same surface, not semantic equivalence across protocols or providers.

Local realtime profile

This source includes a realtime package mapping a limited OpenAI GA audio session to Gemini Live. Its presence in this repository does not establish availability in a published release.

  • Input and output are raw PCM16, mono, little-endian at 24 kHz. The explicit format type is audio/pcm; an omitted rate means 24 kHz, and other declared rates are rejected. Input samples are forwarded unchanged with audio/pcm;rate=24000, relying on Gemini's documented provider-side input resampling. No local audio conversion is performed.
  • The profile requires manual input turns (turn_detection: null) and the Gemini voice Puck. It supports audio responses, confirmed interruption, continued input on the same session, and correlated function calls/results.
  • Continuation uses provider-delivered context, not accurate heard history. Accurate heard-history reconciliation remains unresolved. conversation.item.truncate is refused and requires closing the session; refusal or closure does not satisfy that requirement.
  • Callers serialize all Session calls and forward outputs in order. Transport, playback stop/queue flush, tool execution and billing remain caller-owned; close both sides on an Error with RequiresClose. Session usage is not established by this mapping.

Installation

Requires Go 1.26.5 or newer.

go get github.com/kayiik/llm-translate

go get installs published source; it does not necessarily include every addition in this checkout. Published dependency availability and the current source are separate integration concerns.

Quick Start

Convert Anthropic Messages Request to OpenAI
import "github.com/kayiik/llm-translate"

openaiMessages := translate.AnthropicMessagesToOpenAI(req.Messages, req.System)
tools := translate.AnthropicToolsToOpenAI(req.Tools)
Stream OpenAI Chunks as Anthropic SSE
translate.OpenAIStreamToAnthropicSSE(chunksIter, "claude-3-5-sonnet", func(sseEvent string) {
    w.Write([]byte(sseEvent))
    w.(http.Flusher).Flush()
})

This legacy helper may estimate output tokens from text; its emitted usage is not necessarily provider-reported usage. Prefer the observed stream APIs when source usage and terminal evidence matter.

Stream Anthropic SSE as OpenAI Chunks

AnthropicSSEToOpenAIChunks emits no usage and ends every stream with a finish chunk. AnthropicSSEToOpenAIChunksWithOptions emits the same chunks for a stream that completes, and returns a *StreamReport once lines is exhausted:

report := translate.AnthropicSSEToOpenAIChunksWithOptions(lines, model, emit,
    translate.StreamOptions{IncludeUsage: true})
var upstream *translate.StreamError
switch {
case report.Completed:
    // message_stop arrived and no error event did
case errors.As(report.Err, &upstream):
    // an error event: upstream.Type, upstream.Message
case errors.Is(report.Err, translate.ErrStreamTruncated):
    // the source ended before message_stop
}
  • report.Usage holds the input, cache creation, cache read and output tokens the source reported, also for a failed stream. PromptTokens() sums the three input counts, as Chat's prompt_tokens does.
  • IncludeUsage adds the chunk OpenAI sends for stream_options.include_usage: "choices": [] with prompt_tokens, completion_tokens, total_tokens and prompt_tokens_details.cached_tokens (the cache reads). Every other chunk then carries "usage": null. The chunk is sent only for a completed stream whose source reported usage.
  • A stream that is not Completed ends where its source did, without a finish or usage chunk, so a failure cannot pass for a complete answer.

Loss reports

Each *WithReport function returns the converted value with a Report. A Loss names the source value that changed with a dot-separated path into the source payload (numeric segments index arrays), a class (unsupported, dropped, approximated, renamed) and a severity:

  • material: the conversion changed or omitted meaning. RejectMaterialLoss turns these into an error.
  • advisory: the answer is intact. A product that depends on the value can still reject it by path, for example with a policy rule on **.reasoning.
result := translate.ChatToResponsesWithReport(model, messages, options, false)
if err := result.RejectMaterialLoss(); err != nil {
    return err
}
Vendor fields inside messages

Conversions carry a vendor field when the target can represent it, and report it at its exact path when they cannot:

Field Carried Otherwise reported as
Chat tool call extra_content.google.thought_signature (Gemini) never; no other surface has a place for it material, e.g. messages.3.tool_calls.0.extra_content.google.thought_signature
Chat tool call function.thought_signature or call-level thought_signature (other Gemini providers) never material, e.g. messages.3.tool_calls.0.function.thought_signature, messages.3.tool_calls.0.thought_signature
Chat message reasoning_details never advisory, e.g. messages.2.reasoning_details
Chat message reasoning_content never advisory, e.g. messages.2.reasoning_content
Chat content part cache_control to Anthropic, on text parts advisory, e.g. messages.0.content.1.cache_control
Anthropic block cache_control never; Chat has no breakpoints material (unsupported) by AnthropicRequestToOpenAIWithReport, e.g. system.0.cache_control

A dropped thought signature is material because Gemini needs it back on the next turn of a tool call. Each location that carries one is reported, and empty values carry nothing, so they are never reported. Response and stream conversions use the same field paths under choices.N.message and choices.N.delta. In a stream the index after tool_calls is the call's index field, which identifies it across chunks.

A dropped reasoning item or thinking block keeps its own path with a .reasoning suffix, such as output.2.reasoning, content.0.reasoning or messages.1.content.0.reasoning, so one rule matches reasoning on every surface. These losses are advisory, and they come in addition to whatever the conversion already reported for the item.

cache_control from Chat to Anthropic

OpenAIMessagesToAnthropic and its WithReport form turn user, assistant and tool content that has a cache_control text part into Anthropic text blocks. Text up to and including each marked part becomes one block carrying that cache_control, joined exactly as before, so the cached prefix is the one the Chat request marked. Content without cache_control converts as before.

The system prompt is returned as a string, so only OpenAIMessagesToAnthropicWithReport can carry system breakpoints: it sets AnthropicMessages.SystemBlocks, which should be sent as the request's system value instead of System. cache_control on a part the conversion drops, such as an image, is reported as advisory.

Structured output between Chat and Responses

A Chat response_format and a Responses text.format ask for the same structure, so ChatToResponses carries one as the other and ResponsesRequestToChat carries it back. A json_schema format's name, description, schema and strict sit in its json_schema object in Chat and directly in the format in Responses. A format of type text asks for the default and is not emitted. The rename is reported as advisory. A format with a member neither API defines is reported as material, and so is a Responses text member other than format, such as verbosity.

The adapting assessment checks the finite format/function envelopes: required format name and object schema, typed description/strict controls, and precise unknown member paths. Schema contents themselves remain verbatim JSON data, including refs and custom vocabulary; this does not certify a target model's schema support. Empty object schemas and explicit strict:false are retained.

Function strict defaults differ: Chat→Responses makes Chat's omitted/null non-strict default explicit as strict:false. Missing Chat parameters use the required nullable Responses field; explicit native Responses null parameters omit optional Chat parameters on return. A supplied Chat parameter schema must be an object. Responses' omitted/null function strict policy cannot be ported to Chat's default without guessing; AssessConversion marks its exact path unknown until true/false is supplied or a native Responses target is used. Legacy value-producing APIs remain available; admission must inspect the assessment's feasibility and unknown paths, not merely a nonempty result.

Chat/Responses conversion retains image detail and responses containing both text and function calls, including when rendering buffered Chat chunks. Content parts a conversion cannot represent are reported at their source paths. A foreign user part passed through unchanged is still reported as unsupported; passthrough alone does not establish that the target accepts its type.

Chat user file parts and Responses input_file parts retain filename and exactly one file_data or file_id carrier across their nested/flat envelopes. Provider file IDs retain their identity; callers must not treat them as portable across providers. Responses-only file_url remains a native requirement. Messages user URL images and plain base64 PDF documents map to Chat image/file parts in content order. Document titles, context, citations and cache controls still report material loss when that edge cannot carry them. These are pure carrier mappings: no upload service, remote download or media codec is involved.

Usage conversion between Chat and Responses retains supplied cached_tokens, cache_write_tokens and reasoning_tokens detail counters. Responses→Chat omits missing/null optional counters and retains explicitly reported zero. Chat→Responses preserves the legacy required detail objects and cached/reasoning fields, using synthetic zero placeholders when absent from the source. Those placeholders are wire compatibility values, not provider-reported measurements; source-presence observations use the source-side APIs below. Cache-write detail is carried when supplied. Chat→Responses preserves valid-source aggregate aliases and the legacy absent/zero-total fallback. Responses→Chat derives a total only when it is absent and preserves an explicitly reported zero total.

Both directions accept exact aggregate json.Number integers without floating-point conversion, including int64-sized values. Invalid supplied aggregate counts or an overflowing derived total leave target usage unknown (null); WithReport identifies the material source path instead of fabricating measured zeros. Direction-specific fallback and required wire placeholders remain separate from source observation provenance.

Fidelity and source observations

AssessConversion is an additive assessment of the four named request edges. Pass the full source payload, PhaseRequest or PhaseResponse, and explicit FidelityRequirements. The conversion ID always names the originating request direction: ConversionChatToResponses with PhaseResponse assesses a Responses answer converted back to Chat. The result distinguishes evaluated features, known losses, and UnknownPaths; a loss-free legacy helper alone is not proof of complete fidelity. Model/endpoint capabilities and admission remain the caller's responsibility. Native passthrough does not need a conversion and keeps its unknown fields.

Opaque encrypted/signed continuation state infers replay requirements even with an empty visible summary. Display-only reasoning retains advisory semantics. Unknown nested controls cannot become supported merely because their enclosing message/tool is supported. Schema envelopes that are carried unchanged do not establish target-model schema-vocabulary support.

ObserveUsage extracts a native usage object before conversion placeholders. CountObservation distinguishes absent, invalid, measured zero, reported and derived values. ObserveUsageWithSource(..., UsageSynthetic) labels observations when only a synthesized target object is available. OpenAI cache/read/write and reasoning are subsets of inclusive totals; Anthropic input categories are disjoint and TTL children partition writes. Incomplete source categories remain unknown rather than being filled with measured zeros. UsageAccumulator merges cumulative updates by replacement, retaining omitted counters independently of bounded raw evidence (up to 32 members, 4 KiB per member, 16 KiB content total).

ObserveUsageWithDetails / ObserveUsageWithSourceAndDetails return a UsageObservationWithDetails wrapper, preserving the existing observation and integer-usage layouts. Its Details adds Chat prompt/completion audio and accepted/rejected prediction tokens, plus Messages server web-search/web-fetch request counts where reported. UsageAccumulator.SnapshotWithDetails retains these counters independently of truncated raw evidence. Audio/prediction values are subsets of inclusive totals, not additions; web requests are operation counts, not tokens. Fetch is documented by the selected pinned beta schema, not a guarantee for every stable deployment/model. No unsupported Responses counter counterparts, wire detail fields, prices or cost estimates are invented.

AnthropicSSEToOpenAIChunksObserved and OpenAIStreamToAnthropicSSEObserved combine event-time assessments, source usage and terminal quality without buffering a whole stream. The observer runs before affected source events are converted and at finalization. The Chat input iterator must include [DONE]; missing terminal evidence is truncation, not success. Requested final Chat usage is emitted only when inclusive source totals can be determined. The legacy stream functions keep their existing behavior. Caller transport framing, cancellation, delivery state and no-replay-after-output enforcement remain outside these pure converters.

OpenAIStreamToResponsesSSEObserved incrementally renders Chat JSON chunks plus [DONE] as typed Responses SSE records, including text, refusal and interleaved client function calls. Its request argument supplies only supported response envelope fields; generated response/item IDs do not create stored lineage. ResponsesSSEToOpenAIChunksObserved accepts native Responses JSON event data and emits Chat JSON chunks; its caller adds [DONE] only after a successful terminal result and delivery checks. Required state, hosted tools, citations and unassessed semantics are reported before affected output and stop these bridges. Source usage remains distinct from required wire placeholders, and terminal snapshots do not duplicate already emitted deltas. Both emit during source consumption; per-item text/arguments are assembled for done/terminal objects, without buffering the source generation before delivery. Existing buffered helpers remain unchanged.

Package wire

github.com/kayiik/llm-translate/wire holds typed request, response and stream structs for the three surfaces:

Surface Request Response Stream
Chat Completions ChatRequest ChatResponse ChatChunk
Anthropic Messages AnthropicRequest AnthropicResponse AnthropicEvent
OpenAI Responses ResponsesRequest ResponsesResponse ResponsesEvent

Each has a Decode… function, such as wire.DecodeChatRequest, and encodes with json.Marshal. Stream types hold the JSON data of one SSE event, without the data: framing.

req, err := wire.DecodeChatRequest(body)
if err != nil {
    return err
}
for _, call := range req.Messages[len(req.Messages)-1].ToolCalls {
    log.Println(call.ID, call.ThoughtSignature())
}
out, err := json.Marshal(req) // equal to body as JSON
Round-trip guarantee

Decoding and then encoding reproduces the input as JSON: both parse to the same value, with numbers compared by their literal text. Member order and insignificant whitespace are not kept.

  • Every modeled object keeps the members it does not model in Extra and writes them back.
  • A member whose value does not have the modeled shape, such as null, an empty identifier, or a number where a string is expected, stays in Extra verbatim rather than failing the decode. When a typed field is set, it wins over an Extra entry of the same name.
  • Polymorphic members keep their form: content as a string or parts, tool_choice as a string or an object, stop as a string or an array.
  • Numbers are json.Number, so 1.0 stays 1.0.
  • Optional identifiers are strings omitted when empty. Text that may be empty on purpose, such as streamed arguments, is a *string, and booleans are *bool. A nil slice is omitted, while an empty non-nil slice encodes as [].
Vendor fields

The fields products depend on are typed rather than left in Extra:

  • ChatToolCall.ExtraContent.Google.ThoughtSignature for Gemini's extra_content.google.thought_signature. The ChatToolCall.ThoughtSignature() helper returns the first non-empty signature from that field, then function.thought_signature, then a call-level thought_signature; the last two stay in Extra.
  • ChatMessage.ReasoningDetails, the raw reasoning_details array.
  • CacheControl on ChatContentPart, AnthropicBlock (content, system and nested tool_result blocks) and AnthropicTool.

License

MIT

Documentation

Overview

Package translate round-trips between the Anthropic Messages API and the OpenAI Chat Completions schema. The gateway speaks OpenAI natively; this is the single translator that lets Claude Code and other Anthropic clients talk to it, and lets the native Anthropic backend be called from OpenAI-shaped internals.

Faithful port of llmgw.api.anthropic_translate. JSON objects are modeled as map[string]any / []any to mirror the Python dict handling.

Index

Constants

This section is empty.

Variables

View Source
var ErrChatStreamTruncated = protocolErrorf("Chat", "stream ended without [DONE]")
View Source
var ErrResponsesStreamTruncated = protocolErrorf("Responses", "stream ended without a terminal event")
View Source
var ErrStreamTruncated = protocolErrorf("Anthropic", "stream ended without message_stop")

ErrStreamTruncated is the error of a source that ended without message_stop and without an error event, such as a dropped connection.

Functions

func AnthropicMessagesToOpenAI

func AnthropicMessagesToOpenAI(messages []any, system any) []map[string]any

AnthropicMessagesToOpenAI converts Anthropic messages + system into OpenAI messages.

func AnthropicRequestToOpenAI

func AnthropicRequestToOpenAI(payload map[string]any) ([]map[string]any, map[string]any, []string)

AnthropicRequestToOpenAI strictly converts the Messages core profile. The returned paths name every value that an OpenAI Chat target would lose.

func AnthropicResponseToOpenAI

func AnthropicResponseToOpenAI(response map[string]any, model string) map[string]any

AnthropicResponseToOpenAI converts a non-streaming Anthropic Message to an OpenAI completion.

func AnthropicSSEToOpenAIChunks

func AnthropicSSEToOpenAIChunks(lines func() (string, bool), model string, emit func(string))

AnthropicSSEToOpenAIChunks translates Anthropic SSE data lines into OpenAI chat.completion.chunk JSON strings, invoking emit for each. It emits no usage and ends every stream with a finish chunk, even one that failed or was cut short; AnthropicSSEToOpenAIChunksWithOptions reports both.

func AnthropicStopReasonToOpenAI

func AnthropicStopReasonToOpenAI(reason any) string

AnthropicStopReasonToOpenAI maps an Anthropic stop_reason to an OpenAI finish_reason.

func AnthropicToolChoiceToOpenAI

func AnthropicToolChoiceToOpenAI(choice map[string]any) any

AnthropicToolChoiceToOpenAI converts Anthropic tool_choice to OpenAI's.

func AnthropicToolsToOpenAI

func AnthropicToolsToOpenAI(tools []any) []any

AnthropicToolsToOpenAI converts Anthropic tools to OpenAI function tools.

func ChatResponseToResponses

func ChatResponseToResponses(model string, chat map[string]any) map[string]any

ChatResponseToResponses converts the gateway's internal Chat Completions result back into the public Responses API envelope.

func ChatResponseToResponsesWithRequest

func ChatResponseToResponsesWithRequest(
	model string, chat map[string]any, request map[string]any,
) map[string]any

func ChatToResponses

func ChatToResponses(model string, messages []map[string]any, kw map[string]any, stream bool) map[string]any

ChatToResponses converts a Chat Completions request body into a Responses API request body.

func FailedResponseEnvelope

func FailedResponseEnvelope(
	model, id, code, message string, request map[string]any,
) map[string]any

func NewResponseEnvelope

func NewResponseEnvelope(
	model, id, status string, output []any, request map[string]any,
) map[string]any

NewResponseEnvelope creates the schema-complete Response shape used whenever the gateway adapts a Chat-only backend or synthesizes a terminal failure.

func OpenAIFinishReasonToAnthropic

func OpenAIFinishReasonToAnthropic(reason any) string

OpenAIFinishReasonToAnthropic maps an OpenAI finish_reason to Anthropic stop_reason.

func OpenAIMessagesToAnthropic

func OpenAIMessagesToAnthropic(messages []map[string]any) (string, []map[string]any)

OpenAIMessagesToAnthropic splits an OpenAI message list into Anthropic (system, messages). cache_control on message content parts is carried to the Anthropic blocks; cache_control on system parts needs OpenAIMessagesToAnthropicWithReport, because the system returned here is a plain string.

func OpenAIResponseToAnthropic

func OpenAIResponseToAnthropic(response map[string]any, model string) map[string]any

OpenAIResponseToAnthropic converts a non-streaming OpenAI completion to an Anthropic Message.

func OpenAIStreamToAnthropicSSE

func OpenAIStreamToAnthropicSSE(chunks func() (string, bool), model string, emit func(string))

OpenAIStreamToAnthropicSSE translates OpenAI SSE chunk payloads (JSON strings, data: prefix already stripped) into Anthropic SSE event blocks, invoking emit for each output event.

func OpenAIToolsToAnthropic

func OpenAIToolsToAnthropic(tools []any) []any

OpenAIToolsToAnthropic converts OpenAI tools to Anthropic tool shape.

func PreferredEndpoint

func PreferredEndpoint(supportedEndpoints []string) string

PreferredEndpoint decides which OpenAI-family endpoint a model should use given its published supported_surfaces. Returns "responses" only when the model advertises /responses but NOT /chat/completions; otherwise "chat" (the safe default, including when the list is empty/unknown).

func RejectMaterialLoss

func RejectMaterialLoss(report Report) error

RejectMaterialLoss returns an error containing every material loss in the report.

func ResponsesRequestToChat

func ResponsesRequestToChat(
	payload map[string]any,
) ([]map[string]any, map[string]any, error)

ResponsesRequestToChat converts supported Responses input/history/tools into the gateway's internal Chat representation. Built-in Responses-only tools fail explicitly instead of being silently flattened. A text.format becomes the response_format that asks Chat for the same structure.

func ResponsesToChat

func ResponsesToChat(model string, resp map[string]any) map[string]any

ResponsesToChat converts a Responses API object into a chat.completion.

func ResponsesToChatChunks

func ResponsesToChatChunks(model string, resp map[string]any) []string

ResponsesToChatChunks renders a (buffered) Responses reply as a small sequence of chat.completion.chunk payloads for the streaming code path.

Types

type AnthropicMessages

type AnthropicMessages struct {
	System   string
	Messages []map[string]any
	// SystemBlocks is set when a system or developer text part carries
	// cache_control. It holds the System text as Anthropic text blocks split
	// at the breakpoints, each carrying its cache_control. Send it as the
	// request's system value instead of System, or the breakpoints are lost.
	SystemBlocks []any `json:",omitempty"`
}

AnthropicMessages is the two-part request value returned by Anthropic conversions.

type Assessment added in v0.0.3

type Assessment struct {
	Conversion   ConversionID
	Phase        ObservationPhase
	Feasibility  Feasibility
	Requirements FidelityRequirements
	Evaluated    []FidelityFeature
	UnknownPaths []string
	Report       Report
}

Assessment is conversion evidence, not admission or target-model capability. UnknownPaths are unevaluated source semantics; a loss-free legacy conversion alone is not evidence that those semantics were preserved.

func AssessConversion added in v0.0.3

func AssessConversion(id ConversionID, phase ObservationPhase, payload map[string]any, requirements FidelityRequirements) Assessment

type CompletionQuality added in v0.0.3

type CompletionQuality string
const (
	CompletionUnknown   CompletionQuality = "unknown"
	CompletionPartial   CompletionQuality = "partial"
	CompletionComplete  CompletionQuality = "complete"
	CompletionTruncated CompletionQuality = "truncated"
	CompletionFailed    CompletionQuality = "failed"
)

type ConversionID added in v0.0.3

type ConversionID string

ConversionID identifies an existing request edge throughout its response.

const (
	ConversionChatToResponses ConversionID = "chat-to-responses"
	ConversionResponsesToChat ConversionID = "responses-to-chat"
	ConversionChatToMessages  ConversionID = "chat-to-messages"
	ConversionMessagesToChat  ConversionID = "messages-to-chat"
)

type ConversionResult

type ConversionResult[T any] struct {
	Value  T      `json:"value"`
	Report Report `json:"report"`
}

ConversionResult couples an existing conversion value to its compatibility report.

func AnthropicRequestToOpenAIWithReport

func AnthropicRequestToOpenAIWithReport(payload map[string]any) ConversionResult[OpenAIChatRequest]

AnthropicRequestToOpenAIWithReport delegates to AnthropicRequestToOpenAI and gives its incompatible paths structured material-loss semantics.

func AnthropicResponseToOpenAIWithReport

func AnthropicResponseToOpenAIWithReport(response map[string]any, model string) ConversionResult[map[string]any]

AnthropicResponseToOpenAIWithReport converts an Anthropic response with fidelity findings.

func ChatResponseToResponsesWithReport

func ChatResponseToResponsesWithReport(model string, chat map[string]any) ConversionResult[map[string]any]

ChatResponseToResponsesWithReport converts a Chat response with fidelity findings.

func ChatResponseToResponsesWithRequestAndReport

func ChatResponseToResponsesWithRequestAndReport(model string, chat map[string]any, request map[string]any) ConversionResult[map[string]any]

ChatResponseToResponsesWithRequestAndReport also uses request fields when constructing the Responses envelope.

func ChatToResponsesWithReport

func ChatToResponsesWithReport(model string, messages []map[string]any, kw map[string]any, stream bool) ConversionResult[map[string]any]

ChatToResponsesWithReport converts a Chat request and reports known fallback synthesis.

func OpenAIMessagesToAnthropicWithReport

func OpenAIMessagesToAnthropicWithReport(messages []map[string]any) ConversionResult[AnthropicMessages]

OpenAIMessagesToAnthropicWithReport converts messages and reports known normalizations performed by the existing converter. cache_control on text parts is carried, including on system parts through SystemBlocks; vendor fields Anthropic cannot represent are reported at their paths.

func OpenAIResponseToAnthropicWithReport

func OpenAIResponseToAnthropicWithReport(response map[string]any, model string) ConversionResult[map[string]any]

OpenAIResponseToAnthropicWithReport converts a Chat response with fidelity findings.

func ResponsesRequestToChatWithReport

func ResponsesRequestToChatWithReport(payload map[string]any) (ConversionResult[OpenAIChatRequest], error)

ResponsesRequestToChatWithReport preserves existing validation errors and reports unsupported top-level fields as material compatibility loss.

func ResponsesToChatChunksWithReport

func ResponsesToChatChunksWithReport(model string, response map[string]any) ConversionResult[[]string]

ResponsesToChatChunksWithReport reports that a complete response is rendered as buffered chunks rather than preserving original event boundaries.

func ResponsesToChatWithReport

func ResponsesToChatWithReport(model string, response map[string]any) ConversionResult[map[string]any]

ResponsesToChatWithReport converts a Responses object with fidelity findings.

func (ConversionResult[T]) RejectMaterialLoss

func (r ConversionResult[T]) RejectMaterialLoss() error

RejectMaterialLoss returns an error when this result contains material loss.

type CountObservation added in v0.0.3

type CountObservation struct {
	Value      int64
	Presence   CountPresence
	Provenance UsageProvenance
	Path       string
}

type CountPresence added in v0.0.3

type CountPresence string
const (
	CountAbsent  CountPresence = "absent"
	CountPresent CountPresence = "present"
	CountInvalid CountPresence = "invalid"
)

type Feasibility added in v0.0.3

type Feasibility string
const (
	FeasibilitySupported   Feasibility = "supported"
	FeasibilityUnsupported Feasibility = "unsupported"
	FeasibilityUnknown     Feasibility = "unknown"
)

type FidelityFeature added in v0.0.3

type FidelityFeature string
const (
	FidelityContent FidelityFeature = "content"
	FidelityTools   FidelityFeature = "tools"
	FidelitySchema  FidelityFeature = "schema"
	FidelityBilling FidelityFeature = "billing"
	FidelityReplay  FidelityFeature = "replay"
)

type FidelityRequirements added in v0.0.3

type FidelityRequirements struct {
	Content, Tools, Schema, Billing, Replay bool
}

func InferFidelityRequirements added in v0.0.3

func InferFidelityRequirements(payload map[string]any) FidelityRequirements

type Loss

type Loss struct {
	Path     string       `json:"path"`
	Class    LossClass    `json:"class"`
	Severity LossSeverity `json:"severity"`
	Detail   string       `json:"detail"`
}

Loss is one deterministic, machine-readable compatibility finding.

type LossClass

type LossClass string

LossClass describes how a source value differs after conversion.

const (
	LossUnsupported  LossClass = "unsupported"
	LossDropped      LossClass = "dropped"
	LossApproximated LossClass = "approximated"
	LossRenamed      LossClass = "renamed"
)

type LossSeverity

type LossSeverity string

LossSeverity distinguishes fidelity failures from useful compatibility notes.

const (
	LossMaterial LossSeverity = "material"
	LossAdvisory LossSeverity = "advisory"
)

type MaterialLossError

type MaterialLossError struct {
	Losses []Loss
}

MaterialLossError contains the material subset of a rejected report.

func (*MaterialLossError) Error

func (e *MaterialLossError) Error() string

type ObservationPhase added in v0.0.3

type ObservationPhase string
const (
	PhaseRequest     ObservationPhase = "request"
	PhaseResponse    ObservationPhase = "response"
	PhaseStreamEvent ObservationPhase = "stream-event"
	PhaseFinal       ObservationPhase = "final"
)

type OpenAIChatRequest

type OpenAIChatRequest struct {
	Messages []map[string]any
	Keywords map[string]any
}

OpenAIChatRequest is the two-part request value returned by Chat conversions.

type Report

type Report struct {
	Losses []Loss `json:"losses,omitempty"`
}

Report describes all known compatibility losses for one conversion. Losses are sorted by path, severity, class, then detail.

func AnthropicSSEToOpenAIChunksWithReport

func AnthropicSSEToOpenAIChunksWithReport(lines func() (string, bool), model string, emit func(string)) Report

AnthropicSSEToOpenAIChunksWithReport delegates stream conversion and reports usage fields that the existing Chat chunk adapter does not emit, and the thinking blocks it drops.

func NewReport

func NewReport(losses ...Loss) Report

NewReport normalizes ordering and removes duplicate losses.

func OpenAIStreamToAnthropicSSEWithReport

func OpenAIStreamToAnthropicSSEWithReport(chunks func() (string, bool), model string, emit func(string)) Report

OpenAIStreamToAnthropicSSEWithReport delegates stream conversion and reports the token estimate used when chunks do not carry usage, and the vendor fields of deltas that Anthropic events cannot carry.

func (Report) HasMaterialLoss

func (r Report) HasMaterialLoss() bool

HasMaterialLoss reports whether conversion changed or omitted material semantics.

type StreamError

type StreamError struct {
	Type    string // Anthropic's error type, such as overloaded_error
	Message string
}

StreamError is an error event received from an Anthropic stream, such as an overloaded_error after the response began.

func (*StreamError) Error

func (e *StreamError) Error() string

type StreamObservation added in v0.0.3

type StreamObservation struct {
	Assessment Assessment
	Usage      UsageObservation
	Completed  bool
	Err        error
}

func AnthropicSSEToOpenAIChunksObserved added in v0.0.3

func AnthropicSSEToOpenAIChunksObserved(lines func() (string, bool), model string, emit func(string), opts StreamObservationOptions) StreamObservation

func OpenAIStreamToAnthropicSSEObserved added in v0.0.3

func OpenAIStreamToAnthropicSSEObserved(chunks func() (string, bool), model string, emit func(string), opts StreamObservationOptions) StreamObservation

func OpenAIStreamToResponsesSSEObserved added in v0.0.3

func OpenAIStreamToResponsesSSEObserved(chunks func() (string, bool), model string, request map[string]any, emit func(string), opts StreamObservationOptions) StreamObservation

OpenAIStreamToResponsesSSEObserved emits typed Responses SSE incrementally. It retains emitted item fields for protocol-required terminal objects, not a queue of source events or a generation that must finish before delivery.

func ResponsesSSEToOpenAIChunksObserved added in v0.0.3

func ResponsesSSEToOpenAIChunksObserved(events func() (string, bool), model string, emit func(string), opts StreamObservationOptions) StreamObservation

ResponsesSSEToOpenAIChunksObserved consumes complete JSON event data and emits Chat JSON chunks. The caller owns SSE framing and the final [DONE].

type StreamObservationOptions added in v0.0.3

type StreamObservationOptions struct {
	IncludeUsage bool
	Requirements FidelityRequirements
	Observe      func(StreamObservation)
}

type StreamOptions

type StreamOptions struct {
	// IncludeUsage emits what OpenAI sends for stream_options.include_usage:
	// after the finish chunk, a chunk with empty choices and the usage of the
	// whole request, and "usage": null on every other chunk. The usage chunk
	// is sent only for a completed stream whose source reported usage, so it
	// never carries a partial or invented count.
	IncludeUsage bool
}

StreamOptions selects optional output of AnthropicSSEToOpenAIChunksWithOptions.

type StreamReport

type StreamReport struct {
	// Usage holds the latest count of each kind the source reported, also
	// for a stream that failed, and is zero when the source reported none.
	Usage Usage
	// Completed is true when message_stop arrived and no error event did.
	Completed bool
	// Err is nil when Completed. Otherwise it is the *StreamError of the
	// first error event, or ErrStreamTruncated.
	Err error
}

StreamReport tells how a translated stream ended.

func AnthropicSSEToOpenAIChunksWithOptions

func AnthropicSSEToOpenAIChunksWithOptions(lines func() (string, bool), model string, emit func(string), opts StreamOptions) *StreamReport

AnthropicSSEToOpenAIChunksWithOptions translates like AnthropicSSEToOpenAIChunks and, once lines is exhausted, returns a report that is never nil.

Without options, a completed stream gets exactly the chunks AnthropicSSEToOpenAIChunks emits; StreamOptions describes what IncludeUsage adds. Any other stream ends where its source did, without a finish chunk, so a failed or truncated answer cannot pass for a complete one. Its chunks all come from source events: when an error arrives first, nothing has been emitted and the caller can still respond with an error status.

type Usage

type Usage struct {
	InputTokens              int `json:"input_tokens"`
	CacheCreationInputTokens int `json:"cache_creation_input_tokens"`
	CacheReadInputTokens     int `json:"cache_read_input_tokens"`
	OutputTokens             int `json:"output_tokens"`
}

Usage holds the token counts an Anthropic stream reported. Anthropic counts cache writes and cache reads apart from InputTokens, so the Chat prompt_tokens is the sum of all three.

func (Usage) PromptTokens

func (u Usage) PromptTokens() int

PromptTokens returns the Chat prompt_tokens, which also counts the input read from or written to the cache.

func (Usage) TotalTokens

func (u Usage) TotalTokens() int

TotalTokens returns the Chat total_tokens.

type UsageAccumulator added in v0.0.3

type UsageAccumulator struct {
	// contains filtered or unexported fields
}

UsageAccumulator replaces cumulative source fields, retaining omitted fields. It is single-consumer state, like the existing stream converters.

func NewUsageAccumulator added in v0.0.3

func NewUsageAccumulator(dialect UsageDialect) *UsageAccumulator

func (*UsageAccumulator) Merge added in v0.0.3

func (a *UsageAccumulator) Merge(raw any)

func (*UsageAccumulator) Snapshot added in v0.0.3

func (*UsageAccumulator) SnapshotWithDetails added in v0.0.3

func (a *UsageAccumulator) SnapshotWithDetails(quality CompletionQuality) UsageObservationWithDetails

type UsageDetails added in v0.0.3

type UsageDetails struct {
	InputAudioTokens, OutputAudioTokens                CountObservation
	AcceptedPredictionTokens, RejectedPredictionTokens CountObservation
	WebSearchRequests, WebFetchRequests                CountObservation
}

UsageDetails contains only documented source details, not extra totals. Web request counters have operation units; all other fields have token units.

type UsageDialect added in v0.0.3

type UsageDialect string
const (
	UsageDialectChat      UsageDialect = "chat"
	UsageDialectResponses UsageDialect = "responses"
	UsageDialectMessages  UsageDialect = "messages"
)

type UsageObservation added in v0.0.3

type UsageObservation struct {
	Dialect                                                             UsageDialect
	Source                                                              string
	Quality                                                             CompletionQuality
	InputTokens, OutputTokens, TotalTokens                              CountObservation
	BaseInputTokens, CacheReadTokens, CacheWriteTokens, ReasoningTokens CountObservation
	CacheWrite5mTokens, CacheWrite1hTokens                              CountObservation
	Raw                                                                 map[string]json.RawMessage
	RawTruncated                                                        bool
	Issues                                                              []string
}

UsageObservation describes source evidence, independent of target-required wire placeholders. Raw is a bounded, detached copy of usage members only.

func ObserveUsage added in v0.0.3

func ObserveUsage(dialect UsageDialect, raw any, quality CompletionQuality) UsageObservation

func ObserveUsageWithSource added in v0.0.3

func ObserveUsageWithSource(dialect UsageDialect, raw any, quality CompletionQuality, source UsageProvenance) UsageObservation

type UsageObservationWithDetails added in v0.0.3

type UsageObservationWithDetails struct {
	UsageObservation
	Details UsageDetails
}

UsageObservationWithDetails extends observations without changing the layout of the existing UsageObservation or the legacy integer Usage type.

func ObserveUsageWithDetails added in v0.0.3

func ObserveUsageWithDetails(dialect UsageDialect, raw any, quality CompletionQuality) UsageObservationWithDetails

func ObserveUsageWithSourceAndDetails added in v0.0.3

func ObserveUsageWithSourceAndDetails(dialect UsageDialect, raw any, quality CompletionQuality, source UsageProvenance) UsageObservationWithDetails

type UsageProvenance added in v0.0.3

type UsageProvenance string
const (
	UsageReported  UsageProvenance = "reported"
	UsageDerived   UsageProvenance = "derived"
	UsageSynthetic UsageProvenance = "synthetic"
)

Directories

Path Synopsis
Package realtime maps a bounded OpenAI GA audio session to Gemini Live v1beta.
Package realtime maps a bounded OpenAI GA audio session to Gemini Live v1beta.
Package wire holds typed request, response and stream-chunk structs for the OpenAI Chat Completions, OpenAI Responses and Anthropic Messages surfaces.
Package wire holds typed request, response and stream-chunk structs for the OpenAI Chat Completions, OpenAI Responses and Anthropic Messages surfaces.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL