Skip to content

feat(genkit): @traceai/genkit span processor (TH-8240) - #216

Merged
nik13 merged 13 commits into
devfrom
feat/th-8240-genkit
Oct 26, 2026
Merged

nik13 merged 13 commits into
devfrom
feat/th-8240-genkit

Conversation

@nik13

@nik13 nik13 commented Oct 5, 2026 •

Copy link
Copy Markdown
Contributor

Summary

New TypeScript package @traceai/genkit for TH-8240 (Firebase Genkit, JS). You pass FIGenkitSpanProcessor to Genkit's enableTelemetry({ spanProcessors }).

  • Genkit's own telemetry-server processor stays first (@genkit-ai/core node-telemetry-provider.ts:66-77), so the Dev UI keeps working.
  • For each ended Genkit span, the processor builds a mapped copy and hands it to the @traceai/fi-core provider's processor and OTLP exporter, with the Future AGI resource. Genkit's span is never modified.
  • flushOnSignals(flush) flushes on SIGTERM/SIGINT. Without it, Genkit's own listener exits first and a plain listener never finishes its flush (tested).

How spans are mapped:

  • Kinds come from genkit:type and the ActionType list (core registry.ts:40-62):
    • LLM: model, background-model
    • TOOL: tool, tool.v2
    • RETRIEVER, EMBEDDING, RERANKER, EVALUATOR and AGENT for those action types
    • CHAIN: flow, other actions, flowStep, util, promptTemplate, dotprompt
    • An unknown type gets no kind, but the span is still exported.
  • Tokens: only the fields seen on real model spans (inputTokens, outputTokens, totalTokens) are mapped, on model spans only. Promoted keys found on non-model spans move to genkit.usage.*. thoughtsTokens and cachedContentTokens exist in the schema but never appeared on a span, so they are recorded as unavailable.
  • Model: gen_ai.request.model is the model action's genkit:name. Provider and cost are not available.
  • Content is off by default. genkit:metadata:context is never exported.
  • Session: a beta agent session id becomes session.id; setSession applies to flow spans.
  • License boundary: genkit is a peer dependency (^1.42.0, Apache-2.0) and a 1.42.0 devDependency only. The tarball holds dist/, the README and package.json only (63 files, no *.tsbuildinfo; tested).

Deviations from the accepted architecture/docs (with evidence)

  1. enableTelemetry and flushTracing come from genkit/tracing (genkit/src/tracing.ts:43-44), not genkit. enableTelemetry throws FAILED_PRECONDITION unless genkit is imported first (observed). The README says so.
  2. disableMetrics / forceDevExport are @genkit-ai/google-cloud options, not part of enableTelemetry's config: core 1.42.0 has only TelemetryConfig = Partial<NodeSDKConfiguration>. Metrics are not exported anyway, because no metric reader is set.
  3. register() must be called with setGlobalTracerProvider: false; otherwise Genkit's raw spans also reach Future AGI. The docs example omitted this.

The full span inventory (14 spans, contract/inventory.mjs) and source file:line references are in the commit body. A drift test checks the inventory.

Tests (run on f7c6193, clean tree, by the coordinator)

  • The lockfile is unchanged since 24a2929, and every change is inside typescript/packages/traceai_genkit.
  • jest: 68 passed on each of Node 26.8.1, 20.20.2 and 22.23.3, with that Node first on PATH. The 22.23.3 run was at 20eea0a; f7c6193 changes only the README.
  • Full contract suite (pytest typescript/packages/traceai_genkit/contract), with TRACEAI_NODE_MATRIX set to 20/22/26: 22 passed with each of NODE_BINARY 26.8.1, 20.20.2 and 22.23.3. The 22.23.3 run was at 20eea0a.
  • The 22 are 19 journeys plus the import check on three Node versions. The matrix parametrizes only the import check, which loads the package through a symlink rather than a clean install; the README says so.

Review

Round 1 ran as installed cards on Opus 5.5: pr-reviewer t_6b1ff33d and pr-verifier t_3e78e6b7, which returned CHANGES_REQUESTED. All items are fixed in 24a2929..20eea0a. Swapping the old processor.ts/attributes.ts in gives 3 jest failures (the new N1 and N3 tests), and the contract schema journey fails.

