Repository navigation
feat(go/anthropic): Anthropic Go instrumentor (TH-8272) - #259
Merged
Merged
Conversation
New Go module github.com/future-agi/traceAI/go/anthropic. An option.WithMiddleware middleware for the official Anthropic Go SDK that turns every Messages call, streaming or not, into one LLM span: request and response (input.value/output.value, gen_ai.input/output.messages), model, token counts (cache counts on their own keys), finish reason, tool calls, session.id/user.id from the context, and error.type. anthropic-sdk-go v1.81.0+ opens its own anthropic.messages.create span; when it records, the middleware enriches it instead of opening a second span. With the SDK's spans off it opens its own CLIENT span. Depends only on the OpenTelemetry API and anthropic-sdk-go (v1.82.0). Content is captured by default and turned off with the traceAI FI_HIDE_* variables or TraceConfig. Includes a runnable example that exports with the stock OTLP/HTTP exporter, and httptest-based tests. Co-Authored-By: Claude Opus 5.5 <[email protected]>
… proxy (TH-8272) Co-Authored-By: Claude Opus 5.5 <[email protected]>
11 of 12 tasks
nik13
marked this pull request as ready for review
October 21, 2026 22:46
Co-Authored-By: Claude Code <[email protected]>
Select both modules in the existing integration planner; absent packages remain ignored so either PR can land independently. Co-Authored-By: Claude Code <[email protected]>
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.
Acceptance refresh (2026-10-28, Mac)
Head:
f81ebcde5763c15b22f57a849e92f40f1d2aa310. Merged currentdevnormally and added the module to the existing Go integration CI planner.replaceto this head, officialanthropic-sdk-gov1.82.0;go test -race -count=1 ./...,go vet ./..., and fresh example build pass.http://127.0.0.1:8317/v1/messages, modelclaude-haiku-5-5, selected through the SDK'sANTHROPIC_BASE_URL. This is real Claude through the local proxy, not a directapi.anthropic.comtest.-stream -failpasses: weather-agent → LLM → local get_weather tool → streamed LLM answer; a nonexistent model returns HTTP 400 and an ERROR LLM span. SDK-span enrichment andANTHROPIC_OPEN_TELEMETRY=falseown-span mode both pass without duplicate LLM spans.09e39aa7-bcca-85d4-6ff4-da60526049b0(stream),fd44f0dc-d556-8a69-20bb-444107d64db4(own-span),912eaac1-fec4-4bea-5e53-d0863bb93a97(bad model),5534443d-1740-0321-5cff-2232a8e737e2(privacy).FI_HIDE_INPUTSandFI_HIDE_OUTPUTSremove the unique input marker from the full stored trace, while model and token counts remain. No FI/proxy credentials, local home path, or exception stacktrace appears in the read-back.What does this PR do?
Adds
go/anthropic, a new Go module (github.com/future-agi/traceAI/go/anthropic, packagetraceaianthropic) that traces the official Anthropic Go SDK,github.com/anthropics/anthropic-sdk-go. It is anoption.WithMiddlewaremiddleware: install it once on the client and everyMessages.New/Messages.NewStreamingcall becomes one LLM span with the request and response (input.value,output.value,gen_ai.input.messages,gen_ai.output.messages, system instructions, tool definitions), model, token counts, cache counts, finish reason, tool calls,session.id/user.idfrom the context, anderror.typeon failure.Linear: TH-8272 (parent TH-8103). Docs page: future-agi/docs#962 (
/docs/integrations/traceai/anthropic-go).Why?
traceAI had Anthropic instrumentors for Python, TypeScript and Java, and nothing for Go. anthropic-sdk-go has no Future AGI path: its own spans (v1.81.0+) land in Observe with empty Input/Output columns because Observe reads
input.value/output.value, and they carry no span kind, session or user.Design
anthropic.messages.createCLIENT span through the registered tracer provider. When that span is recording, the middleware adds the Future AGI attributes to it and leaves its status and end to the SDK. When the SDK's spans are off (option.WithoutOpenTelemetry()/ANTHROPIC_OPEN_TELEMETRY=false), the middleware opens its own CLIENT span with the same name and the samegen_ai.*keys (one per HTTP attempt) and sets status and end itself.message_stop. The own span ends at EOF, a read error,Close, or context cancel (viacontext.AfterFunc, so a canceled stream that nobody reads still ends).go/fiotel, noreplace, no Arize/OpenInference module. Builds and tests alone fromorigin/dev. The OTel SDK andotlptracehttpare used only by the tests andexample/.python/fi_instrumentation/instrumentation/config.pyexactly:FI_HIDE_LLM_INVOCATION_PARAMETERS,FI_HIDE_INPUTS,FI_HIDE_OUTPUTS,FI_HIDE_INPUT_MESSAGES,FI_HIDE_OUTPUT_MESSAGES,FI_HIDE_INPUT_IMAGES,FI_HIDE_INPUT_TEXT,FI_HIDE_OUTPUT_TEXT,FI_BASE64_IMAGE_MAX_LENGTH, sentinel__REDACTED__, all defaults capture-on.WithTraceConfig(TraceConfig{...})overrides in code.gen_ai.span.kind=LLM,input.value,output.value,gen_ai.*,session.id,user.id), and thegen_ai.*keys are spelled exactly as anthropic-sdk-go spells them on its own spans.gen_ai.usage.input_tokens = input_tokens + cache_read_input_tokens + cache_creation_input_tokens, and the two cache counts are also ongen_ai.usage.cache_read.input_tokens/gen_ai.usage.cache_write.input_tokens.authorization_tokenin the request body (always replaced with__REDACTED__ininput.value), error message text in status orerror.type(only the API error type, e.g.rate_limit_error, or a Go error type name), stack traces.middleware.go,request.go,response.go) was dropped, nothing of it is kept.Deviations from the approved PRD/architecture (followed reality or the coordinator's binding decisions):
go/fiotelimport (coordinator 2026-10-16: each Go module depends only on the OTel API + its SDK). A shared helper can be consolidated after the first Go PR merges.FI_HIDE_*switches (coordinator privacy clarification 2026-10-16), not "content off by default".gen_ai.span.kind=LLMis set (same names as the Python instrumentors), superseding D-LR07's "no span kind".Versions
go.modrequirement). Matrix: unit tests also pass on v1.81.0; the library and example build on v1.75.0 (PRD pin) and the v1.75.0 build ran E2E (leg G).go getraises anything older than v1.82.0 to v1.82.0go.modsaysgo 1.24, same as the SDK) and 1.27.2 (latest)How was it tested?
cd go/anthropic && go test -race ./...(Go 1.24.3 and 1.27.2). The real SDK againsthttptestservers with recorded Messages JSON and SSE bodies (testdata/, synthetic), every case in both modes (SDK span enriched / own span): one span per call, model/usage/cache keys, stream as one span, tool use recorded and no tool span, 401/429 → ERROR without message text, streamerrorevent, stream cut short, cancel while reading, cancel while not reading, earlyClose, retries (own span per attempt; one SDK span per call), eachFI_HIDE_*flag, unparsable flag value, base64 image limit andFI_HIDE_INPUT_IMAGES,count_tokensnot spanned, undecoded (Bedrock event-stream) body not buffered, MCPauthorization_tokennever exported, SDK-content-mode warning, stream truncation flag. Coverage 79%.gofmt,go vetclean.How to run
E2E (real SDK, local Future AGI)
A customer-style module (
go mod init,go get github.com/future-agi/traceAI/go/anthropic@feat/th-8272-anthropic-gostraight from GitHub = commite6bc2e3, latest anthropic-sdk-go v1.82.0 / OTel v1.47.0, Go 1.27.2,main.go=example/main.goverbatim) exporting with the stockotlptracehttpexporter (both headers,project_nameresource attribute, uncompressed) to the local collector, projectth8103-rest-anthropic-go. OnlyANTHROPIC_BASE_URL/ANTHROPIC_API_KEYwere changed: STANDIN — the Anthropic API was reached through the local CLIProxy (real Claudeclaude-haiku-4-5-20251001responses through the proxy's Claude subscription; plus one leg ongpt-6-lunathrough the proxy's Messages translation). No call went toapi.anthropic.comwith an Anthropic API key.41b266aeb28c8ca1fb0bdfd38d51deda, failurebbdf4fa18590d564b37a5df410a78121ANTHROPIC_OPEN_TELEMETRY=false), Claude, non-streamedb5765bd8ce39079959d1549eee923c4d6226113600fb29e36640b9d4017b358363cae175850c8bee3a485530f25b2b93FI_HIDE_INPUTS=true FI_HIDE_OUTPUTS=true, SDK span, streamed, plus rejected call02b350277fd1bbea99077c2ce81de49f, failure22f5095f50954b6777587fda0e4ce0bec1a0ff1823d47bb2647c717f92a2e2da, failure20e4e5db48874b7466853a785e528dbadc91e774362f1874b1f52f8d5bca78c04759231d46319d622a00e120e1f821bc,c9eb1b7e666ea74818aaf9f65aed8abaclaude-sonnet-5-5,session.id/user.idfrom the context9a62a253292f8d3509a4b77f14b7f3e9fi_verify(excerpt, run before leg I):traces=12 spans=31 … PASSLeg H stored
gen_ai.usage.input_tokens=17115(13 uncached + 17,102 cache),cache_write=17102on call 1 andcache_read=17102on call 2. Every call was exactly one LLM span in both modes (no doubles).Privacy proof
Unique markers per run; a scan of every stored row and column of the project in ClickHouse (spans incl.
resource_attrs,span_events,attributes_extra, input/output, rollups, sessions, traces) and Postgres; labels and counts only:The API endpoint hits are
url.full/server.addressthat anthropic-sdk-go's own span records as metadata (the SDK's documented behavior;api.anthropic.comin production). The middleware's own span records no URL. Documented on the page.Live verification (2026-10-19, real Claude via an Anthropic-compatible proxy)
These calls went through a local Anthropic-compatible proxy (CLIProxy), not directly to
api.anthropic.com. The proxy serves the Anthropic Messages API backed by real Claude models, so the model answers are real, but the endpoint is the proxy's, and it has not been run againstapi.anthropic.comwith an Anthropic API key. The SDK was pointed at the proxy exactly as in step 2:ANTHROPIC_BASE_URLset to the proxy andANTHROPIC_API_KEYset to the proxy's key. The middleware does not read either.claude-haiku-5-5, local Future AGI collector,go run ./example -stream -fail.get_weather, the TOOL span and an LLM span with the answer. The streamed answer is one span with its token counts (483 input, 26 output).llm,provideranthropic, the model, input and output tokens (403/50 for the tool-asking call), and Input and Output filled.fi_verifypassed.FI_HIDE_INPUTS=trueandFI_HIDE_OUTPUTS=true, a unique marker (also sent as an email address and a file path) was absent from every stored column of the project, including resource attributes and span events. With the defaults, the marker was present in the Input and Output columns. The Future AGI keys, the proxy key, host name, user name and home directory were never stored. The SDK's own span still stored the proxy's address (server.address,url.full), as described under Privacy.Limitations / not supported
count_tokens, batches, files, models) get no LLM attributes.ANTHROPIC_OPEN_TELEMETRY_TRACES_CONTENT_MODE=contentmakes the SDK add its own content, whichFI_HIDE_*cannot remove (the middleware logs a warning).go/anthropic/v*tag exists, install with@devafter merge.BLOCKED-ON
None for the current acceptance route: the refreshed real Claude Messages run through CLIProxy and Cloud read-back pass (see refresh above). A direct api.anthropic.com run is not claimed.
Checklist
dev(PR basedev)go/anthropic/README.md🤖 Generated with Claude Code