Skip to content

feat(cloudflare): add Braintrust tracing for Cloudflare Workers AI - #238

Open
Atharva Mhaske (atharvamhaske) wants to merge 3 commits into
braintrustdata:mainfrom
atharvamhaske:feat/cloudflare-workers-ai-tracing
Open

Atharva Mhaske (atharvamhaske) wants to merge 3 commits into
braintrustdata:mainfrom
atharvamhaske:feat/cloudflare-workers-ai-tracing

Conversation

@atharvamhaske

@atharvamhaske Atharva Mhaske (atharvamhaske) commented Sep 14, 2026

Copy link
Copy Markdown
Contributor

Summary

  • Adds tracing for Cloudflare Workers AI in trace/contrib/cloudflare, covering text generation (chat, tool calls), text/multimodal embeddings, and every other Workers AI task type through client.AI.Run.
  • Workers AI sends every task through one endpoint, and two request shapes are genuinely ambiguous on the wire (bare {"image":[...]} and bare {"text":"..."} each map to two different task types). The tracer classifies from the response instead of the request, which is always unambiguous: {data,shape} is an embeddings result, a usage key is a text generation result, anything else is logged as Cloudflare's own response shape.
  • Text generation output now converts to the OpenAI Chat Completions format (choices array, tool_calls with string-encoded arguments) and available tools convert to the OpenAI-nested metadata.tools shape, per the Braintrust instrumentation spec.
  • Orchestrion instruments cloudflare.NewClient at compile time. No manual WithMiddleware call is needed.
  • Adds an example under examples/internal/cloudflare, now including tool-calling and text classification.

Verification

  • The audit named github.com/cloudflare/cloudflare-go/v6. The current version is v7.10.0. Every type the audit cited exists under the v7 path.
  • Real calls to @cf/meta/llama-3.1-8b-instruct-fast (text generation, tool calls), @cf/baai/bge-base-en-v1.5 (text embeddings), and @cf/huggingface/distilbert-sst-2-int8 (text classification) confirmed the response shapes above, including that Cloudflare sets "response": null (not omitted) on tool-calling-only replies, so classification keys on usage instead.
  • No live model supports multimodal embeddings today, but it shares the exact {data,shape} response shape as text embeddings, so it's covered by the same code path and test.
  • The ~10 non-chat, non-embedding task types (classification, detection, translation, summarization, speech, captioning, image generation) aren't converted to a canonical shape and are logged as Cloudflare's native response, since the instrumentation spec's relevant sections (Embedding APIs, Multimodal API surfaces) are still TODO as of this writing.
  • Attachments (binary image/audio data) aren't uploaded as braintrust_attachment references yet; oversized binary fields are summarized to {type, length} so a span never carries megabytes of raw bytes, but that's a stopgap, not a real attachment upload. Left as a follow-up.
  • Workers AI puts the account ID in the URL path. This broke VCR cassette matching between record and replay. Cassettes use a fixed placeholder account ID, with a comment for future recordings.

Changes

  • trace/contrib/cloudflare/: tracecloudflare.go, airun.go (response-based classification, OpenAI-format conversion), orchestrion.go, orchestrion.yml, tests, cassettes.
  • examples/internal/cloudflare/: example, now covering tool-calling and text classification.
  • examples/go.mod, go.work, scripts/nested_modules.txt: registered the new module.
  • trace/contrib/all/: registered cloudflare in the meta-module and regenerated orchestrion.yml.
  • trace/contrib/orchestrion_test.go: added cloudflare to the fixture's replace-directive map and import list.
  • trace/contrib/testdata/orchestrion/: added TestCloudflare. It confirms orchestrion instruments a plain cloudflare.NewClient(...) call with no manual WithMiddleware.
  • internal/genorchestrion/genorchestrion_test.go: raised the expected aspect count from 18 to 19.

Notes

make ci passes locally, including both TestOrchestrionInjection variants.

Screenshots

1. text generation
Screenshot 2026-09-15 at 12 18 08 AM

2. embeddings
Screenshot 2026-09-15 at 12 22 32 AM

Closes #236

Note on task type coverage

This PR does not add 14 example calls for the 14 Workers AI task types. The tracer code handles all task types. It sorts each response into one of 3 groups: embeddings, text generation, or a fallback group for everything else. The fallback group logs Cloudflare's own response shape as-is.

The example script shows one live call per group, plus a tool-calling call. That gives 4 calls total: text generation, tool calls, embeddings, and text classification (the fallback group).

The other 9 task types (translation, summarization, image tasks, speech tasks) do not have example calls yet. Most of them need image or audio input, and the instrumentation spec does not yet define an attachment format for that input. This PR does not add those attachments.

…I Go SDK

Signed-off-by: atharvamhaske <atharvamhaske76@gmail.com>

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 235339902b

ℹ️ 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 (@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 (@codex) address that feedback".

Comment thread trace/contrib/cloudflare/airun.go Outdated
if _, ok := raw["image"]; ok {
return taskMultimodalEmbedding
}
return taskTextEmbeddings

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Distinguish non-embedding Workers AI tasks

Because cloudflareRouter creates an aiRunTracer for every /ai/run/ URL, any Workers AI operation whose JSON omits both messages and image takes this default branch. Those calls are recorded as text embeddings and the subsequent input/output handling looks only for text and {data, shape}, so other task shapes produce mislabeled and incomplete traces. Restrict this default to recognized embedding bodies or add classifiers for the remaining Workers AI task schemas.

Useful? React with 👍 / 👎.

Comment thread trace/contrib/cloudflare/airun.go Outdated
if shape, ok := result["shape"]; ok {
summary["shape"] = shape
}
return internal.SetJSONAttr(span, "braintrust.output_json", summary)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Preserve embedding usage metrics

This embeddings path only writes braintrust.output_json and never records the usage object returned by Workers AI. Consequently every text- or multimodal-embedding trace loses prompt/total-token accounting, unlike the text-generation path above, making token-based cost and experiment summaries inaccurate. Parse result["usage"] here and set braintrust.metrics as well.

Useful? React with 👍 / 👎.

@AbhiPrasad

Copy link
Copy Markdown
Member

can you make sure the instrumentation follows this skill? https://github.com/braintrustdata/braintrust-spec/blob/main/skills/instrumentation-spec/SKILL.md

@atharvamhaske

Copy link
Copy Markdown
Contributor Author

can you make sure the instrumentation follows this skill? https://github.com/braintrustdata/braintrust-spec/blob/main/skills/instrumentation-spec/SKILL.md

yes sure, adding this on my workflows

…output

Response-side classification (instead of guessing from the ambiguous
request shape) covers all Workers AI task types; text generation now
emits OpenAI-format choices/tool_calls and OpenAI-nested metadata.tools
per the Braintrust instrumentation spec.

Signed-off-by: atharvamhaske <atharvamhaske76@gmail.com>
…kers-ai-tracing

# Conflicts:
#	internal/genorchestrion/genorchestrion_test.go
#	trace/contrib/all/all.go
#	trace/contrib/testdata/orchestrion/go.mod
#	trace/contrib/testdata/orchestrion/go.sum
#	trace/contrib/testdata/orchestrion/orchestrion_test.go
@atharvamhaske

Copy link
Copy Markdown
Contributor Author

Abhijeet Prasad (@AbhiPrasad) i have pushed some changes after using skills and added screenshots, can be reviewed and test on your end !

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[bot] Instrument Cloudflare Workers AI (610,717 weekly downloads)

2 participants