ID Finding Resolution
N1 (required) Nested schema-validation errors exported model output through the span status and exception events, with content off status.message, exception.message and exception.stacktrace are cut at Provided data: and end with [data redacted: captureContent is off]. The event and exception.type are kept; with content on, nothing changes. New contract journey: a structured generate gets non-matching JSON from the fake model (b5cb204)
N4 (required) The README Node 20/22 claim covered only the import check Full suite run per Node; the README states exactly what ran (20eea0a)
N3 Cost parts were demoted only on exact keys Prefix match on gen_ai.cost. / llm.cost. (4973cae)
N2 Firebase/GCP telemetry coexistence README: not supported together in this release, with the reason and cost note (20eea0a)
N5-N7 Signals, FI_HIDE_*, shared-provider shutdown README sentences (20eea0a)

Round 2 was installed pr-verifier t_f01a72b2 at 20eea0a. It confirmed N1, N2, N3 and N5-N7 resolved, with no regressions. On the Firebase auto-init question it agreed with the implementer: auto-init happens only when a span starts before enableTelemetry. It checked every parseSchema path, and GenkitError.detail does not leak. It returned CHANGES_REQUESTED on one evidence gap:

ID Finding Resolution
N4-r2 (P2, evidence) The README claims a full contract run on Node 22.23.3 and jest on 20/22/26, but the coordinator's claims didn't cover them The coordinator reran both: jest 68 passed on 20.20.2 and on 22.23.3, and the contract suite 22 passed with NODE_BINARY 22.23.3. The README row stands as written.
P3 Genkit error paths that embed data without the marker README now names the validateSupport middleware (Request: <JSON>) and defineSimpleRetriever (Item: <JSON>) errors as exported as thrown, plus the key paths kept before the marker (f7c6193)
P3 enableTelemetry ordering README: call it before the first Genkit span, otherwise Genkit's own enableTelemetry({}) wins (@genkit-ai/core src/tracing.ts:32-40)

Round 3 (installed pr-verifier t_d5c84073 at f7c6193) returned VERIFIED, ready to merge. The delta is one commit that changes only the README:

  • N4-r2 is resolved.
  • The new README statements about validateSupport, defineSimpleRetriever, the key path and enableTelemetry ordering match Genkit 1.42.0.

Review is complete at f7c6193. P3 follow-ups after merge:

  • README: an app can also add the validateSupport middleware through genkit/middleware, not only a model plugin.
  • The contract run on Node 22.23.3 and two of the jest runs were made at 20eea0a. The code at f7c6193 is identical; only the README changed.
  • Carried over from earlier rounds:
    • AC-08: the clean-install check is a symlinked import check, not a real npm install. The README discloses this.
    • J3: there is no retriever/embedder journey yet.
    • fi-core: register({batch:true}) does nothing on sdk-trace-base 2.x. That belongs to a separate fi-core ticket.

Video demo

Narrated terminal demo, 6:38, recorded at head f7c6193 (the reviewed head), 1080p H.264/AAC, 10 chapters. sha256 0b00cf119112ebd9609c27fe1861b4d33a83fbb46995db9d93c01bda192d404f.

The video, captions, transcript, chapter list, preview, media check and verification evidence are attached privately to Linear TH-8240 (Future AGI workspace access needed). Every run uses real genkit 1.42.0 with Genkit's mock model, placeholder keys and the shared harness receiver: no vendor call and no live fi-collector.

Time Chapter
00:00 The problem
00:54 Head f7c6193: branch, 0 uncommitted files, genkit 1.42.0 (peer ^1.42.0), Node 26.8.1, 5 commits on 3eaadc8
01:30 Flow: qaFlow = ai.run('prepare') + ai.generate with tool lookup; 7 exports to /tracer/v1/traces with both auth headers (values not shown); span tree CHAIN / LLM / TOOL; trace tokens 32/7/39 = the two model calls (11/3/14 + 21/4/25), counted once; no token or cost keys on non-LLM spans; content off: 0 of 7 spans with input/output/genkit:input/genkit:output and no markers in the export
02:30 Cost (N3): inside fi-core setAttributes cost keys, plain gen_ai.cost.* / llm.cost.* stay on the 2 LLM spans and move under genkit.usage.* on the 5 non-LLM spans; collector-visible cost 0.0092 = 2 calls x 0.0046
03:06 Schema error (N1): the app still gets the full error; all 6 exported status/exception texts end with [data redacted: captureContent is off], with no Provided data:, marker or model JSON; the capture-on control keeps the data
03:46 SIGTERM: with flushOnSignals all 7 of 7 spans arrive before Genkit's own listener exits; with a plain process.on('SIGTERM') listener its flush does not finish before exit, so delivery is a race (2 of 7 arrived on the recorded run, 7 of 7 on the coordinator's driver run; both marked "not guaranteed")
04:23 Node matrix: the same journey on Node 20.20.2, 22.23.3 and 26.8.1, 7 spans (2 LLM), tokens 32/7/39 each
04:54 The change: src/processor.ts N1 cut and src/attributes.ts N3 cost demotion
05:31 Tests: jest 5 suites 68 passed; contract 22 passed (Node matrix 20/22/26); 0 uncommitted files after
05:53 Review and limits

