Repository navigation
feat: audit hardening — streaming/retry fixes, canonical mapping, Anthropic adaptive thinking, observability, documents, RunTools, CountTokens - #16
Merged
Conversation
…, terminal oversize, redirect downgrade
…ool deltas, unmapped incomplete reasons, empty stream args, Gemini image safety
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ni parametersJsonSchema, Gemini TTS URL, raw STT text formats
…tokens only via ThinkingBudget
…tion for gateways
…on, deep-copied cache, ListModels honours retry policy and budget
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
A full audit of the SDK followed by six milestones of fixes and features. Every change was test-first: a failing reproduction test (RED), then the fix (GREEN), then lint, the race suite and a weakened-test scan, then an independent review per stage. Each milestone's commits are kept separate so the history stays reviewable. Squash-merge as usual.
Behaviour changes callers may notice are listed under⚠️ below.
M1: Streaming and retry core (bug fixes)
onDeltacallback could previously trigger a falseErrIdleTimeoutafter partial output.{"error":…}chunks now surface the provider's message. Before, they could end as an empty success. Non-objecterrorkeys are ignored.[DONE]with nothing emitted is now a premature-close error, per invariant 3.\rline endings.Retry-Aftervalue no longer overflows into a negative duration.Retry-Afterwait outlasts the remaining budget, the call returns the last provider error immediately. Before, it slept until the deadline and returned a bareDeadlineExceeded.M2: Canonical mapping (bug fixes)
stoptogether with tool calls now finishestool_calls, in both buffered and streaming modes.indexare no longer merged into one corrupted call (invariant 8).incompletereason maps to""(invariant 1). The precedence for tool-call turns is now documented in README and AGENTS.md.{}on every accumulator, as it already was in buffered mode.IMAGE_SAFETY,IMAGE_PROHIBITED_CONTENTandIMAGE_RECITATIONmap tocontent_filter.M3: Request validity (bug fixes)
image/jpgis now accepted, as the README already promised.parametersJsonSchema, so full JSON Schema works.models/prefix stripped.text,srtandvttreturn the raw body. Before, the transcription was billed and then failed.M4: Anthropic current-model surface (feature)
Thinkingpresets now sendthinking:{type:"adaptive"}plusoutput_config.effort.budget_tokensis sent only whenThinkingBudget > 0, which remains the escape hatch for models older than 4.6. Background:budget_tokensreturns a 400 on Opus 4.7+, Sonnet 5+, Fable and Haiku 5.5.ResponseFormatnow uses nativeoutput_config.format. It can be combined with thinking, tools andToolChoice. Background: forcedtool_choicereturns a 400 on Opus 5.5, Sonnet 5.5 and Fable 5.1.AnthropicToolJSONregistry quirk keeps the old forced-tool emulation, unchanged, for gateways that lackoutput_config.M5: Models and embeddings (bug fixes)
encoding_format:"base64". Large batches previously exceeded the 50MB body cap. Float arrays are still accepted as a fallback./v1/modelsresponse, as Together returns, is accepted.has_morewithout alast_idis now an error instead of a silently truncated list.ListModelshonoursWithRetryPolicy, the request-timeout budget andRetry-After.M6a: Response metadata and observability (feature)
ChatResult.RefusalandDeltaRefusalon OpenAI chat, Responses and Anthropic. Refusal turns finishcontent_filter.ResponseMetaon chat, embed, speak and transcribe results: provider response ID, served model, request ID, and rate limits with aReportedflag. All fields are provider-reported or zero, never guessed.Headers, validated before any I/O. Reserved auth and framing headers are rejected, as are case-insensitive duplicates and control characters.WithAttemptObserver(AttemptEvent)fires on every HTTP attempt, on every entry point. A panicking observer is recovered, and keys are masked.M6b: Inputs and helpers (feature)
DocumentPart(PDF and plain text) andImageURLPart/ImageURLPartMIMEon every format, with boundary validation. MIME types are normalized to lower case.RunTools, a tool-loop helper. It replays thinking blocks and signatures throughAssistantMessage()(invariant 7) and supports parallel handlers,MaxTurns(ErrMaxToolTurns) and anOnTurnhook.CountTokensfor Anthropic and Gemini. Other formats returnErrUnsupported, never an estimate (invariant 5).TopK; a key-by-key merge of GeminiExtra["generationConfig"]; andEmbedRequest.TaskTypefor Gemini.RunToolschecks the context before each handler. On cancellation it returns the last completed result plus the context error, which is now documented. Real tests also cover the reachable lines that had lost coverage.ThinkingBudgetto keepbudget_tokens.output_config.format. Anthropic-format gateways without it need theAnthropicToolJSONquirk.encoding_format:"base64". The official OpenAI SDK sends this by default.[DONE]with no output, or a provider error chunk, is now an error instead of an empty success.ConfigError.Test plan
go test -count=1 -timeout 240s -race ./...: green on the final headgofmt,go vet ./...,go vet -tags e2e ./...,golangci-lint run: cleango test -tags e2e -run TestE2E -timeout 15m -v .): not run, because it needs provider keys and spends money. Recommended before merge, especially for Anthropic adaptive thinking and native JSON, OpenAI base64 embeddings, and GeminiparametersJsonSchema.Known residuals (minor, non-blocking)
Contentis{}but no matching delta is emitted.Embedresults carryMetafrom the last batch only.ListModelsretry restarts from page 1.🤖 Generated with Claude Code