diff --git a/dev-packages/e2e-tests/test-applications/cloudflare-workers-send-to-sentry/src/env.d.ts b/dev-packages/e2e-tests/test-applications/cloudflare-workers-send-to-sentry/src/env.d.ts index aaf72be7640b..15557b010a50 100644 --- a/dev-packages/e2e-tests/test-applications/cloudflare-workers-send-to-sentry/src/env.d.ts +++ b/dev-packages/e2e-tests/test-applications/cloudflare-workers-send-to-sentry/src/env.d.ts @@ -1,4 +1,5 @@ interface Env { E2E_TEST_DSN: string; SLEEP_WORKFLOW: Workflow; + AI: Ai; } diff --git a/dev-packages/e2e-tests/test-applications/cloudflare-workers-send-to-sentry/src/index.ts b/dev-packages/e2e-tests/test-applications/cloudflare-workers-send-to-sentry/src/index.ts index 93f1c24f124e..324c9161cf69 100644 --- a/dev-packages/e2e-tests/test-applications/cloudflare-workers-send-to-sentry/src/index.ts +++ b/dev-packages/e2e-tests/test-applications/cloudflare-workers-send-to-sentry/src/index.ts @@ -27,6 +27,10 @@ export default { } case '/test-unhandled-error': throw new Error('E2E test unhandled error'); + case '/test-workers-ai': { + await env.AI.run('@cf/meta/llama-3.2-1b-instruct', { prompt: 'Say hi', max_tokens: 5 }); + return Response.json({ traceId: spanContext?.traceId }); + } case '/test-span': return Response.json({ spanId: spanContext?.spanId, traceId: spanContext?.traceId }); case '/test-workflow-sleep': { diff --git a/dev-packages/e2e-tests/test-applications/cloudflare-workers-send-to-sentry/tests/send-to-sentry.test.ts b/dev-packages/e2e-tests/test-applications/cloudflare-workers-send-to-sentry/tests/send-to-sentry.test.ts index 8578ecb7bee7..044883dea3b0 100644 --- a/dev-packages/e2e-tests/test-applications/cloudflare-workers-send-to-sentry/tests/send-to-sentry.test.ts +++ b/dev-packages/e2e-tests/test-applications/cloudflare-workers-send-to-sentry/tests/send-to-sentry.test.ts @@ -2,6 +2,7 @@ import { randomBytes } from 'node:crypto'; import { expect, test } from '@playwright/test'; import { EVENT_POLLING_OPTIONS, + fetchSpanAttributes, fetchTrace, findErrorInTrace, findSpanInTrace, @@ -73,3 +74,22 @@ test('Sends the spans of Workflow steps before the Workflow goes to sleep', asyn ); expect(['running', 'waiting']).toContain(status); }); + +test('Sends a Workers AI gen_ai span to Sentry', async () => { + const { traceId }: { traceId: string } = JSON.parse(await fetchFromWorker(`${workerUrl}/test-workers-ai`, 200)); + + console.log(`Polling for gen_ai.chat span: sentry trace view ${traceTarget(traceId)}`); + + let spanId: string | undefined; + await expect + .poll(() => (spanId = findSpanInTrace(traceId, 'gen_ai.chat')?.event_id), EVENT_POLLING_OPTIONS) + .toBeDefined(); + + // Sentry stores `gen_ai.response.text` as `gen_ai.output.messages`. + await expect + .poll(() => fetchSpanAttributes(traceId, spanId!), EVENT_POLLING_OPTIONS) + .toMatchObject({ + 'gen_ai.input.messages': expect.stringContaining('Say hi'), + 'gen_ai.output.messages': expect.stringContaining('"role":"assistant"'), + }); +}); diff --git a/dev-packages/e2e-tests/test-applications/cloudflare-workers-send-to-sentry/wrangler.jsonc b/dev-packages/e2e-tests/test-applications/cloudflare-workers-send-to-sentry/wrangler.jsonc index d6b15b1dbf5e..f1c0857ea5dd 100644 --- a/dev-packages/e2e-tests/test-applications/cloudflare-workers-send-to-sentry/wrangler.jsonc +++ b/dev-packages/e2e-tests/test-applications/cloudflare-workers-send-to-sentry/wrangler.jsonc @@ -6,6 +6,7 @@ "compatibility_date": "2026-05-20", "compatibility_flags": ["nodejs_compat"], "workers_dev": true, + "ai": { "binding": "AI" }, // Workers Logs keep the invocations of the last 7 days, so a failed CI run can still be inspected. "observability": { "enabled": true }, // Workflow names are unique per account, so deployWorker() renames it to the worker name.