On-screen waits are trimmed in the edit (disclosed per chapter in the transcript); output is unchanged. Checked by the coordinator: sha256, full decode, chapter markers, no pause of 2.5 s or more, frames per chapter against the driver output, no keys or header values in frames, captions or transcript.

Limits

  • Real model plugins (e.g. googleai) were not run, so their usage fields and the Firebase plugin's double export are untested.
  • Reasoning/cached tokens, provider and cost are unavailable.
  • No real fi-collector.
  • README only; the docs page is tracked in TH-8103.

Stacked on #203 (shared harness). Not merged.

Linear: TH-8240. approved: Nikhil 2026-10-03 blanket

Verification sweep (2026-10-12)

  • Verified end to end: the real library made real calls, traces were exported to a local Future AGI stack built from dev, and the spans were checked in ClickHouse and in the Observe UI (hierarchy, span types, input/output, model and tokens, sessions, error status, and a secret scan of everything stored).
  • Updated since this description was written: Gemini thinking and cached-prompt tokens are now mapped (gen_ai.usage.output_tokens.reasoning, gen_ai.usage.cache_read_tokens), and model spans carry the bare model name plus gen_ai.provider.name so Future AGI can price them.
  • Commits added in this sweep:
    • 53d9c8f fix(genkit): map Gemini thinking and cached-prompt tokens on model spans
    • 33be4ef fix(genkit): bare model name and provider on model spans so Future AGI prices them
    • ed7c58f docs: move the README catalog row to the TH-8103 catalog PR
  • Merge order: merge test(harness): shared OTLP harness for TH-8103 contract tests #203 first (merge commit), and the README catalog PR docs: list the TH-8103 integrations in the README catalogs #250 last. Everything else is order-independent.

Re-verification (2026-10-16)

Versions: genkit 1.42.0 (latest stable on npm on 2026-10-16, same as the pin; 1.43.0-rc.1 not tested), @genkit-ai/google-genai 1.42.0, @genkit-ai/compat-oai 1.42.0, @traceai/fi-core 1.0.0 from npm, @traceai/genkit packed from this branch. E2E on Node 22.18.0; jest and the contract suite on Node 20.20.2 and 22.18.0.

What changed in 506bfeb, and why

  1. Privacy default follows the traceAI TraceConfig convention (TH-8103 decision of 2026-10-16 for traceAI packages): content is exported by default (fi-core DEFAULT_HIDE_* = false); FI_HIDE_INPUTS / FI_HIDE_OUTPUTS (and FI_HIDE_INPUT_MESSAGES / _OUTPUT_MESSAGES / _INPUT_TEXT / _OUTPUT_TEXT on model spans, FI_HIDE_EMBEDDING_VECTORS on embedder spans) or traceConfig hide it, with fi-core's parsing and precedence (option > env > default; env applies without a traceConfig). A hidden side drops the genkit:* content keys and sets input.value / output.value to __REDACTED__. captureContent is removed (diag warning; false still hides inputs and outputs). This supersedes "Content is off by default" in the Summary above and the README's "captureContent is the only content switch".
  2. Never exported, in either mode. The real Gemini run on the previous head ed7c58f (content "off") exported the failing tool's message, with the run's marker, an email and the absolute working-directory path, in the span status of 3 spans: the Provided data: cut only covered schema-validation errors. Now exception.stacktrace is dropped, stack frames and local paths are removed from every exported string (cwd → [cwd], home → ~, other absolute local paths → [path], JSON-aware), status message and exception.message lose emails and the machine's user and host names, and with inputs or outputs hidden the message becomes <exception.type>: __REDACTED__ (e.g. Error: __REDACTED__, 400: __REDACTED__ for a Genkit INVALID_ARGUMENT).
  3. Embedder spans now carry embedding.model_name (bare id) and gen_ai.provider.name; both were missing in the real run.
  4. Tests: jest 84 → 95 passed (Node 20.20.2 and 22.18.0). Contract suite updated (content on by default; FI_HIDE_* hides it and keeps kinds/tokens/model; error type only when hidden; no stack trace, local path, user or host name in any journey): 22 passed with NODE_BINARY 20.20.2 and with 22.18.0 (matrix 20/22).

