Repository navigation
feat(go/eino): CloudWeGo Eino instrumentor (TH-8275) - #265
Merged
Merged
Conversation
New Go module github.com/future-agi/traceAI/go/eino (package traceaieino): an Eino callbacks.Handler, registered once with callbacks.AppendGlobalHandlers (or per run with compose.WithCallbacks / adk.WithCallbacks), that turns every component run Eino reports into one OpenTelemetry span with the traceAI attribute names: graphs, chains, nodes, chat models (model, usage, messages, tool definitions), tools, retrievers, embedders, prompts, lambdas and ADK agents. - Parent from the callback context; another handler's span on another provider is never made the parent. One graph or agent run is one trace. - Streams are one span, ended after the handler's own copy is read; usage comes from the reporting chunk, never summed. Agent spans end with the agent's event stream. - OnError then OnEnd ends once; an OnEnd without a matching OnStart never ends another run's span; OnError without OnStart gets its own span. - Content on by default with the traceAI FI_HIDE_* switches and TraceConfig; error.message is a scrubbed first line, dropped when content is hidden; no stack traces, exception events, resource attributes or credentials. - Shutdown stops the handler, waits for background span ends, ForceFlushes. - Depends only on the OpenTelemetry API and Eino (go.mod minimum v0.9.19; tested on v0.9.21 and v0.9.19, Go 1.26.9 and 1.27.2). Example (ADK ChatModelAgent + eino-ext OpenAI model + tool) uses the stock OTLP/HTTP exporter; tests use real Eino graphs/agents with a scripted model. Co-Authored-By: Claude Opus 5.5 <[email protected]>
…gent output is redacted The example's request span gets input.value / output.value through TraceConfig.SetAttributes, so the trace list shows them and the FI_HIDE_* switches apply. With outputs hidden, an agent span that had an answer gets output.value=__REDACTED__ like every other span, instead of no value. Co-Authored-By: Claude Opus 5.5 <[email protected]>
This was referenced Oct 16, 2026
nik13
marked this pull request as ready for review
October 21, 2026 22:46
Co-Authored-By: Claude Code <[email protected]>
Plan only supported packages present at the head. Preserve Python and LiveKit coverage; use frozen dependencies, pinned toolchains and a fail-closed aggregate. No vendor, publication or deployment credentials. Co-Authored-By: Claude Code <[email protected]>
Temurin labels the build metadata with an LTS suffix; pin the unambiguous 21.0.8 release instead of an unavailable 21.0.8+9 variant. Co-Authored-By: Claude Code <[email protected]>
Keep independently landing integration workflows coherent by exposing the pinned Corepack shim to later build steps. No planner, path selection, test command, or result gate changes. 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.
Summary
New Go module
github.com/future-agi/traceAI/go/eino(packagetraceaieino) for CloudWeGo Eino (TH-8275, parent TH-8103).It is an Eino
callbacks.Handler, Eino's own extension point. Register it once withcallbacks.AppendGlobalHandlers, or pass it to one run withcompose.WithCallbacks/adk.WithCallbacks. Every component run Eino reports then becomes one OpenTelemetry span, with the attribute names Python traceAI uses:ChatModel/AgenticModelLLM:gen_ai.request.model, usage,gen_ai.input/output.messages.*, tool calls, tool definitions, finish reasonToolTOOL: name, call id, arguments, resultAgent/AgenticAgentAGENT: the span ends when the agent's event stream endsRetriever/EmbeddingRETRIEVER/EMBEDDING(vectors are never written)CHAINUNKNOWN, kept with its nameOnErrorthenOnEndends the span once, including when the two run concurrently. AnOnEndwithout a matchingOnStartnever ends another run's span (the RunInfo pointer is checked). AnOnErrorwithout anOnStartgets its own ERROR span.Handler.Shutdown(ctx)makes later callbacks no-ops (no panic, no export), waits for spans that are still being finished in the background, and callsForceFlushon the provider.FI_HIDE_*switches orTraceConfigturn it off.error.messageis a scrubbed first line with no URL, path, IP, host:port, email, user name or home dir; it is dropped when content is hidden. No stack traces, no exception events, no resource attributes, no credentials.go/fiotelimport and noreplacelines. The example wires the stockotlptracehttpexporter.Files:
handler.go(callbacks, parenting, end-once, Shutdown),mapping.go(component to attributes),stream.go(streams, agent events),errors.go(error type and scrubber),traceconfig.go/context.go(same API as the other Go modules),example/(ADKChatModelAgentwith the eino-ext OpenAI model and a tool),README.md.How to run
The docs companion is future-agi/docs PR #979. Its
examples/traceai/eino-go/run_e2e.shbuilds the example the way the page says (go mod init,go get) from this branch's commit and sends three runs: default capture, plain, and hidden.Versions
go.modminimum) also ran the full E2E and unit tests. v0.10.0-alpha.35 unit tests pass (informational only, not claimed)go 1.26.0)-racetests) and 1.26.9 (tests)Re-pin: the PRD pinned v0.9.19. v0.9.20 and v0.9.21 have shipped since. The callback interface (
callbacks/interface.go,internal/callbacks) has no diff between v0.9.19 and v0.9.21. The only change in a callback payload is the newPromptTokenDetails.CacheWriteTokens, which this module does not read (so it still builds on v0.9.19).E2E evidence (real, local Future AGI)
Real Eino v0.9.21 ADK
ChatModelAgent(eino-ext OpenAI chat model plus aget_weathertool viautils.InferTool) ran throughadk.Runner. The handler was registered once withAppendGlobalHandlers, as the page tells customers. It exported through the stockotlptracehttpexporter to the local Future AGI collector (http://localhost:14338/tracer/v1/traces, both headers,project_name, no gzip). The module was fetched withgo get github.com/future-agi/traceAI/go/eino@f5c35b610572(GOPRIVATE, straight from this branch). The model calls went through an OpenAI-compatible local proxy (CLIProxy) togpt-6-luna; the endpoint was the only thing swapped. Projectth8103-rest-eino, commitf5c35b6:d541f30b31ff6e4277dcdab1bcd1b40fno-such-model)b18c11e1c10fff7893d3e197643c39f5*openai.APIErroron LLM → ReAct → graph → agent → request845fd07cc2182f5788ae124dd10c52e0FI_HIDE_INPUTS=true FI_HIDE_OUTPUTS=true, streamed44783e7b8f1977a39255617865a6d5ab__REDACTED__db56ac442e7e8a0dcfd3794bb85360c6error.messageMatrix leg Eino v0.9.19, same commit, project
th8103-rest-eino-v0919:f2e27f9a54d60ccc96fc68a2b84c3495,a562af6d74ca87c9ef8ab87b590de994,dd294638c7b4d256687533dfdb25d9f7,3f30b6b1562c98ca5f153920b66ee171,1b3e12eddae92cfb77c444905ff62209. fi_verify PASS, same shape. An earlier run of both versions on the first commit73c26fbalso passed; its traces are still in both projects.fi_verify --expect-type llm --expect-type tool --expect-type agent --expect-type chainexcerpt:Failing LLM span as stored:
error.type=*openai.APIError,error.message="error, status code: 400, status: 400 Bad Request, message: unknown provider for model no-such-model". In the hidden run there is noerror.message. Screenshots of the Observe trace view (chat view with system, user, tool and assistant messages, model and tokens), the failed span, the hidden span (REDACTED) and the trace list are kept locally in the workspaceevidence/.Privacy proof
run_e2e.shputs a unique marker in the run-A question (MARK_DEFAULT) and in the run-C question (MARK_HIDDEN, plus[email protected]and/Users/someone/MARK_HIDDEN.txt), and asks the model to repeat it in the answer. A scan then searched every stored row of the project: all 11 ClickHouse tables with aproject_id(spanswith all 46 columns, includingresource_attrs,span_events,attributes_extra,input,output,status_message) plus Postgrestracer_*.The v0.9.19 project gives the same result. In other words, the default mode shows content (the opt-out works as documented), and the hidden mode exports nothing of prompts, replies, tool arguments or results, emails, paths or error text. In neither mode do local identity, stack traces or keys reach the collector.
Tests
go test -race ./...is green on Go 1.27.2 and 1.26.9, and on Eino v0.9.19 and v0.9.21. Coverage is 87.5%, andstaticcheck2026.2.2 is clean. The tests use real Eino graphs, chains, lambdas (invoke/stream/collect),ToolsNodeand ADKChatModelAgentruns (streamed and not), with a scripted chat model (callback-enabled and graph-wrapped), against the in-memory recorder. They cover the PRD acceptance rows:OnErrorthenOnEndgives one ERROR span, also when concurrent.Shutdown, then callbacks do not panic or export, and after the provider shuts down they do nothing either.OnEndends nothing.They also cover a foreign handler's span (never made the parent), the caller's span (is the parent), a nil
RunInfo, the error scrubber, multimodal messages, and the retriever, embedding and prompt mappings. The example test runsexample/main.goagainst a fake OpenAI server and a fake collector. It checks AC-09 (path, both headers,project_name, no gzip) and AC-11 (no key in the body), one trace per question, and the failing run as ERROR.Limitations / unsupported
eino.agent.interrupted/eino.agent.resumedbut no test covers them.AgenticModel/AgenticAgentcontent is written only as generic JSON (not tested).ChatModelAgentshows Eino's internal ReAct nodes (Init,CancelCheck,AfterToolCalls, ...) asCHAINspans because Eino reports them. There is no filter option yet; that could be a follow-up.gen_ai.provider.nameis not written:RunInfo.Typenames the component (OpenAI), not the endpoint.gen_ai.usage.cache_write_tokensis not read (the field only exists in v0.9.21+). Request temperature and top_p are written only when non-zero, because Eino'sConfigcannot tell 0 from unset.Extramap after the chat model's stream has been copied to the callback handlers. A handler that callsschema.ConcatMessageson its copy (which readsExtra) races with that write (seen with-race). This module joins chunks without readingExtra, so it is race-free. Not reported upstream (no upstream contact per the TH-8103 rules).Deviations from the approved spec (and why)
go/fiotelimport. The architecture said to import the shared helper landed by TH-8271. The coordinator decision of 2026-10-16 replaced that: each Go module depends only on the OTel API plus its upstream SDK, carries its own TraceConfig, and wires the stock exporter in its example. TH-8271 (feat(go/openai): OpenAI Go instrumentor (TH-8271) #260) did not addgo/fiotel. Consolidating the five Go modules into one shared module is a follow-up after the first Go PR merges.TraceConfig(DEFAULT_HIDE_* = False) with the standardFI_HIDE_*switches. Error text is always scrubbed, and local identity is never exported in either mode.gen_ai.span.kindis written. D-LR07 said no span kind. The coordinator decision says Go modules use the same attribute names as the Python instrumentors, and Python's span-kind key isgen_ai.span.kind(as ingo/openai,go/anthropic,go/genai,go/a2a).go.modminimum kept at the PRD's v0.9.19. Both were tested.BLOCKED-ON
None. Eino is free OSS, and the run needed no account or spend. Kept as a draft per the TH-8103 process.
🤖 Generated with Claude Code