-
Notifications
You must be signed in to change notification settings - Fork 1.2k
feat(debug): add content-free adapter and bridge stream diagnostics #3983
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: dev
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1 @@ | ||
| /Volumes/PortableSSD/Projects/opencodex/node_modules | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🎯 Functional Correctness | 🟠 Major | ⚡ Quick win 🔎 Supported by static analysis🏁 Script executed: set -eu
printf '%s\n' '--- git entry ---'
git ls-files -s -- node_modules
printf '%s\n' '--- filesystem entry ---'
ls -ld -- node_modules
readlink -- node_modules || true
printf '%s\n' '--- lockfiles ---'
find . -maxdepth 2 -type f \( -name 'package-lock.json' -o -name 'npm-shrinkwrap.json' -o -name 'yarn.lock' -o -name 'pnpm-lock.yaml' -o -name 'bun.lockb' -o -name 'bun.lock' \) -printRepository: lidge-jun/opencodex Length of output: 493 🏁 Script executed: git ls-files -s -- node_modules
ls -ld -- node_modules
readlink -- node_modules || true
find . -maxdepth 2 -type f \( -name 'package-lock.json' -o -name 'npm-shrinkwrap.json' -o -name 'yarn.lock' -o -name 'pnpm-lock.yaml' -o -name 'bun.lockb' -o -name 'bun.lock' \) -printRepository: lidge-jun/opencodex Length of output: 432 Remove the machine-specific Git tracks 🤖 Prompt for AI Agents |
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,85 @@ | ||
| import type { AdapterEvent } from "../types"; | ||
| import { debugFingerprint, debugStreamDiagnostic, type DebugStreamDiagnosticContext } from "../lib/debug"; | ||
|
|
||
| export interface BridgeDiagnosticSequence { value: number } | ||
|
|
||
| export interface BridgeDiagnosticContext extends DebugStreamDiagnosticContext { | ||
| sequence?: BridgeDiagnosticSequence; | ||
| } | ||
|
|
||
| export function adapterEventDiagnosticDetails(event: AdapterEvent): Record<string, unknown> { | ||
| switch (event.type) { | ||
| case "text_delta": | ||
| return { byteLength: Buffer.byteLength(event.text), fingerprint: debugFingerprint(event.text) }; | ||
| case "thinking_delta": | ||
| return { byteLength: Buffer.byteLength(event.thinking), fingerprint: debugFingerprint(event.thinking) }; | ||
| case "reasoning_raw_delta": | ||
| return { byteLength: Buffer.byteLength(event.text), fingerprint: debugFingerprint(event.text) }; | ||
| case "thinking_signature": | ||
| case "redacted_thinking": | ||
| case "kiro_redacted_reasoning": { | ||
| const content = event.type === "thinking_signature" ? event.signature : event.data; | ||
| return { byteLength: Buffer.byteLength(content), fingerprint: debugFingerprint(content) }; | ||
| } | ||
| case "tool_call_delta": | ||
| return { byteLength: Buffer.byteLength(event.arguments), fingerprint: debugFingerprint(event.arguments) }; | ||
| case "tool_call_start": | ||
| return { | ||
| idByteLength: Buffer.byteLength(event.id), | ||
| idFingerprint: debugFingerprint(event.id), | ||
| nameByteLength: Buffer.byteLength(event.name), | ||
| nameFingerprint: debugFingerprint(event.name), | ||
| }; | ||
| case "web_search_call_begin": | ||
| return { idByteLength: Buffer.byteLength(event.id), idFingerprint: debugFingerprint(event.id) }; | ||
| case "web_search_call_end": { | ||
| const queries = JSON.stringify(event.queries); | ||
| return { | ||
| idByteLength: Buffer.byteLength(event.id), | ||
| idFingerprint: debugFingerprint(event.id), | ||
| byteLength: Buffer.byteLength(queries), | ||
| fingerprint: debugFingerprint(queries), | ||
| status: event.status, | ||
| }; | ||
| } | ||
| case "error": | ||
| return { | ||
| byteLength: Buffer.byteLength(event.message), | ||
| fingerprint: debugFingerprint(event.message), | ||
| ...(event.status !== undefined ? { status: event.status } : {}), | ||
| ...(event.code !== undefined | ||
| ? { codeByteLength: Buffer.byteLength(event.code), codeFingerprint: debugFingerprint(event.code) } | ||
| : {}), | ||
| ...(event.retryable !== undefined ? { retryable: event.retryable } : {}), | ||
| }; | ||
| case "incomplete": | ||
| return { | ||
| ...(event.message !== undefined ? { byteLength: Buffer.byteLength(event.message), fingerprint: debugFingerprint(event.message) } : {}), | ||
| reasonByteLength: Buffer.byteLength(event.reason), | ||
| reasonFingerprint: debugFingerprint(event.reason), | ||
| ...(event.retryable !== undefined ? { retryable: event.retryable } : {}), | ||
| }; | ||
| case "done": | ||
| return { | ||
| ...(event.stopReason !== undefined | ||
| ? { stopReasonByteLength: Buffer.byteLength(event.stopReason), stopReasonFingerprint: debugFingerprint(event.stopReason) } | ||
| : {}), | ||
| ...(event.endTurn !== undefined ? { endTurn: event.endTurn } : {}), | ||
| }; | ||
| default: | ||
| return {}; | ||
| } | ||
| } | ||
|
|
||
| /** Emit one adapter-stage diagnostic while preserving one sequence across sidecar iterations. */ | ||
| export function diagnoseAdapterEvent(context: BridgeDiagnosticContext, event: AdapterEvent): void { | ||
| const sequence = context.sequence ??= { value: 0 }; | ||
| debugStreamDiagnostic( | ||
| context, | ||
| "adapter", | ||
| ++sequence.value, | ||
| event.type, | ||
| adapterEventDiagnosticDetails(event), | ||
| ); | ||
| } | ||
|
|
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -18,7 +18,7 @@ import type { AdapterEvent, OcxMessage, OcxParsedRequest, OcxProviderContinuatio | |
| import { namespacedToolName, toolChoiceToolPredicate } from "../types"; | ||
| import { cloneProviderOpaqueToolCallMetadata } from "../responses/provider-opaque-metadata"; | ||
| import type { AttemptRecoveryKind } from "../usage/log"; | ||
| import { bridgeToResponsesSSE } from "../bridge"; | ||
| import { bridgeToResponsesSSE, diagnoseAdapterEvent, type BridgeDiagnosticContext } from "../bridge"; | ||
| import { clearableDeadline, idleDeadline } from "../lib/abort"; | ||
| import { readBoundedResponseBody } from "../lib/bounded-body"; | ||
| import { applyUpstreamRecoveryInit, fetchWithResetRetry, prepareSameTarget429Wait } from "../lib/upstream-retry"; | ||
|
|
@@ -332,6 +332,8 @@ export interface ImageBridgeDeps { | |
| onCompletedResponse?: (response: Record<string, unknown>, providerState?: OcxProviderContinuationState) => void; | ||
| /** WebSocket Responses path only — leave response id empty for protocol compatibility. */ | ||
| forceEmptyResponseId?: boolean; | ||
| /** Internal, opt-in structural stream diagnostics shared with the final bridge. */ | ||
| diagnostic?: BridgeDiagnosticContext; | ||
| } | ||
|
|
||
| /** | ||
|
|
@@ -725,6 +727,10 @@ export async function runWithImageBridge(deps: ImageBridgeDeps): Promise<Respons | |
| inactivityTimeoutMs: stallTimeoutMs, | ||
| translatorBudget, | ||
| })) { | ||
| if (deps.diagnostic) { | ||
| deps.diagnostic.adapterName = prepared.responseAdapter.name; | ||
| diagnoseAdapterEvent(deps.diagnostic, event); | ||
| } | ||
|
Comment on lines
+730
to
+733
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🎯 Functional Correctness | 🟠 Major | ⚡ Quick win 🔎 Supported by static analysis🏁 Script executed: sed -n '680,780p' src/images/loop.ts
sed -n '1000,1070p' src/images/loop.ts
rg -n -C 4 'collectedEvents|consumeIterationEvents|iterationBudget|diagnoseAdapterEvent' src/images/loop.tsRepository: lidge-jun/opencodex Length of output: 12812 🏁 Script executed: sed -n '440,560p' src/images/loop.ts
sed -n '780,875p' src/images/loop.ts
rg -n -C 8 'diagnoseAdapterEvent|bridgeToResponsesSSE|diagnostic|adapterName' src/bridge.ts src/images/loop.tsRepository: lidge-jun/opencodex Length of output: 22274 🏁 Script executed: sed -n '1,260p' src/bridge/diagnostic.ts
rg -n -C 10 'diagnostic|diagnoseAdapterEvent|adapterEventDiagnosticDetails' src/bridgeRepository: lidge-jun/opencodex Length of output: 13053 Diagnose collected
At the consumption boundary, set 🤖 Prompt for AI Agents |
||
| if (event.type === "heartbeat") yield event; | ||
| else { | ||
| iterationBudget.retain(event); | ||
|
|
@@ -1045,6 +1051,7 @@ export async function runWithImageBridge(deps: ImageBridgeDeps): Promise<Respons | |
| onUsage: (usage: OcxUsage | undefined) => deps.onUsage?.(usage), | ||
| } : {}), | ||
| ...(deps.onCompletedResponse ? { onCompletedResponse: deps.onCompletedResponse } : {}), | ||
| ...(deps.diagnostic ? { diagnostic: deps.diagnostic } : {}), | ||
| }, | ||
| ); | ||
| return new Response(sse, { headers: SSE_HEADERS }); | ||
|
|
||
Uh oh!
There was an error while loading. Please reload this page.