E2E against a local Future AGI stack built from dev (configured exactly as the README: register({ setGlobalTracerProvider: false }), enableTelemetry({ spanProcessors: [new FIGenkitSpanProcessor({ tracerProvider })] }), flushOnSignals(flushTracing), fi-core setSession / setUser around each flow). Flows: supportFlow (ai.run step, real embedder, custom retriever, generate with tool lookupOrder: 2 model calls), streamFlow (generateStream), failingFlow (tool throws, toolChoice: "required").

Run Trace ids
Gemini gemini-3.7-flash + gemini-embedding-001, default 9f9d8616-37e2-95ba-e8dc-608154b7e317, fdc23b2f-b560-a31e-669a-285cfee6de5a, af62ad95-0cb6-3dfb-1c99-c97eb2adf145 (ERROR)
Gemini, FI_HIDE_INPUTS/OUTPUTS/EMBEDDING_VECTORS=true 91372b15-6930-9caf-85f2-c8fb1b4a81ef, 30a4e115-2c57-5e4d-a665-917597ffa83b, f54dde0c-b2f5-8a8c-0258-128ba3140027 (ERROR)
OpenAI gpt-4.1-nano + text-embedding-3-small via @genkit-ai/compat-oai, default (live vendor) 67ce77eb-43a4-d6c1-3a7a-943ca8f925a2, 12891ab0-ca9d-89bf-0bf3-2fa77d06a378, ba54c11d-b626-4e5b-4fbc-de87673a693d (ERROR)
OpenAI, hidden c03084e2-b9bc-7b7b-bff1-a18c88c0571d, b2aec706-3138-9077-f005-dee7f40c7a6a, 57bb2f46-eb73-ee94-fa3d-e88d4a503eb6 (ERROR)
Docs page snippet, verbatim (OpenAI variant) 2e96ea7c-1a9d-e9a5-3d6d-26123802563f

Stored-span check (span tree read back from the store; markers masked):

- [chain] supportFlow in=72B out=163B
  - [chain] prepare
  - [embedding] googleai/gemini-embedding-001          embedding.model_name=gemini-embedding-001
  - [retriever] kbRetriever
  - [chain] generate
    - [llm] googleai/gemini-3.7-flash model=gemini-3.7-flash provider=google tok=142/19
    - [tool] lookupOrder
    - [chain] generate
      - [llm] googleai/gemini-3.7-flash model=gemini-3.7-flash tok=276/58
- [chain] failingFlow status=ERROR: card declined for 5 USD, ref <marker>-ERR, owner <email>, config [cwd]/billing.json
  - [chain] generate status=ERROR (same)
    - [llm] googleai/gemini-3.7-flash tok=82/15
    - [tool] chargeCard status=ERROR (same)
- [chain] failingFlow status=ERROR: Error: __REDACTED__        (hidden run)
PASS

Cost is computed by Future AGI from the bare model name (e.g. $0.000178 / $0.000425 for the two Gemini calls in the first trace, $0.0000111 for a gpt-4.1-nano call); session.id and user.id are on every span. The Observe UI shows the tree, kinds, model, provider, tokens, cost, the input/output messages (default) and REDACTED plus Error: __REDACTED__ (hidden); checked by eye.

