1- import { AIChatMessage , AIChatCompletionDelta , AIChatProtocolClient } from '@microsoft/ai-chat-protocol ' ;
1+ import { type AIChatMessage , type AIChatCompletionDelta } from '../models.js ' ;
22
33export const apiBaseUrl : string = import . meta. env . VITE_API_URL || '' ;
44
@@ -9,20 +9,69 @@ export type ChatRequestOptions = {
99 apiUrl : string ;
1010} ;
1111
12- export async function * getCompletion ( options : ChatRequestOptions ) {
12+ export async function getCompletion ( options : ChatRequestOptions ) {
1313 const apiUrl = options . apiUrl || apiBaseUrl ;
14- const client = new AIChatProtocolClient ( `${ apiUrl } /api/chats` ) ;
15- const result = await client . getStreamedCompletion ( options . messages , { context : options . context } ) ;
14+ const response = await fetch ( `${ apiUrl } /api/chats/stream` , {
15+ method : 'POST' ,
16+ headers : { 'Content-Type' : 'application/json' } ,
17+ body : JSON . stringify ( {
18+ messages : options . messages ,
19+ context : options . context || { } ,
20+ } ) ,
21+ } ) ;
1622
17- for await ( const response of result ) {
18- if ( ! response . delta ) {
19- continue ;
20- }
23+ if ( response . status > 299 || ! response . ok ) {
24+ let json : JSON | undefined ;
25+ try {
26+ json = await response . json ( ) ;
27+ } catch { }
2128
22- yield new Promise < AIChatCompletionDelta > ( ( resolve ) => {
29+ const error = json ?. [ 'error' ] ?? response . statusText ;
30+ throw new Error ( error ) ;
31+ }
32+
33+ return getChunksFromResponse < AIChatCompletionDelta > ( response , options . chunkIntervalMs ) ;
34+ }
35+
36+ class NdJsonParserStream extends TransformStream < string , JSON > {
37+ private buffer = '' ;
38+ constructor ( ) {
39+ let controller : TransformStreamDefaultController < JSON > ;
40+ super ( {
41+ start ( _controller ) {
42+ controller = _controller ;
43+ } ,
44+ transform : ( chunk ) => {
45+ const jsonChunks = chunk . split ( '\n' ) . filter ( Boolean ) ;
46+ for ( const jsonChunk of jsonChunks ) {
47+ try {
48+ this . buffer += jsonChunk ;
49+ controller . enqueue ( JSON . parse ( this . buffer ) ) ;
50+ this . buffer = '' ;
51+ } catch {
52+ // Invalid JSON, wait for next chunk
53+ }
54+ }
55+ } ,
56+ } ) ;
57+ }
58+ }
59+
60+ export async function * getChunksFromResponse < T > ( response : Response , intervalMs : number ) : AsyncGenerator < T , void > {
61+ const reader = response . body ?. pipeThrough ( new TextDecoderStream ( ) ) . pipeThrough ( new NdJsonParserStream ( ) ) . getReader ( ) ;
62+ if ( ! reader ) {
63+ throw new Error ( 'No response body or body is not readable' ) ;
64+ }
65+
66+ let value : JSON | undefined ;
67+ let done : boolean ;
68+ // eslint-disable-next-line no-await-in-loop
69+ while ( ( ( { value, done } = await reader . read ( ) ) , ! done ) ) {
70+ const chunk = value as T ;
71+ yield new Promise < T > ( ( resolve ) => {
2372 setTimeout ( ( ) => {
24- resolve ( response ) ;
25- } , options . chunkIntervalMs ) ;
73+ resolve ( chunk ) ;
74+ } , intervalMs ) ;
2675 } ) ;
2776 }
2877}
0 commit comments