fix(observability): address code review findings from PR #860 - #875
Conversation
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
|
@coderabbitai review |
✅ Single Commit Policy - COMPLIANTStatus: Policy requirements met • 1 commit • Valid format • Ready for merge 📊 View validation details📝 Commit Details
✅ Validation Results
🤖 Automated validation by NeuroLink Single Commit Enforcement |
✅ Actions performedReview triggered.
|
|
Important Review skippedAuto incremental reviews are disabled on this repository. Please check the settings in the CodeRabbit UI or the ⚙️ Run configurationConfiguration used: Organization UI Review profile: CHILL Plan: Pro Run ID: You can disable this status message by setting the Use the checkbox below for a quick retry:
WalkthroughThe PR centralizes observability changes: it moves span recording to SpanSerializer, adds safe metadata filtering for exporters, trims metrics latency arrays with spans, extends redaction keys, makes batch flush resilient, emits non-blocking generation:end on errors, and tweaks minor provider text. Public APIs unchanged. Changes
Sequence Diagram(s)sequenceDiagram
participant Client as Client
participant Base as BaseProvider
participant Span as SpanSerializer
participant Neu as Neurolink
participant Metrics as MetricsAggregator
participant Export as LangfuseExporter
rect rgba(200,230,255,0.5)
Client ->> Base: request.generate()
Base ->> Span: startSpan(...)
Base ->> /* provider call */ Base: call provider
Base ->> Span: endSpan(...) %% now authoritative end
Span ->> Export: toLangfuseFormat(filterSafeMetadata)
end
rect rgba(255,230,200,0.5)
Base ->> Neu: (on error) emit generation:end (non-blocking)
Neu -->> Metrics: listeners record metrics (authoritative)
end
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~20 minutes Possibly related PRs
Suggested labels
Suggested reviewers
Poem
🚥 Pre-merge checks | ✅ 2 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
📝 Coding Plan
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
🤖 AI Review & Build Compliance ✅Status: AI analysis complete • Build rules validated • Ready for review 📊 View detailed analysis results🛡️ Analysis Complete
📋 Ready for Merge When
🤖 AI analysis complete - check individual code comments for specific feedback |
There was a problem hiding this comment.
Actionable comments posted: 3
🧹 Nitpick comments (1)
src/lib/observability/utils/spanSerializer.ts (1)
400-419: Avoid duplicatingSAFE_METADATA_KEYS/filterSafeMetadataacross observability modules.The same whitelist/helper exists in
src/lib/observability/exporters/langfuseExporter.ts; keeping two copies increases drift risk.♻️ Refactor direction
-const SAFE_METADATA_KEYS = new Set([...]); -function filterSafeMetadata(...) { ... } +import { filterSafeMetadata } from "./safeMetadata.js";Create a shared utility (e.g.,
src/lib/observability/utils/safeMetadata.ts) and reuse it from both serializer and exporter.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/lib/observability/utils/spanSerializer.ts` around lines 400 - 419, Extract the duplicate SAFE_METADATA_KEYS and filterSafeMetadata into a single shared module (e.g., export const SAFE_METADATA_KEYS and export function filterSafeMetadata from a new safeMetadata utility) and update both spanSerializer.ts and langfuseExporter.ts to import and use those exports instead of keeping local copies; remove the local definitions of SAFE_METADATA_KEYS and filterSafeMetadata from those files so both modules reference the single shared implementation and preserve the current behavior and API (same symbol names: SAFE_METADATA_KEYS, filterSafeMetadata).
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@src/lib/core/baseProvider.ts`:
- Around line 302-304: Error paths from generateTextInternal and provider.stream
currently neither emit generation:end/stream:complete nor record spans, losing
metrics; restore metrics and events on error by adding conditional
SpanSerializer.recordSpan()/SpanSerializer.endSpan() (or the same recording
logic used on success) and emitting the corresponding events before rethrowing
in the error handlers inside baseProvider (targets: generateTextInternal,
provider.stream) OR, alternatively, update the neurolink.ts error handlers that
catch these exceptions to emit generation:end and stream:complete (and then call
the existing SpanSerializer.endSpan) so every failure path produces the same
completion events and recorded spans the listeners expect.
In `@src/lib/observability/metricsAggregator.ts`:
- Around line 183-186: The latency retention check in MetricsAggregator is
off-by-one: update the trimming condition that references this.latencyValues and
this.config.maxSpansRetained to use >= instead of > so latencyValues never
exceeds the configured cap; locate the block where latencyValues.shift() is
called and change the comparison operator to >= to keep latency retention
strictly aligned with span retention.
In `@src/lib/observability/spanProcessor.ts`:
- Around line 316-321: The try/catch around onBatchReady can let this.batch grow
unbounded when exports repeatedly fail; add a bounded retry backlog by defining
a constant (e.g. MAX_RETRY_BACKLOG) and, inside the catch block for the
onBatchReady call in the SpanProcessor class, trim this.batch to that maximum
(keep the most recent spans via this.batch =
this.batch.slice(-MAX_RETRY_BACKLOG)) so failed spans are retained only up to
the configured limit; update any relevant comments and ensure the constant is
reasonable and documented near the class.
---
Nitpick comments:
In `@src/lib/observability/utils/spanSerializer.ts`:
- Around line 400-419: Extract the duplicate SAFE_METADATA_KEYS and
filterSafeMetadata into a single shared module (e.g., export const
SAFE_METADATA_KEYS and export function filterSafeMetadata from a new
safeMetadata utility) and update both spanSerializer.ts and langfuseExporter.ts
to import and use those exports instead of keeping local copies; remove the
local definitions of SAFE_METADATA_KEYS and filterSafeMetadata from those files
so both modules reference the single shared implementation and preserve the
current behavior and API (same symbol names: SAFE_METADATA_KEYS,
filterSafeMetadata).
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
Run ID: 87ebfde2-4aa1-4a4c-8d62-c44193710011
📒 Files selected for processing (7)
src/lib/core/baseProvider.tssrc/lib/observability/exporters/langfuseExporter.tssrc/lib/observability/metricsAggregator.tssrc/lib/observability/spanProcessor.tssrc/lib/observability/utils/spanSerializer.tssrc/lib/providers/googleVertex.tstest/zod-schema-test-function.ts
34eee03 to
1f26871
Compare
CodeRabbit Review Feedback Addressed (Cycle 1)3 Items Fixed
Validation
@coderabbitai review |
|
Triggering a fresh incremental review now to cover these changes. ✅ Actions performedReview triggered.
|
🤖 AI Review & Build Compliance ✅Status: AI analysis complete • Build rules validated • Ready for review 📊 View detailed analysis results🛡️ Analysis Complete
📋 Ready for Merge When
🤖 AI analysis complete - check individual code comments for specific feedback |
1f26871 to
f9d27a5
Compare
Nitpick Addressed (Cycle 2)Extracted
All 3 actionable comments + 1 nitpick from Review 1 are now resolved. @coderabbitai review |
|
🧠 Learnings used✅ Actions performedReview triggered.
|
🤖 AI Review & Build Compliance ✅Status: AI analysis complete • Build rules validated • Ready for review 📊 View detailed analysis results🛡️ Analysis Complete
📋 Ready for Merge When
🤖 AI analysis complete - check individual code comments for specific feedback |
There was a problem hiding this comment.
Actionable comments posted: 4
♻️ Duplicate comments (1)
src/lib/core/baseProvider.ts (1)
302-304:⚠️ Potential issue | 🟠 Major
SpanSerializer.endSpan(...)return values are dropped, so these calls are effectively no-ops.At Line 304, Line 322, Line 1026, and Line 1033,
SpanSerializer.endSpan(...)returns an ended span object, but nothing consumes it and no recorder/exporter is invoked in this file. With the current serializer behavior, this can silently lose providerstream/generatespan data unless another path records every one of these spans.Please either (a) forward the returned ended span to the authoritative recorder in these paths, or (b) remove this local span lifecycle entirely if
neurolink.tsis the only source of truth.Also applies to: 320-322, 1022-1026, 1031-1033
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/lib/core/baseProvider.ts` around lines 302 - 304, SpanSerializer.endSpan(...) calls currently drop their return value so the ended span is never recorded; capture the returned ended span from each call (the ones at/around SpanSerializer.endSpan lines noted) and forward it to the authoritative recorder/exporter used by the system instead of discarding it — for example, call the central recorder (the same component neurolink.ts uses) to record the span (e.g., getMetricsAggregator().recordSpan(endedSpan) or the project's equivalent recorder.recordSpan(endedSpan)); if neurolink.ts is intended to be the single source of truth, remove the local endSpan usage entirely and ensure only neurolink.ts produces/records spans.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@src/lib/neurolink.ts`:
- Around line 3202-3215: The telemetry listener for the "generation:end" event
is always marking spans as SpanStatus.OK even when the emitted payload indicates
failure; update the generation:end event handler to inspect the payload (e.g.,
payload.success and/or payload.error from the emitter.emit call that includes
provider, model, responseTime, error, success) and set the span status to a
non-OK value (e.g., SpanStatus.ERROR) when success is false or an error is
present instead of always using SpanStatus.OK; locate the handler that
references SpanStatus.OK and change the conditional logic to set
SpanStatus.ERROR for failures and SpanStatus.OK only for successful completions.
In `@src/lib/observability/metricsAggregator.ts`:
- Around line 181-186: When evicting the oldest span in MetricsAggregator,
remove the corresponding latency sample for that specific evicted span instead
of only trimming latencyValues by length; change the block that currently calls
this.spans.shift() to capture the removed span (const evicted =
this.spans.shift()), then if evicted.durationMs is defined remove one matching
entry from this.latencyValues (e.g., find the index of evicted.durationMs and
splice it out) so latencyValues stays aligned with spans regardless of spans
without durationMs; keep the maxSpansRetained guard but base latency removal on
the evicted span's durationMs rather than only on this.latencyValues.length.
In `@src/lib/observability/spanProcessor.ts`:
- Around line 316-325: flush() currently calls onBatchReady(spans) without
handling promise rejections, which can lose spans if onBatchReady is async and
rejects; change flush() to wrap the callback with
Promise.resolve(this.onBatchReady(spans)).then(() => { clear this.batch })
.catch(() => { keep this.batch but cap growth using this.batchSize * 20 and
slice to the newest entries }), ensuring synchronous callers of flush() are not
forced to await but both sync throws and async rejections are handled; reference
the methods/fields flush(), onBatchReady, this.batch and this.batchSize (and
consider shutdown() behavior) when applying the change.
In `@src/lib/observability/utils/safeMetadata.ts`:
- Around line 11-18: SAFE_METADATA_KEYS currently includes "stream.chunk_count"
and "stream.content_length", which expands the allowlist beyond the intended
ai.* trace metadata contract; update SAFE_METADATA_KEYS to only include the four
ai.* keys ("ai.provider", "ai.model", "ai.temperature", "ai.max_tokens") by
removing the two stream.* entries so only the declared trace metadata is sent to
third-party backends (reference the SAFE_METADATA_KEYS symbol to locate and
change the Set contents).
---
Duplicate comments:
In `@src/lib/core/baseProvider.ts`:
- Around line 302-304: SpanSerializer.endSpan(...) calls currently drop their
return value so the ended span is never recorded; capture the returned ended
span from each call (the ones at/around SpanSerializer.endSpan lines noted) and
forward it to the authoritative recorder/exporter used by the system instead of
discarding it — for example, call the central recorder (the same component
neurolink.ts uses) to record the span (e.g.,
getMetricsAggregator().recordSpan(endedSpan) or the project's equivalent
recorder.recordSpan(endedSpan)); if neurolink.ts is intended to be the single
source of truth, remove the local endSpan usage entirely and ensure only
neurolink.ts produces/records spans.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
Run ID: c52b3c0d-db75-40ab-9917-105cf17e81ee
📒 Files selected for processing (9)
src/lib/core/baseProvider.tssrc/lib/neurolink.tssrc/lib/observability/exporters/langfuseExporter.tssrc/lib/observability/metricsAggregator.tssrc/lib/observability/spanProcessor.tssrc/lib/observability/utils/safeMetadata.tssrc/lib/observability/utils/spanSerializer.tssrc/lib/providers/googleVertex.tstest/zod-schema-test-function.ts
🚧 Files skipped from review as they are similar to previous changes (4)
- src/lib/observability/utils/spanSerializer.ts
- src/lib/providers/googleVertex.ts
- src/lib/observability/exporters/langfuseExporter.ts
- test/zod-schema-test-function.ts
| if (this.spans.length >= this.config.maxSpansRetained) { | ||
| this.spans.shift(); // Remove oldest span | ||
| // Note: We keep aggregated metrics, only raw spans are trimmed | ||
| // Trim latencyValues in sync to prevent unbounded memory growth | ||
| if (this.latencyValues.length >= this.config.maxSpansRetained) { | ||
| this.latencyValues.shift(); | ||
| } |
There was a problem hiding this comment.
Keep latency trimming tied to the evicted span, not only to max length.
Current logic can leave stale latency samples when the removed span had durationMs but latencyValues.length is still below maxSpansRetained (mixed spans with/without duration), which can skew latency stats.
💡 Suggested fix
- if (this.spans.length >= this.config.maxSpansRetained) {
- this.spans.shift(); // Remove oldest span
- // Trim latencyValues in sync to prevent unbounded memory growth
- if (this.latencyValues.length >= this.config.maxSpansRetained) {
- this.latencyValues.shift();
- }
+ if (this.spans.length >= this.config.maxSpansRetained) {
+ const removedSpan = this.spans.shift(); // Remove oldest span
+ // Trim latencyValues in sync with the removed span
+ if (removedSpan?.durationMs !== undefined && this.latencyValues.length > 0) {
+ this.latencyValues.shift();
+ }
// Note: We keep aggregated metrics, only raw spans and latency values are trimmed
}🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@src/lib/observability/metricsAggregator.ts` around lines 181 - 186, When
evicting the oldest span in MetricsAggregator, remove the corresponding latency
sample for that specific evicted span instead of only trimming latencyValues by
length; change the block that currently calls this.spans.shift() to capture the
removed span (const evicted = this.spans.shift()), then if evicted.durationMs is
defined remove one matching entry from this.latencyValues (e.g., find the index
of evicted.durationMs and splice it out) so latencyValues stays aligned with
spans regardless of spans without durationMs; keep the maxSpansRetained guard
but base latency removal on the evicted span's durationMs rather than only on
this.latencyValues.length.
| try { | ||
| this.onBatchReady(spans); | ||
| this.batch = []; | ||
| } catch { | ||
| // Keep spans for next flush attempt, but cap backlog growth | ||
| const maxBacklog = this.batchSize * 20; | ||
| if (this.batch.length > maxBacklog) { | ||
| this.batch = this.batch.slice(this.batch.length - maxBacklog); | ||
| } | ||
| } |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo "1) Locate BatchProcessor constructions and onBatchReady assignments"
rg -nP --type=ts -C3 'new\s+BatchProcessor\s*\(|onBatchReady\s*:' src
echo
echo "2) Detect explicitly async onBatchReady callbacks"
rg -nP --type=ts -C2 'onBatchReady\s*:\s*async\b' src
echo
echo "Expected result:"
echo "- If any async onBatchReady exists, current flush() path can drop spans on rejection."
echo "- If none exist today, this is still a latent correctness bug for future callsites."Repository: juspay/neurolink
Length of output: 127
🏁 Script executed:
#!/bin/bash
set -euo pipefail
# Check if the file exists and get its size
echo "=== Checking file existence and size ==="
if [ -f "src/lib/observability/spanProcessor.ts" ]; then
wc -l src/lib/observability/spanProcessor.ts
echo "File exists"
else
echo "File not found at src/lib/observability/spanProcessor.ts"
# Try to find the file
echo "Searching for spanProcessor files..."
fd -e ts spanProcessor
fiRepository: juspay/neurolink
Length of output: 155
🏁 Script executed:
#!/bin/bash
set -euo pipefail
# Read lines around 316-325 from the spanProcessor file
echo "=== Code at lines 316-325 ==="
sed -n '310,335p' src/lib/observability/spanProcessor.ts
echo
echo "=== Looking for flush method definition ==="
rg -n 'flush\s*\(' src/lib/observability/spanProcessor.ts | head -5
echo
echo "=== Looking for onBatchReady definition ==="
rg -n 'onBatchReady' src/lib/observability/spanProcessor.ts | head -10Repository: juspay/neurolink
Length of output: 1235
🏁 Script executed:
#!/bin/bash
set -euo pipefail
# Search for BatchProcessor class definition and onBatchReady signature
echo "=== Search for class definition ==="
rg -n 'class\s+BatchProcessor' src/lib/observability/spanProcessor.ts
echo
echo "=== Get full context around flush method ==="
# Use ast-grep to find flush method with context
ast-grep --pattern $'private flush() {
$$$
}'
echo
echo "=== Search for any async callbacks ==="
rg -n 'async\s*\(' src/lib/observability/spanProcessor.ts | head -10Repository: juspay/neurolink
Length of output: 200
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo "=== Where is BatchProcessor instantiated? ==="
rg -n 'new\s+BatchProcessor' src --type=ts
echo
echo "=== All onBatchReady assignments/usages ==="
rg -n 'onBatchReady' src --type=ts -B2 -A2
echo
echo "=== Check if any async callbacks exist in the codebase ==="
# Look for patterns where async callbacks might be passed
rg -n 'async\s*\(\s*spans' src --type=ts
</parameter>Repository: juspay/neurolink
Length of output: 105
🏁 Script executed:
#!/bin/bash
set -euo pipefail
# Read the full config type and constructor
echo "=== Full BatchProcessor constructor and config type ==="
sed -n '272,295p' src/lib/observability/spanProcessor.ts
echo
echo "=== Check shutdown behavior ==="
sed -n '331,350p' src/lib/observability/spanProcessor.ts
</parameter>Repository: juspay/neurolink
Length of output: 1515
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo "=== Search for BatchProcessor usage across entire codebase ==="
rg 'BatchProcessor' src --type=ts
echo
echo "=== Search for any async function passed as callback ==="
rg 'onBatchReady\s*[:=]' src --type=ts -A1Repository: juspay/neurolink
Length of output: 447
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo "=== Full shutdown method ==="
sed -n '331,345p' src/lib/observability/spanProcessor.ts
echo
echo "=== Check SpanProcessor interface to understand expectations ==="
rg -n 'interface SpanProcessor' src --type=ts -A8Repository: juspay/neurolink
Length of output: 1150
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo "=== All flush() call sites ==="
sed -n '295,340p' src/lib/observability/spanProcessor.ts
echo
echo "=== Check if flush is called from timer/synchronous context ==="
rg -B5 'this.flush()' src/lib/observability/spanProcessor.tsRepository: juspay/neurolink
Length of output: 2071
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo "=== Verify TypeScript behavior: can async be assigned to void-returning type? ==="
echo "Checking if there are any TypeScript compiler directives or strict settings..."
fd -e json tsconfig | head -3 | xargs cat 2>/dev/null | head -30
echo
echo "=== Check if BatchProcessor is exported for public use ==="
rg -n 'export.*BatchProcessor' src --type=ts
</parameter>Repository: juspay/neurolink
Length of output: 1307
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo "=== Check the learning from previous PR about Promise handling ==="
echo "The learning suggests using .then()/.catch() for backward compatibility..."
echo ""
echo "=== Look for similar Promise handling patterns in the codebase ==="
rg 'onBatchReady\s*\(.*\)\.then|\.catch' src --type=ts
echo
echo "=== Check if there are examples of .then/.catch handling elsewhere ==="
rg '\.then\(|\.catch\(' src/lib/observability --type=ts -B2 -A2 | head -40Repository: juspay/neurolink
Length of output: 5878
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo "=== Verify: Can an async function be assigned to a void-returning type in TypeScript strict mode? ==="
cat > /tmp/test.ts << 'EOF'
// Test TypeScript assignability with strict mode
type SyncCallback = (data: string[]) => void;
type AsyncCallback = (data: string[]) => Promise<void>;
const asyncFn: AsyncCallback = async (data) => { console.log(data); };
const syncFn: SyncCallback = asyncFn; // Does this error in strict mode?
EOF
echo "TypeScript strict mode allows assigning async functions to void-returning types"
echo "This is because Promise<void> is compatible with void in JavaScript execution"
echo
echo "=== Confirm the actual issue: unhandled promise rejection scenario ==="
echo "Current code path:"
echo "1. Line 317: this.onBatchReady(spans) // If async, returns Promise but not awaited"
echo "2. Line 318: this.batch = [] // Clears batch immediately"
echo "3. If Promise rejects later: catch block doesn't execute, spans lost"Repository: juspay/neurolink
Length of output: 629
flush() loses spans when onBatchReady is async.
Line 317 calls onBatchReady(spans) without awaiting. Although the type signature declares void, TypeScript allows assigning async functions to void-returning types. If an async callback is passed and rejects, the catch block (line 320) never runs, and line 318 has already cleared the batch, losing all spans.
This is critical during shutdown() (line 331), which is async but calls flush() synchronously at line 334. If the callback rejects, spans are lost without warning.
Fix: Use .then().catch() pattern to handle both sync and async callbacks while preserving backward compatibility:
Suggested fix (backward-compatible Promise handling)
private flush(): void {
if (this.batch.length > 0 && this.onBatchReady) {
const spans = [...this.batch];
this.batch = [];
- try {
- this.onBatchReady(spans);
- } catch {
- // Keep spans for next flush attempt, but cap backlog growth
- const maxBacklog = this.batchSize * 20;
- if (this.batch.length > maxBacklog) {
- this.batch = this.batch.slice(this.batch.length - maxBacklog);
- }
+ const result = this.onBatchReady(spans);
+ if (result instanceof Promise) {
+ result.catch(() => {
+ // Keep spans for next flush attempt on rejection, but cap backlog growth
+ this.batch.unshift(...spans);
+ const maxBacklog = this.batchSize * 20;
+ if (this.batch.length > maxBacklog) {
+ this.batch = this.batch.slice(this.batch.length - maxBacklog);
+ }
+ });
+ }
}
}This keeps flush() synchronous for callers while safely handling async callbacks.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@src/lib/observability/spanProcessor.ts` around lines 316 - 325, flush()
currently calls onBatchReady(spans) without handling promise rejections, which
can lose spans if onBatchReady is async and rejects; change flush() to wrap the
callback with Promise.resolve(this.onBatchReady(spans)).then(() => { clear
this.batch }) .catch(() => { keep this.batch but cap growth using this.batchSize
* 20 and slice to the newest entries }), ensuring synchronous callers of flush()
are not forced to await but both sync throws and async rejections are handled;
reference the methods/fields flush(), onBatchReady, this.batch and
this.batchSize (and consider shutdown() behavior) when applying the change.
f9d27a5 to
d9704bb
Compare
Review Feedback Addressed (Cycle 3)5 Items Fixed (all from CodeRabbit Review 2)
Cumulative Resolution (all cycles)
Validation
CommitAll changes squashed into single commit: @coderabbitai review |
|
Triggering an incremental review on commit ✅ Actions performedReview triggered.
|
🤖 AI Review & Build Compliance ✅Status: AI analysis complete • Build rules validated • Ready for review 📊 View detailed analysis results🛡️ Analysis Complete
📋 Ready for Merge When
🤖 AI analysis complete - check individual code comments for specific feedback |
d9704bb to
f5c4d0e
Compare
🤖 AI Review & Build Compliance ✅Status: AI analysis complete • Build rules validated • Ready for review 📊 View detailed analysis results🛡️ Analysis Complete
📋 Ready for Merge When
🤖 AI analysis complete - check individual code comments for specific feedback |
f5c4d0e to
a5cfb17
Compare
🤖 AI Review & Build Compliance ✅Status: AI analysis complete • Build rules validated • Ready for review 📊 View detailed analysis results🛡️ Analysis Complete
📋 Ready for Merge When
🤖 AI analysis complete - check individual code comments for specific feedback |
a5cfb17 to
43ad68d
Compare
🤖 AI Review & Build Compliance ✅Status: AI analysis complete • Build rules validated • Ready for review 📊 View detailed analysis results🛡️ Analysis Complete
📋 Ready for Merge When
🤖 AI analysis complete - check individual code comments for specific feedback |
43ad68d to
e83031d
Compare
🤖 AI Review & Build Compliance ✅Status: AI analysis complete • Build rules validated • Ready for review 📊 View detailed analysis results🛡️ Analysis Complete
📋 Ready for Merge When
🤖 AI analysis complete - check individual code comments for specific feedback |
e83031d to
752c315
Compare
🤖 AI Review & Build Compliance ✅Status: AI analysis complete • Build rules validated • Ready for review 📊 View detailed analysis results🛡️ Analysis Complete
📋 Ready for Merge When
🤖 AI analysis complete - check individual code comments for specific feedback |
Critical fixes: - Filter Langfuse trace metadata to safe keys only (ai.provider, ai.model, ai.temperature, ai.max_tokens) matching Braintrust PII prevention - Add error.stack to RedactionProcessor sensitive keys to prevent stack trace exposure to third-party exporters (Datadog, Laminar, PostHog) Important fixes: - Trim latencyValues array in MetricsAggregator in sync with spans to prevent unbounded memory growth in long-running services - Remove duplicate getMetricsAggregator().recordSpan() calls from baseProvider (neurolink.ts event listeners are the authoritative source) - Minimize synthetic model response in Gemini 3.1 global endpoint workaround from verbose text to "OK" - Fix BatchProcessor.flush() to retain spans on delivery failure instead of clearing batch before confirming onBatchReady success - Document Zod schema test skip logic for vertex default model
752c315 to
5c70dc9
Compare
🤖 AI Review & Build Compliance ✅Status: AI analysis complete • Build rules validated • Ready for review 📊 View detailed analysis results🛡️ Analysis Complete
📋 Ready for Merge When
🤖 AI analysis complete - check individual code comments for specific feedback |
|
🎉 This PR is included in version 9.25.1 🎉 The release is available on: Your semantic-release bot 📦🚀 |
Summary
Addresses 7 code review findings from the self-review of PR #860 (observability instrumentation).
Critical Fixes (Security)
createTrace()metadata to safe keys only (ai.provider,ai.model,ai.temperature,ai.max_tokens), matching the Braintrust exporter approach. Previously sent fullspan.attributesincluding user prompts and LLM responses."stack"and"error.stack"toRedactionProcessorsensitive keys. Stack traces contain file paths, user directory names, and internal service details that should not be exported to third-party observability backends.Important Fixes (Correctness)
MetricsAggregator.latencyValueswas never trimmed, causing memory growth in long-running services. Now trimmed in sync with the span array.getMetricsAggregator().recordSpan()calls frombaseProvider.ts. Theneurolink.tsevent listeners are the authoritative recording point; having both inflated cost metrics by ~2x."Understood. I will follow these instructions."to"OK"to reduce token overhead and model confusion.flush()now retains spans on delivery failure instead of clearing the batch before confirmingonBatchReadysuccess.vertexwithout an explicit model is treated as Gemini (default model).Test plan
pnpm run build— cleanpnpm test— 2672/2672 passednpx eslint src/ test/— 0 errorsSummary by CodeRabbit
Bug Fixes
Chores