Privacy proof. Each run used a fresh unique marker in the prompt, a knowledge-base document, the tool result and the failing tool's error text (plus an email and the working-directory path in that error). Every stored row carrying the project id was scanned (every ClickHouse table with a project_id column, the Postgres rows):

  • Default: the marker is present (content on: 13 span rows in the Gemini run, 14 in the OpenAI run); 0 hits for the home directory, the OS user name, the host name, the email, exception.stacktrace, stack frames or node_modules; the error text reads <email> and [cwd]/billing.json.
  • Hidden (FI_HIDE_INPUTS + FI_HIDE_OUTPUTS + FI_HIDE_EMBEDDING_VECTORS): 0 hits for the marker, the prompt text, the order id, the tool error text, the answer text, the vector values, and the identity strings above, in both the Gemini and the OpenAI run. Across the whole verification project (125 spans, every run including an intermediate build's and quota-interrupted ones) 0 hits for any hidden run's marker or the identity strings.

Limits and notes

  • The shared Gemini free-tier daily quota (20 requests per model per day) ran out mid-session (429 RESOURCE_EXHAUSTED); the docs page's exact Google AI snippet completed its first model call and the tool call, and the OpenAI variant of the same snippet ran to completion.
  • When a script's flow throws and await flushTracing() comes after it, the process exits on the unhandled rejection and Genkit's error spans (and their parents) are never exported. The docs page flushes in finally.
  • Still as before: AC-08 clean install is a symlinked import check; reranker / evaluator / agent kinds come from source and mock-model journeys; Genkit Python / Go not covered; register({ batch: true }) is a no-op on sdk-trace-base 2.x (fi-core).

Docs page: future-agi/docs#1016 (draft, base main), suggested nav: Frameworks & Agents → Firebase Genkit (/docs/integrations/traceai/genkit).

Merge order unchanged: #203 first (merge commit), the README catalog PR #250 last.

🤖 Generated with Claude Code

nik13 added 8 commits October 3, 2026 18:58
The Node OTLP exporter streams with Transfer-Encoding: chunked and sends
no Content-Length. The Receiver read Content-Length only, so every Node
export came back 400 with zero spans; two TH-8103 children had to put a
de-chunking relay in front of it. Receiver now accepts both framings.

It also records one entry per accepted export (path, lower-cased
headers, flattened resource attributes) via requests(), so a contract
test can assert X-Api-Key/X-Secret-Key and project_name through the
shared harness instead of a private recorder.

Verified: 6 harness tests pass, and the TanStack example's real Node
exporter delivered 4 chunked exports (4 spans, collector path, both
auth headers, project_name/project_type) with no relay.

Refs: TH-8339, TH-8103
New TypeScript package typescript/packages/traceai_genkit (@traceai/genkit,
JS only). FIGenkitSpanProcessor is passed to Genkit's
enableTelemetry({ spanProcessors }). Genkit's own telemetry-server processor
stays first (@genkit-ai/core src/tracing/node-telemetry-provider.ts:66-77), so
the Dev UI keeps working. For each ended Genkit span the processor builds a
mapped copy (Genkit's span is never modified) and hands it to the
@traceai/fi-core provider's span processor and OTLP exporter with the Future
AGI resource. Also: flushOnSignals(flush) for SIGTERM/SIGINT, README, root
README row, jest unit tests, shared-harness contract test, lockfile entries.

Tested: genkit 1.42.0 (@genkit-ai/core 1.42.0, @genkit-ai/ai 1.42.0, all
Apache-2.0 per node_modules LICENSE/package.json), Node 20.20.2, 22.23.3,
26.8.1. genkit is a peerDependency (^1.42.0) and a 1.42.0 devDependency only.

Inventory (contract/inventory.mjs, Genkit's mockModel from genkit/testing,
one flow with an ai.run() step and generateStream with a tool loop, called
and streamed: 14 spans, scope genkit-tracer, sdk-trace-base 1.25 shape):
- action/flow: genkit:type, genkit:metadata:subtype, genkit:key, genkit:name,
  genkit:isRoot (bool), genkit:path, genkit:input, genkit:output,
  genkit:state, genkit:metadata:context
- flowStep: genkit:type, genkit:name, genkit:path, genkit:output, genkit:state
- util (generate): genkit:type, genkit:name, genkit:path, genkit:input,
  genkit:output, genkit:state
- action/model and action/tool: genkit:type, genkit:metadata:subtype,
  genkit:key, genkit:name, genkit:path, genkit:input, genkit:output,
  genkit:state
- model genkit:output JSON: message, finishReason, usage {inputTokens,
  outputTokens, totalTokens}, latencyMs
- beta agent run adds promptTemplate (render, genkit:metadata:promptName),
  flowStep runTurn-1 (genkit:metadata:agent:snapshotId) and action/agent
  (genkit:metadata:agent:sessionId, genkit:init); errors add
  genkit:isFailureSource.
Kind map from genkit:type labels (core action.ts:538-543, flow.ts:219-221,
ai generate/action.ts:147-149, prompt.ts:273-275,490-492) and the ActionType
list (core registry.ts:40-62): model/background-model LLM, tool/tool.v2 TOOL,
retriever RETRIEVER, embedder EMBEDDING, reranker RERANKER, evaluator
EVALUATOR, agent AGENT, flow and other actions CHAIN, flowStep/util/
promptTemplate/dotprompt CHAIN, unknown type: no kind, span still exported.
Usage: only inputTokens/outputTokens/totalTokens (seen on the dumped model
spans) go to gen_ai.usage.input_tokens/output_tokens/total_tokens, on model
spans only. thoughtsTokens, cachedContentTokens and the character/media
counters exist in GenerationUsageSchema (ai model-types.ts:260-275) but no
inventoried span carried them: unavailable, not mapped. Promoted token/cost
keys on a non-model span move to genkit.usage.<key>; the contract test checks
the trace sum equals the two model calls (32/7/39). Model:
gen_ai.request.model = genkit:name of the model action span (not parsed);
provider and cost unavailable.

Deviations from the TH-8240 architecture/docs, with evidence:
1. enableTelemetry and flushTracing are exported from genkit/tracing
   (genkit/src/tracing.ts:43-44), not genkit. genkit/tracing does not
   initialise the telemetry provider; importing genkit does
   (genkit/src/common.ts:173-175 -> @genkit-ai/core src/node.ts:20-23).
   enableTelemetry before any genkit import throws FAILED_PRECONDITION:
   TelemetryProvider is not initialized (observed). README says import
   genkit first.
2. @genkit-ai/core 1.42.0 telemetryTypes.ts is 24 lines and has only
   TelemetryConfig = Partial<NodeSDKConfiguration> (line 24); there is no
   TelemetryOptions with forceDevExport / metricExportIntervalMillis /
   disableMetrics. Those are @genkit-ai/google-cloud options (src/types.ts:
   92,110). The example does not pass disableMetrics (types.test.ts asserts
   it is a type error). Metrics are not exported anyway: enableTelemetry adds
   no metricReader and @opentelemetry/sdk-node 0.52.1 builds a meter provider
   only with a reader or views (build/src/sdk.js:97-105,158). Dev UI export
   is not controlled by forceDevExport: Genkit always prepends its
   telemetry-server processor.
3. register() must be called with setGlobalTracerProvider: false (the docs
   example omits it). fi-core defaults it to true (otel.ts:486,604-605);
   Genkit takes its tracer from the global API (instrumentation.ts:96,197),
   so with fi-core global the NodeSDK cannot register and Genkit spans reach
   Future AGI raw (content, no kind). Contract control test covers this; the
   processor emits warning TRACEAI_GENKIT_GLOBAL_PROVIDER.
4. Session: a native key exists for beta agents only,
   genkit:metadata:agent:sessionId (ai/src/agent.ts:1061-1063), mapped to
   session.id on the agent span. Flows have none; the app sets it with
   fi-core setSession() around the call (read in onStart from the parent
   context). No session id is invented.
5. Tool name is genkit:name of the tool action span (spec: unverified).
6. AC-06 flush: flushTracing is confirmed (core src/tracing.ts:113). genkit
   registers SIGTERM/SIGINT listeners at import that call process.exit(0)
   without flushing (genkit/src/genkit.ts:786-793), so an app listener that
   awaits flushTracing() loses the race (contract control test). Added
   flushOnSignals(flushTracing), which runs the flush before the earlier
   listeners.
7. Genkit's NodeSDK uses sdk-trace-base 1.25 spans (parentSpanId,
   instrumentationLibrary); fi-core exports with 2.x (parentSpanContext,
   instrumentationScope). The processor converts the copy and reads the
   fi-core provider's _activeSpanProcessor/_resource (no public accessor).
8. Kinds beyond the spec's list (reranker, evaluator, agent,
   background-model, tool.v2, non-action types) come from the inventory.
