1+ import { startKeepAlive , stopKeepAlive } from "./keepAlive" ;
12import { getPluginOptions , type LlmParameters } from "./optionsParams" ;
23
34export enum LlmRoles {
@@ -81,14 +82,15 @@ export async function sendContentToLlm(
8182 ...options . params ,
8283 } ;
8384
84- return callLlmApi ( options . model , requestBody , abortSignal , options . api_token ) ;
85+ return callLlmApi ( options . model , requestBody , abortSignal , options . api_token , options . timeout ) ;
8586}
8687
8788async function callLlmApi (
8889 url : string ,
8990 requestBody : LlmApiRequestBody ,
9091 signal : AbortSignal ,
9192 token ?: string ,
93+ timeout ?: number ,
9294) : Promise < LlmTextCompletionResponse | TgiErrorResponse > {
9395 const headers : { [ key : string ] : string } = {
9496 "Content-Type" : "application/json" ,
@@ -97,21 +99,51 @@ async function callLlmApi(
9799 headers . Authorization = `Bearer ${ token } ` ;
98100 }
99101
100- console . log ( `LLM-CONNECTION: Sending request to LLM: POST ${ url } with body:\n` , JSON . stringify ( requestBody ) ) ;
101- const response = await fetch ( url , {
102- signal : signal ,
103- method : "POST" ,
104- headers : headers ,
105- body : JSON . stringify ( requestBody ) ,
106- } ) ;
107- if ( ! response . ok ) {
108- const errorResponseBody = await response . text ( ) ;
109- throw Error ( `LLM-CONNECTION: Error response from ${ url } : ${ errorResponseBody } ` ) ;
102+ // Create a combined abort controller for both user cancellation and timeout
103+ const combinedAbortController = new AbortController ( ) ;
104+ let timeoutId : ReturnType < typeof setTimeout > | undefined ;
105+
106+ // Listen to the user's abort signal
107+ const abortHandler = ( ) => combinedAbortController . abort ( signal . reason ) ;
108+ signal . addEventListener ( "abort" , abortHandler ) ;
109+
110+ // Set up timeout if specified
111+ if ( timeout !== undefined && timeout > 0 ) {
112+ timeoutId = setTimeout ( ( ) => {
113+ combinedAbortController . abort (
114+ new DOMException (
115+ `LLM request timed out after ${ timeout / 1000 } seconds. You can increase or disable the timeout in the extension settings.` ,
116+ "TimeoutError" ,
117+ ) ,
118+ ) ;
119+ } , timeout ) ;
110120 }
111- const responseBody = ( await response . json ( ) ) as LlmTextCompletionResponse | TgiErrorResponse ;
112- console . log ( "LLM-CONNECTION: LLM responded with:" , response . status , responseBody ) ;
113121
114- return responseBody ;
122+ await startKeepAlive ( ) ;
123+ try {
124+ console . log ( `LLM-CONNECTION: Sending request to LLM: POST ${ url } with body:\n` , JSON . stringify ( requestBody ) ) ;
125+ const response = await fetch ( url , {
126+ signal : combinedAbortController . signal ,
127+ method : "POST" ,
128+ headers : headers ,
129+ body : JSON . stringify ( requestBody ) ,
130+ } ) ;
131+ if ( ! response . ok ) {
132+ const errorResponseBody = await response . text ( ) ;
133+ throw Error ( `LLM-CONNECTION: Error response from ${ url } : ${ errorResponseBody } ` ) ;
134+ }
135+ const responseBody = ( await response . json ( ) ) as LlmTextCompletionResponse | TgiErrorResponse ;
136+ console . log ( "LLM-CONNECTION: LLM responded with:" , response . status , responseBody ) ;
137+
138+ return responseBody ;
139+ } finally {
140+ await stopKeepAlive ( ) ;
141+ // Clean up
142+ signal . removeEventListener ( "abort" , abortHandler ) ;
143+ if ( timeoutId !== undefined ) {
144+ clearTimeout ( timeoutId ) ;
145+ }
146+ }
115147}
116148
117149export function isLlmTextCompletionResponse ( response : LlmTextCompletionResponse | TgiErrorResponse ) {
0 commit comments