feat(contrib/googleapis/go-genai): add Google GenAI Go SDK integration#4951
feat(contrib/googleapis/go-genai): add Google GenAI Go SDK integration#4951billyac wants to merge 7 commits into
Conversation
|
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 01f68751f7
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| span, ctx := startLLMSpan(ctx, "genai.chat.send_message", c.model, c.provider) | ||
|
|
||
| resp, err := c.chat.SendMessage(ctx, parts...) | ||
| finishLLMSpan(span, c.inputForSpan(parts), c.config, resp, err) |
There was a problem hiding this comment.
Snapshot chat input before sending
On successful chat sends, google.golang.org/genai mutates the chat history before this line returns by recording the user input and model output, so c.inputForSpan(parts) reads a history that already contains the current turn and then appends the current user message again. This makes LLMObs input include the assistant response and duplicate user message for every successful Chat.SendMessage call (and the same pattern exists in the stream path), corrupting the recorded conversation telemetry; build the span input from a pre-call history snapshot instead.
Useful? React with 👍 / 👎.
There was a problem hiding this comment.
Good catch — fixed in dcb3fa8. Snapshotted history into a local before calling SendMessage/SendMessageStream (new snapshotInput helper). Extended TestChatSendMessage to two consecutive sends: each span's input now contains the new user turn exactly once and the assistant reply for that turn doesn't appear in its own input.
| "type": "FIX_ME", | ||
| "default": "FIX_ME" |
There was a problem hiding this comment.
Fill in supported configuration metadata
This entry was left with the configinverter placeholders. Since supported_configurations.json is the source of metadata for known env vars, shipping FIX_ME reports DD_TRACE_GOOGLE_GENAI_ANALYTICS_ENABLED with an invalid type/default instead of matching the other DD_TRACE_*_ANALYTICS_ENABLED boolean keys with default false.
Useful? React with 👍 / 👎.
There was a problem hiding this comment.
Fixed in dcb3fa8 — set type="boolean", default="false" to match the other DD_TRACE_*_ANALYTICS_ENABLED keys.
BenchmarksBenchmark execution time: 2026-07-08 14:34:56 Comparing candidate commit bbdae1c in PR branch Found 0 performance improvements and 0 performance regressions! Performance is the same for 326 metrics, 0 unstable metrics, 1 flaky benchmarks without significant changes.
|
Add a new contrib submodule that wraps *genai.Client to produce LLM Observability spans for GenerateContent, GenerateContentStream, EmbedContent and chat sessions (Chats.Create, SendMessage, SendMessageStream). Provider is derived from the configured backend (google / google_vertexai / google_enterprise). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- chats.go: snapshot chat history before SendMessage / SendMessageStream; genai.Chat appends the current turn to History on success, so reading history after the call double-counted the user message and leaked the assistant reply into the recorded input (Codex P2). - Fix supported_configurations.json placeholder for DD_TRACE_GOOGLE_GENAI_ANALYTICS_ENABLED to boolean / default false (Codex P2). - Add testify direct/transitive deps to contrib go.mod, eliben tokenizer entry to go.work.sum, google.golang.org/genai to the third-party libraries list in stacktrace, and goimports group separator in example_test.go (CI: check-modules, check-format, generate). - Bump TestAgentIntegration default integration count 57 -> 58 for the new contribIntegrations entry. - Extend TestChatSendMessage to two consecutive sends and assert no duplication of user content across spans. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The previous run failed during 'go mod download' with a transient proxy.golang.org stream error fetching unrelated modules (elastic/go-elasticsearch/v8, envoyproxy/go-control-plane/envoy). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
4f214c7 to
9adb68b
Compare
Config Audit |
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
| PackageUptraceBun Package = "uptrace/bun" | ||
| PackageLogSlog Package = "log/slog" | ||
| PackageModelContextProtocolGoSDK Package = "modelcontextprotocol/go-sdk" | ||
| PackageGoogleAPIsGoGenAI Package = "googleapis/go-genai" |
There was a problem hiding this comment.
| PackageGoogleAPIsGoGenAI Package = "googleapis/go-genai" | |
| PackageGoogleAPIsGoGenAI Package = "google.golang.org/genai" |
There was a problem hiding this comment.
Applied in 0e44ff9. Also updated the naming registration and matching MCP-style tests still pass.
| @@ -0,0 +1,114 @@ | |||
| // Unless explicitly stated otherwise all files in this repository are licensed | |||
There was a problem hiding this comment.
the contrib path should match the traced package path
(in this case it should live under contrib/google.golang.org/genai)
There was a problem hiding this comment.
Moved in 0e44ff9 to contrib/google.golang.org/genai/ to match the traced import path (same as google.golang.org/grpc and google.golang.org/api).
| // WrapClient returns a tracing wrapper around the given *genai.Client. | ||
| // The returned *Client exposes wrapper Models and Chats with the same | ||
| // method signatures as the upstream SDK. | ||
| func WrapClient(c *genai.Client) *Client { |
There was a problem hiding this comment.
usually we accept here a variadic opts ...Option argument in case we want to provide configuration for the integration. Even if we don't have any option in mind right now, its worth it to add it to avoid introducing breaking changes in the api if we need it in the future.
There was a problem hiding this comment.
Done in 0e44ff9: added an Option type and WrapClient(c *genai.Client, opts ...Option) — no options exported yet, but the variadic form is in place to avoid a breaking change later.
- Move contrib from contrib/googleapis/go-genai to contrib/google.golang.org/genai to match the traced package's canonical import path (per contrib/README.md naming convention). - Update the Package constant value to "google.golang.org/genai" so the telemetry package name matches the traced import path (matches how google.golang.org/grpc and google.golang.org/api are registered). - Accept variadic opts ...Option in WrapClient so we can add integration options later without breaking the API. No public options are exported yet. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Main upgraded the WAF dependency; retidying the contrib swaps go-libddwaf/v4 v4.10.0 -> go-libddwaf/v5 v5.0.0 in the contrib's go.mod / go.sum and adds the missing v4.10.0 /go.mod entry to go.work.sum. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Move the go.mod pseudo-versions to the latest commit of the branch (bbdae1c51d37) and rename the contrib path to contrib/google.golang.org/genai to match the reviewer feedback on DataDog/dd-trace-go#4951. Document the go get @branch-name pattern in the README so anyone running the smoke test can refresh to the branch tip without looking up commit hashes. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Summary
Adds Datadog LLM Observability tracing for
google.golang.org/genaiatcontrib/googleapis/go-genai/v2.API matches the existing LLMObs contrib precedent (
mark3labs/mcp-go,modelcontextprotocol/go-sdk): the user callsgenaitrace.WrapClient(*genai.Client) *Clientand uses the returned client in place of the original. Noorchestrion.ymlfor now — same as the other LLMObs contribs.Instrumented methods
Models.GenerateContent,Models.GenerateContentStream(chunks accumulated for output text + final usage)Models.EmbedContentChats.Create→ traced*Chat.SendMessage/*Chat.SendMessageStreamSpan data
google/google_vertexai/google_enterprise)input_tokens,output_tokens,total_tokens,cache_read_input_tokens,reasoning_output_tokensRepo wiring
instrumentation/packages.go:PackageGoogleAPIsGoGenAIddtrace/tracer/option.gocontribIntegrations:google.golang.org/genaiinternal/env/supported_configurations.{json,gen.go}:DD_TRACE_GOOGLE_GENAI_ANALYTICS_ENABLEDinternal/stacktrace/contribs_generated.go(regenerated)go.workTest plan
go test ./contrib/googleapis/go-genai/...— five tests covering generate, generate stream (SSE), embed, chat with prior history (two consecutive sends to verify history-snapshot ordering), and error path, all running against anhttptestmock server.go test ./instrumentation/...—TestInstrumentation_AnalyticsRate/googleapis/go-genaipasses.go test ./ddtrace/tracer/ -run TestAgentIntegration— passes after bumping default integration count 57 → 58.go build ./...from repo root.Review follow-ups
chats.go):genai.Chat.SendMessageappends the current turn toHistory()on success, so reading history after the call duplicated the user message and leaked the assistant reply into the recorded input. Fixed by snapshotting history before the call in a newsnapshotInputhelper;TestChatSendMessagenow performs two consecutive sends and asserts each span's input contains the new user turn exactly once and no assistant content from the same turn.supported_configurations.json): replaced theFIX_MEplaceholders forDD_TRACE_GOOGLE_GENAI_ANALYTICS_ENABLEDwithboolean/ defaultfalse, matching every otherDD_TRACE_*_ANALYTICS_ENABLEDkey.Outstanding
dd-gitlab/validate_supported_configurations_v2_local_filerequires a Datadog maintainer to registerDD_TRACE_GOOGLE_GENAI_ANALYTICS_ENABLEDin the internal configuration registry (per CONTRIBUTING.md). I can't do this from outside.Claude session:
10d83514-c53a-4629-b0b1-ccde7bb21f6bResume:
claude --resume 10d83514-c53a-4629-b0b1-ccde7bb21f6b🤖 Generated with Claude Code