9. Lockfile: @genkit-ai/core lists @genkit-ai/firebase as an
   optionalDependency, so the dev-only tree adds firebase-admin and
   @genkit-ai/google-cloud. Neither is a dependency of @traceai/genkit.
   pnpm-lock.yaml keeps 3eaadc8 byte for byte plus the new importer and the
   177 packages / 180 snapshots reachable from it that the base lacked
   (git diff --minimal: 2374 insertions, 0 deletions);
   pnpm install --frozen-lockfile (with and without --ignore-scripts)
   exits 0: "Lockfile is up to date, resolution step is skipped".

Tests (this tree): jest 5 suites, 62 tests passed (Node 26.8.1, 20.20.2,
22.23.3). Contract suite 20 passed with NODE_BINARY = node 26.8.1, 20.20.2
and 22.23.3. pnpm pack: 63 files, dist/ + README.md + package.json only.

approved: Nikhil 2026-10-03 blanket
Refs: TH-8240
Installed-review N1. Genkit's ValidationError embeds the rejected data in
its message after "Provided data:" (@genkit-ai/core 1.42.0 src/schema.ts:78)
and instrumentation.ts:156-162 copies e.message to the span status and an
exception event on every span the error passes through. The processor passed
status and events through unchanged, so with captureContent off (default) a
structured ai.generate whose model output fails the schema exported that
output to Future AGI.

With captureContent off, toExportableSpan now cuts status.message and the
exception.message / exception.stacktrace attributes of exception events at
"Provided data:" and appends "[data redacted: captureContent is off]".
exception.type, the event and every other event are kept. New objects are
built; Genkit's span (read by the Dev UI exporter) is not modified. With
captureContent true both pass through unchanged.

