Add req id

This commit is contained in:
Siddharth Ganesan
2026-04-09 15:23:41 -07:00
parent 649ee9c869
commit 3ef87e55a3
6 changed files with 55 additions and 2 deletions

View File

@@ -10,6 +10,7 @@ import {
ModalContent,
ModalFooter,
ModalHeader,
TagIcon,
Textarea,
ThumbsDown,
ThumbsUp,
@@ -46,13 +47,16 @@ interface MessageActionsProps {
content: string
chatId?: string
userQuery?: string
requestId?: string
}
export function MessageActions({ content, chatId, userQuery }: MessageActionsProps) {
export function MessageActions({ content, chatId, userQuery, requestId }: MessageActionsProps) {
const [copied, setCopied] = useState(false)
const [copiedRequestId, setCopiedRequestId] = useState(false)
const [pendingFeedback, setPendingFeedback] = useState<'up' | 'down' | null>(null)
const [feedbackText, setFeedbackText] = useState('')
const resetTimeoutRef = useRef<number | null>(null)
const requestIdTimeoutRef = useRef<number | null>(null)
const submitFeedback = useSubmitCopilotFeedback()
useEffect(() => {
@@ -60,6 +64,9 @@ export function MessageActions({ content, chatId, userQuery }: MessageActionsPro
if (resetTimeoutRef.current !== null) {
window.clearTimeout(resetTimeoutRef.current)
}
if (requestIdTimeoutRef.current !== null) {
window.clearTimeout(requestIdTimeoutRef.current)
}
}
}, [])
@@ -79,6 +86,20 @@ export function MessageActions({ content, chatId, userQuery }: MessageActionsPro
}
}, [content])
const copyRequestId = useCallback(async () => {
if (!requestId) return
try {
await navigator.clipboard.writeText(requestId)
setCopiedRequestId(true)
if (requestIdTimeoutRef.current !== null) {
window.clearTimeout(requestIdTimeoutRef.current)
}
requestIdTimeoutRef.current = window.setTimeout(() => setCopiedRequestId(false), 1500)
} catch {
/* clipboard unavailable */
}
}, [requestId])
const handleFeedbackClick = useCallback(
(type: 'up' | 'down') => {
if (chatId && userQuery) {
@@ -144,6 +165,21 @@ export function MessageActions({ content, chatId, userQuery }: MessageActionsPro
>
<ThumbsDown className={ICON_CLASS} />
</button>
{requestId && (
<button
type='button'
aria-label='Copy request ID'
onClick={copyRequestId}
className={BUTTON_CLASS}
title={copiedRequestId ? 'Copied!' : 'Copy request ID'}
>
{copiedRequestId ? (
<Check className={ICON_CLASS} />
) : (
<TagIcon className={ICON_CLASS} />
)}
</button>
)}
</div>
<Modal open={pendingFeedback !== null} onOpenChange={handleModalClose}>

View File

@@ -182,6 +182,7 @@ export function MothershipChat({
content={msg.content}
chatId={chatId}
userQuery={precedingUserMsg?.content}
requestId={msg.requestId}
/>
</div>
)}

View File

@@ -158,6 +158,10 @@ export interface StreamLoopOptions extends OrchestratorOptions {
* Return true to skip the default handler for this event.
*/
onBeforeDispatch?: (event: StreamEvent, context: StreamingContext) => boolean | undefined
/**
* Called when the Go backend's trace ID (go_trace_id) is first received via SSE.
*/
onGoTraceId?: (goTraceId: string) => void
}
/**
@@ -234,8 +238,12 @@ export async function runStreamLoop(
const streamEvent = eventToStreamEvent(raw)
if (raw.trace?.requestId) {
const prev = context.requestId
context.requestId = raw.trace.requestId
context.trace.setGoTraceId(raw.trace.requestId)
if (raw.trace.requestId !== prev) {
options.onGoTraceId?.(raw.trace.requestId)
}
}
if (shouldSkipToolCallEvent(streamEvent) || shouldSkipToolResultEvent(streamEvent)) {

View File

@@ -43,6 +43,7 @@ export interface CopilotLifecycleOptions extends OrchestratorOptions {
goRoute?: string
trace?: TraceCollector
simRequestId?: string
onGoTraceId?: (goTraceId: string) => void
}
export async function runCopilotLifecycle(

View File

@@ -143,6 +143,9 @@ export function createSSEStream(params: StreamingOrchestrationParams): ReadableS
trace: collector,
simRequestId: requestId,
abortSignal: abortController.signal,
onGoTraceId: (goTraceId) => {
publisher.updateRequestId(goTraceId)
},
onEvent: async (event) => {
await publisher.publish(event)
},

View File

@@ -22,7 +22,7 @@ export interface StreamWriterOptions {
export class StreamWriter {
private readonly streamId: string
private readonly chatId: string | undefined
private readonly requestId: string
private requestId: string
private readonly keepaliveMs: number
private readonly flushIntervalMs: number
private readonly flushMaxBatch: number
@@ -55,6 +55,10 @@ export class StreamWriter {
return this._sawComplete
}
updateRequestId(id: string): void {
this.requestId = id
}
attach(controller: ReadableStreamDefaultController): void {
this.controller = controller
}