@@ -6,6 +6,7 @@ import { streamSSE } from "hono/streaming"
66import { awaitApproval } from "~/lib/approval"
77import { checkRateLimit } from "~/lib/rate-limit"
88import { state } from "~/lib/state"
9+ import { setupPingInterval } from "~/lib/utils"
910import {
1011 createResponsesStreamState ,
1112 translateResponsesStreamEvent ,
@@ -84,33 +85,39 @@ const handleWithChatCompletions = async (
8485
8586 consola . debug ( "Streaming response from Copilot" )
8687 return streamSSE ( c , async ( stream ) => {
88+ const pingInterval = setupPingInterval ( stream )
89+
8790 const streamState : AnthropicStreamState = {
8891 messageStartSent : false ,
8992 contentBlockIndex : 0 ,
9093 contentBlockOpen : false ,
9194 toolCalls : { } ,
9295 }
9396
94- for await ( const rawEvent of response ) {
95- consola . debug ( "Copilot raw stream event:" , JSON . stringify ( rawEvent ) )
96- if ( rawEvent . data === "[DONE]" ) {
97- break
98- }
97+ try {
98+ for await ( const rawEvent of response ) {
99+ consola . debug ( "Copilot raw stream event:" , JSON . stringify ( rawEvent ) )
100+ if ( rawEvent . data === "[DONE]" ) {
101+ break
102+ }
99103
100- if ( ! rawEvent . data ) {
101- continue
102- }
104+ if ( ! rawEvent . data ) {
105+ continue
106+ }
103107
104- const chunk = JSON . parse ( rawEvent . data ) as ChatCompletionChunk
105- const events = translateChunkToAnthropicEvents ( chunk , streamState )
108+ const chunk = JSON . parse ( rawEvent . data ) as ChatCompletionChunk
109+ const events = translateChunkToAnthropicEvents ( chunk , streamState )
106110
107- for ( const event of events ) {
108- consola . debug ( "Translated Anthropic event:" , JSON . stringify ( event ) )
109- await stream . writeSSE ( {
110- event : event . type ,
111- data : JSON . stringify ( event ) ,
112- } )
111+ for ( const event of events ) {
112+ consola . debug ( "Translated Anthropic event:" , JSON . stringify ( event ) )
113+ await stream . writeSSE ( {
114+ event : event . type ,
115+ data : JSON . stringify ( event ) ,
116+ } )
117+ }
113118 }
119+ } finally {
120+ clearInterval ( pingInterval )
114121 }
115122 } )
116123}
@@ -135,45 +142,51 @@ const handleWithResponsesApi = async (
135142 if ( responsesPayload . stream && isAsyncIterable ( response ) ) {
136143 consola . debug ( "Streaming response from Copilot (Responses API)" )
137144 return streamSSE ( c , async ( stream ) => {
138- const streamState = createResponsesStreamState ( )
145+ const pingInterval = setupPingInterval ( stream )
139146
140- for await ( const chunk of response ) {
141- const eventName = chunk . event
142- if ( eventName === "ping" ) {
143- await stream . writeSSE ( { event : "ping" , data : "" } )
144- continue
145- }
147+ const streamState = createResponsesStreamState ( )
146148
147- const data = chunk . data
148- if ( ! data ) {
149- continue
149+ try {
150+ for await ( const chunk of response ) {
151+ const eventName = chunk . event
152+ if ( eventName === "ping" ) {
153+ await stream . writeSSE ( { event : "ping" , data : "" } )
154+ continue
155+ }
156+
157+ const data = chunk . data
158+ if ( ! data ) {
159+ continue
160+ }
161+
162+ consola . debug ( "Responses raw stream event:" , data )
163+
164+ const events = translateResponsesStreamEvent (
165+ JSON . parse ( data ) as ResponseStreamEvent ,
166+ streamState ,
167+ )
168+ for ( const event of events ) {
169+ const eventData = JSON . stringify ( event )
170+ consola . debug ( "Translated Anthropic event:" , eventData )
171+ await stream . writeSSE ( {
172+ event : event . type ,
173+ data : eventData ,
174+ } )
175+ }
150176 }
151177
152- consola . debug ( "Responses raw stream event:" , data )
153-
154- const events = translateResponsesStreamEvent (
155- JSON . parse ( data ) as ResponseStreamEvent ,
156- streamState ,
157- )
158- for ( const event of events ) {
159- const eventData = JSON . stringify ( event )
160- consola . debug ( "Translated Anthropic event:" , eventData )
178+ if ( ! streamState . messageCompleted ) {
179+ consola . warn (
180+ "Responses stream ended without completion; sending fallback message_stop" ,
181+ )
182+ const fallback = { type : "message_stop" as const }
161183 await stream . writeSSE ( {
162- event : event . type ,
163- data : eventData ,
184+ event : fallback . type ,
185+ data : JSON . stringify ( fallback ) ,
164186 } )
165187 }
166- }
167-
168- if ( ! streamState . messageCompleted ) {
169- consola . warn (
170- "Responses stream ended without completion; sending fallback message_stop" ,
171- )
172- const fallback = { type : "message_stop" as const }
173- await stream . writeSSE ( {
174- event : fallback . type ,
175- data : JSON . stringify ( fallback ) ,
176- } )
188+ } finally {
189+ clearInterval ( pingInterval )
177190 }
178191 } )
179192 }
0 commit comments