Tests (written first):
- jest processor.test.ts: marker in status.message and an exception event,
  off redacts / on keeps / no-marker unchanged. RED on 24a2929: the off case
  failed (marker present), 16 others passed.
- contract: new `schema` journey (ai.generate with an output schema, the fake
  mockModel returns non-matching JSON). Asserts the model output text and
  its marker appear nowhere in the export, both error spans end with the
  note, exception.type kept ("400", GenkitError.code). RED on 24a2929 (marker
  found in the exported exception event); opt-in control passed.
- GREEN: jest 65 passed; contract 20 passed (Node 26.8.1, no matrix).

README: Content table row and paragraph for error-message redaction; the
Errors line no longer says status/events pass through unchanged.

approved: Nikhil 2026-10-03 blanket
Refs: TH-8240
…pans

Installed-review N3. isPromotedUsageKey matched only gen_ai.cost.total and
llm.cost.total exactly, so cost parts (gen_ai.cost.input/output,
llm.cost.prompt/completion) set through fi-core context attributes or Genkit
telemetryLabels stayed unprefixed on flow/step/generate/tool spans, where
fi-collector would sum them into the trace cost again. Match the cost
families by prefix (gen_ai.cost., llm.cost.), like the token families. Model
spans keep the keys unprefixed.

Tests (written first), mapping.test.ts:
- every gen_ai.cost.* / llm.cost.* key is promoted (parts and totals);
  look-alikes (gen_ai.costs, llm.costing, genkit.usage.*) are not.
- cost parts on a flow span, from span and from context attributes, move to
  genkit.usage.<key>.
- a model span keeps them unprefixed (control).
RED on b5cb204: the first two failed (2 failed, 37 passed in the file).
GREEN: jest 68 passed; contract 20 passed (Node 26.8.1). The contract
helper's promoted-key check now uses the same prefixes.

README: the promoted-key list names the cost prefixes.

approved: Nikhil 2026-10-03 blanket
Refs: TH-8240
Installed-review README findings.

N4: the Node row claimed the contract suite and import check on 20.20.2,
22.23.3 and 26.8.1, but NODE_BINARY is one Node per run and
TRACEAI_NODE_MATRIX parametrizes only the entry-point import check. The
full contract suite was rerun once per Node (NODE_BINARY=20.20.2, 22.23.3,
26.8.1; TRACEAI_NODE_MATRIX = all three each time): 22 passed on each at
4973cae. jest also ran once per Node (that Node first on PATH): 68 passed
each. The Node row and Tests section now say exactly that, name what
TRACEAI_NODE_MATRIX parametrizes, and note the import check uses a symlink,
not a clean npm install.

N2: new subsection: using the Firebase / Google Cloud telemetry plugin
(enableFirebaseTelemetry, enableGoogleCloudTelemetry,
ENABLE_FIREBASE_MONITORING) with this package is not supported in this
release. Reason: each enableTelemetry() starts another NodeSDK; only the
first gets Genkit's spans and flushTracing() flushes only the last.
Checked with a local probe (genkit 1.42.0, no plugin, two enableTelemetry
calls with recording processors): first processor got the flow span and no
flush, second got no span and the flush. Includes the PRD cost note (Future
AGI export adds no GCP cost; the GCP copy may be billed by Google).

N5: call flushOnSignals after every other SIGTERM/SIGINT listener; later
listeners run in parallel with the flush; a second signal is ignored until
the flush ends or times out.
N6: fi-core TraceConfig / FI_HIDE_* masking is not applied; captureContent
is the only content switch.
N7: shutdown() also shuts down the register() provider passed in (Genkit's
NodeSDK calls it on SIGTERM); use a dedicated provider for Genkit.

Docs only; no code or test change.

approved: Nikhil 2026-10-03 blanket
Refs: TH-8240
…y ordering

verify-r2 (t_f01a72b2) follow-ups:
- validateSupport middleware (Request: <JSON>) and defineSimpleRetriever
  (Item: <JSON>) errors carry data without the Provided data: marker and
  are exported as thrown; parse-error key paths before the marker stay.
- enableTelemetry({ spanProcessors }) must run before the first Genkit
  span, otherwise Genkit's own enableTelemetry({}) wins
  (@genkit-ai/core src/tracing.ts:32-40).

N4-r2 is evidence only: coordinator reran jest on Node 20.20.2/22.23.3
(68 passed each) and the full contract with NODE_BINARY 22.23.3 (22 passed),
backing the README Node row as written.

