@@ -4,90 +4,46 @@ import { streamSSE } from "hono/streaming"
44import { FetchError } from "ofetch"
55
66import type { ChatCompletionsPayload } from "~/services/copilot/chat-completions/types"
7- import type { ChatCompletionsChunk } from "~/services/copilot/chat-completions/types.streaming"
87
98import { chatCompletions } from "~/services/copilot/chat-completions/service"
109
10+ import { createContentChunk , createFinalChunk , segmentResponse } from "./utils"
11+
1112export const chatCompletionsRoutes = new Hono ( )
1213
1314chatCompletionsRoutes . post ( "/chat/completions" , async ( c ) => {
14- const payload = await c . req . json < ChatCompletionsPayload > ( )
15-
16- payload . stream = false
17-
18- consola . info ( `Received request: ${ JSON . stringify ( payload ) . slice ( 0 , 500 ) } ` )
19-
20- const response = await chatCompletions ( payload ) . catch ( ( error : unknown ) => {
15+ try {
16+ const payload = await c . req . json < ChatCompletionsPayload > ( )
17+ payload . stream = false
18+
19+ consola . info ( `Received request: ${ JSON . stringify ( payload ) . slice ( 0 , 500 ) } ` )
20+
21+ const response = await chatCompletions ( payload )
22+ consola . info ( `Response from Copilot: ${ JSON . stringify ( response ) } ` )
23+
24+ const segments = segmentResponse ( response . choices [ 0 ] . message . content )
25+ const chunks = segments . map ( ( segment ) =>
26+ createContentChunk ( segment , response , payload . model ) ,
27+ )
28+
29+ chunks . push ( createFinalChunk ( response , payload . model ) )
30+
31+ consola . info (
32+ `Streaming response, first chunk: ${ JSON . stringify ( chunks . at ( 0 ) ) } ` ,
33+ )
34+
35+ return streamSSE ( c , async ( stream ) => {
36+ for ( const chunk of chunks ) {
37+ await stream . writeSSE ( {
38+ data : JSON . stringify ( chunk . data ) ,
39+ } )
40+ await stream . sleep ( 1 ) // Simulated latency
41+ }
42+ } )
43+ } catch ( error ) {
2144 if ( error instanceof FetchError ) {
22- consola . error (
23- // eslint-disable-next-line @typescript-eslint/no-base-to-string, @typescript-eslint/restrict-template-expressions
24- `Request failed: ${ JSON . stringify ( payload ) } \n ${ error } \n ${ error . response ?. _data } ` ,
25- )
45+ consola . error ( `Request failed: ${ error . message } ` , error . response ?. _data )
2646 }
27-
2847 throw error
29- } )
30-
31- consola . info ( `Response from Copilot: ${ JSON . stringify ( response ) } ` )
32-
33- const segmenter = new Intl . Segmenter ( "en" , { granularity : "word" } )
34-
35- const segmentedMessages = segmenter . segment (
36- response . choices [ 0 ] . message . content ,
37- )
38-
39- const chunks : Array < ChatCompletionsChunk > = Array . from ( segmentedMessages ) . map (
40- ( segment ) => ( {
41- data : {
42- object : "chat.completion.chunk" ,
43- choices : [
44- {
45- delta : {
46- content : segment . segment ,
47- role : response . choices [ 0 ] . message . role ,
48- } ,
49- index : 0 ,
50- } ,
51- ] ,
52- created : response . created ,
53- id : response . id ,
54- model : payload . model ,
55- usage : null ,
56- system_fingerprint : "fp_44709d6fcb" ,
57- } ,
58- } ) ,
59- )
60-
61- chunks . push ( {
62- data : {
63- object : "chat.completion.chunk" ,
64- choices : [
65- {
66- delta : { } ,
67- finish_reason : response . choices [ 0 ] . finish_reason ,
68- index : 0 ,
69- } ,
70- ] ,
71- created : response . created ,
72- id : response . id ,
73- model : payload . model ,
74- usage : response . usage ,
75- system_fingerprint : "fp_44709d6fcb" ,
76- } ,
77- } )
78-
79- consola . info (
80- `Streaming response, first chunk: ${ JSON . stringify ( chunks . at ( 0 ) ) } ` ,
81- )
82-
83- return streamSSE ( c , async ( stream ) => {
84- for ( const chunk of chunks ) {
85- await stream . writeSSE ( {
86- data : JSON . stringify ( chunk . data ) ,
87- } )
88-
89- // Fake latency lol
90- await stream . sleep ( 1 )
91- }
92- } )
48+ }
9349} )
0 commit comments