approved: Nikhil 2026-10-03 blanket
Refs: TH-8240
@nik13
nik13 requested a review from NVJKKartik October 10, 2026 08:15
nik13 and others added 3 commits October 11, 2026 19:49
The Google AI / Vertex AI plugin (@genkit-ai/google-genai 1.42.0,
lib/googleai/gemini.js:642-646) reports usage.thoughtsTokens and
usage.cachedContentTokens. Gemini's outputTokens excludes thinking tokens,
so they were only visible inside total_tokens: a real gemini-3.8-flash
haiku call exported input 14 / output 12 / total 410 with the 384 thinking
tokens unattributed.

Map thoughtsTokens to gen_ai.usage.output_tokens.reasoning and
cachedContentTokens to gen_ai.usage.cache_read_tokens (model spans only,
same as the other usage keys). Verified end to end against a local Future
AGI collector with gemini-3.5-flash: 66 + 15 + 39 reasoning = 120 total.

README: list the two keys, drop them from the unavailable list, and say the
package was also run against the real Google AI plugin.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
…I prices them

gen_ai.request.model carried the registered action name
("googleai/gemini-3.5-flash"). fi-collector prices a span by an exact
model-name lookup in its litellm table, which lists "gemini-3.5-flash",
so every Genkit model span showed cost 0 and no provider.

Model spans now get the vendor model id without the plugin namespace on
gen_ai.request.model and gen_ai.response.model, and gen_ai.provider.name
from the namespace for the model plugins whose namespaces were read from
their published packages (googleai -> google, vertexai -> vertexai,
openai, deepseek, xai, anthropic, ollama, mistral -> mistralai, groq,
aws-bedrock -> aws, azure-openai -> azure). Other namespaces leave the
provider unset. The full registered name stays on genkit:name.

Live check with gemini-3.5-flash: model gemini-3.5-flash, provider
google, cost 0.000234 / 0.000357 / 0.000147 in ClickHouse and the UI
(was 0). README: replace the retired gemini-2.5-flash example, document
the provider map and how cost is computed.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
The catalog rows for all TH-8103 integrations now live in one PR
(docs/th-8103-integration-catalog), so this PR no longer conflicts with
its siblings in README.md and can merge in any order.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
…ths, embedder model (TH-8240)

Re-verification 2026-10-16 against local Future AGI with real genkit
1.42.0 (latest stable on npm, same as the pin), @genkit-ai/google-genai
1.42.0 (gemini-3.7-flash / gemini-3.8-flash, gemini-embedding-001) and
@traceai/fi-core 1.0.0 from npm.

Privacy default (TH-8103 coordinator decision 2026-10-16): follow the
traceAI TraceConfig convention instead of the package's own
captureContent switch. Content is exported by default (fi-core
DEFAULT_HIDE_* = false); FI_HIDE_INPUTS / FI_HIDE_OUTPUTS (and
FI_HIDE_INPUT_MESSAGES / _OUTPUT_MESSAGES / _INPUT_TEXT / _OUTPUT_TEXT /
FI_HIDE_EMBEDDING_VECTORS) or traceConfig hide it, with fi-core's
parsing (only "true" hides) and precedence (option > env > default). The
env vars apply even when no traceConfig is passed. A hidden side drops
the genkit:* content keys and sets input.value / output.value to
__REDACTED__ (no mime type), as fi-core's masking does. Passing the
removed captureContent option logs a diag warning; captureContent: false
still hides inputs and outputs.

Never exported, in either mode (the real run on the previous head
exported the failing tool's message with the user marker, an email and
the absolute working-directory path in status_message, with content
"off"):
- exception.stacktrace is dropped from every exception event;
- stack frames and local paths are removed from every exported string
  (cwd -> [cwd], home -> ~, other absolute local paths / file URLs /
  Windows paths -> [path]), JSON-aware;
- status message and exception.message lose emails and this machine's
  user and host names; with inputs or outputs hidden the message becomes
  "<exception.type>: __REDACTED__" (this replaces the "Provided data:"
  cut, which only covered schema-validation errors).

Embedder spans now carry embedding.model_name (bare id) and
gen_ai.provider.name from genkit:name.

Tests: jest 95 passed on Node 20.20.2 and 22.18.0; contract suite 22
passed with NODE_BINARY 20.20.2 and 22.18.0 (matrix 20/22).

Co-Authored-By: Claude Opus 5.5 <[email protected]>
@nik13
nik13 merged commit 1ece51b into dev Oct 26, 2026
4 checks passed
@nik13
nik13 deleted the feat/th-8240-genkit branch October 26, 2026 10:03
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.

1 participant