@schmitech/chatbot-api 2.1.1 → 2.1.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +29 -15
- package/dist/api.cjs +2 -2
- package/dist/api.cjs.map +1 -1
- package/dist/api.d.ts +1 -0
- package/dist/api.mjs +60 -58
- package/dist/api.mjs.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -12,12 +12,12 @@ npm install @schmitech/chatbot-api
|
|
|
12
12
|
|
|
13
13
|
### Configuration
|
|
14
14
|
|
|
15
|
-
First,
|
|
15
|
+
First, initialize the API client with your server details:
|
|
16
16
|
|
|
17
17
|
```typescript
|
|
18
|
-
import {
|
|
18
|
+
import { ApiClient } from '@schmitech/chatbot-api';
|
|
19
19
|
|
|
20
|
-
|
|
20
|
+
const client = new ApiClient({
|
|
21
21
|
apiUrl: 'https://your-api-server.com',
|
|
22
22
|
apiKey: 'your-api-key',
|
|
23
23
|
sessionId: 'optional-session-id' // Optional, for conversation tracking
|
|
@@ -28,7 +28,9 @@ configureApi({
|
|
|
28
28
|
|
|
29
29
|
```typescript
|
|
30
30
|
async function chat() {
|
|
31
|
-
|
|
31
|
+
const stream = client.streamChat('Hello, how can I help?');
|
|
32
|
+
|
|
33
|
+
for await (const response of stream) {
|
|
32
34
|
console.log(response.text);
|
|
33
35
|
if (response.done) {
|
|
34
36
|
console.log('Chat complete!');
|
|
@@ -89,28 +91,40 @@ node test/test-npm-package.js "how many r are in a strawberry?" "http://localhos
|
|
|
89
91
|
Here's how to use the API in a React component:
|
|
90
92
|
|
|
91
93
|
```tsx
|
|
92
|
-
import React, { useState } from 'react';
|
|
93
|
-
import {
|
|
94
|
-
|
|
95
|
-
// Configure once at app startup
|
|
96
|
-
configureApi({
|
|
97
|
-
apiUrl: 'https://your-api-server.com',
|
|
98
|
-
apiKey: 'your-api-key',
|
|
99
|
-
sessionId: 'user_123_session_456' // Optional
|
|
100
|
-
});
|
|
94
|
+
import React, { useState, useMemo } from 'react';
|
|
95
|
+
import { ApiClient } from '@schmitech/chatbot-api';
|
|
101
96
|
|
|
102
97
|
function ChatComponent() {
|
|
103
98
|
const [messages, setMessages] = useState<Array<{ text: string; isUser: boolean }>>([]);
|
|
104
99
|
const [input, setInput] = useState('');
|
|
105
100
|
|
|
101
|
+
// Initialize client (memoize to avoid re-creation on every render)
|
|
102
|
+
const client = useMemo(() => new ApiClient({
|
|
103
|
+
apiUrl: 'https://your-api-server.com',
|
|
104
|
+
apiKey: 'your-api-key',
|
|
105
|
+
sessionId: 'user_123_session_456' // Optional
|
|
106
|
+
}), []);
|
|
107
|
+
|
|
106
108
|
const handleSubmit = async (e: React.FormEvent) => {
|
|
107
109
|
e.preventDefault();
|
|
110
|
+
if (!input.trim()) return;
|
|
111
|
+
|
|
108
112
|
setMessages(prev => [...prev, { text: input, isUser: true }]);
|
|
109
113
|
|
|
110
114
|
let responseText = '';
|
|
111
|
-
|
|
115
|
+
const stream = client.streamChat(input);
|
|
116
|
+
|
|
117
|
+
for await (const response of stream) {
|
|
112
118
|
responseText += response.text;
|
|
113
|
-
setMessages(prev =>
|
|
119
|
+
setMessages(prev => {
|
|
120
|
+
const newMessages = [...prev];
|
|
121
|
+
if (newMessages.length > 0 && !newMessages[newMessages.length - 1].isUser && responseText.startsWith(newMessages[newMessages.length - 1].text)) {
|
|
122
|
+
// Update existing bot message if it's the last one
|
|
123
|
+
newMessages[newMessages.length - 1] = { text: responseText, isUser: false };
|
|
124
|
+
return newMessages;
|
|
125
|
+
}
|
|
126
|
+
return [...prev, { text: responseText, isUser: false }];
|
|
127
|
+
});
|
|
114
128
|
if (response.done) break;
|
|
115
129
|
}
|
|
116
130
|
setInput('');
|
package/dist/api.cjs
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
"use strict";var
|
|
2
|
-
`,E))!==-1;){const m=f.slice(E,T).trim();if(E=T+1,m&&m.startsWith("data: ")){const d=m.slice(6).trim();if(!d||d==="[DONE]"){yield{text:"",done:!0};return}try{const
|
|
1
|
+
"use strict";var N=Object.create;var F=Object.defineProperty;var J=Object.getOwnPropertyDescriptor;var M=Object.getOwnPropertyNames;var H=Object.getPrototypeOf,X=Object.prototype.hasOwnProperty;var R=(o,t,e)=>t in o?F(o,t,{enumerable:!0,configurable:!0,writable:!0,value:e}):o[t]=e;var L=(o,t,e,s)=>{if(t&&typeof t=="object"||typeof t=="function")for(let r of M(t))!X.call(o,r)&&r!==e&&F(o,r,{get:()=>t[r],enumerable:!(s=J(t,r))||s.enumerable});return o};var S=(o,t,e)=>(e=o!=null?N(H(o)):{},L(t||!o||!o.__esModule?F(e,"default",{value:o,enumerable:!0}):e,o));var A=(o,t,e)=>R(o,typeof t!="symbol"?t+"":t,e);Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});let I=null,$=null;typeof window>"u"&&Promise.all([import("http").catch(()=>null),import("https").catch(()=>null)]).then(([o,t])=>{var e,s;(e=o==null?void 0:o.default)!=null&&e.Agent?I=new o.default.Agent({keepAlive:!0}):o!=null&&o.Agent&&(I=new o.Agent({keepAlive:!0})),(s=t==null?void 0:t.default)!=null&&s.Agent?$=new t.default.Agent({keepAlive:!0}):t!=null&&t.Agent&&($=new t.Agent({keepAlive:!0}))}).catch(o=>{console.warn("Failed to initialize HTTP agents:",o.message)});class U{constructor(t){A(this,"apiUrl");A(this,"apiKey");A(this,"sessionId");if(!t.apiUrl||typeof t.apiUrl!="string")throw new Error("API URL must be a valid string");if(t.apiKey!==void 0&&t.apiKey!==null&&typeof t.apiKey!="string")throw new Error("API key must be a valid string or null");if(t.sessionId!==void 0&&t.sessionId!==null&&typeof t.sessionId!="string")throw new Error("Session ID must be a valid string or null");this.apiUrl=t.apiUrl,this.apiKey=t.apiKey??null,this.sessionId=t.sessionId??null}setSessionId(t){if(t!==null&&typeof t!="string")throw new Error("Session ID must be a valid string or null");this.sessionId=t}getSessionId(){return this.sessionId}async validateApiKey(){var t;if(!this.apiKey)throw new Error("API key is required for validation");try{const e=await fetch(`${this.apiUrl}/admin/api-keys/${this.apiKey}/status`,{...this.getFetchOptions({method:"GET"})}).catch(r=>{throw r.name==="TypeError"&&r.message.includes("Failed to fetch")?new Error("Could not connect to the server. Please check if the server is running."):r});if(!e.ok){let r="";try{r=await e.text()}catch{r=`HTTP ${e.status}`}let i,n;try{const l=JSON.parse(r);i=l.detail||l.message||r}catch{i=r||`HTTP ${e.status}`}switch(e.status){case 401:n="API key is invalid or expired";break;case 403:n="Access denied: API key does not have required permissions";break;case 404:n="API key not found";break;case 503:n="API key management is not available in inference-only mode";break;default:n=`Failed to validate API key: ${i}`;break}throw new Error(n)}const s=await e.json();if(!s.exists){const r="API key does not exist";throw new Error(r)}if(!s.active){const r="API key is inactive";throw new Error(r)}return s}catch(e){let s;throw e instanceof Error&&e.message?e.message.includes("API key")||e.message.includes("Access denied")||e.message.includes("invalid")||e.message.includes("expired")||e.message.includes("inactive")||e.message.includes("not found")||e.message.includes("Could not connect")?s=e.message:s=`API key validation failed: ${e.message}`:e.name==="TypeError"&&((t=e.message)!=null&&t.includes("Failed to fetch"))?s="Could not connect to the server. Please check if the server is running.":s="API key validation failed. Please check your API key and try again.",console.warn(`[ApiClient] ${s}`),new Error(s)}}async getAdapterInfo(){var t;if(!this.apiKey)throw new Error("API key is required to get adapter information");try{const e=await fetch(`${this.apiUrl}/admin/api-keys/info`,{...this.getFetchOptions({method:"GET"})}).catch(r=>{throw r.name==="TypeError"&&r.message.includes("Failed to fetch")?new Error("Could not connect to the server. Please check if the server is running."):r});if(!e.ok){let r="";try{r=await e.text()}catch{r=`HTTP ${e.status}`}let i,n;try{const l=JSON.parse(r);i=l.detail||l.message||r}catch{i=r||`HTTP ${e.status}`}switch(e.status){case 401:n="API key is invalid, disabled, or has no associated adapter";break;case 404:n="Adapter configuration not found";break;case 503:n="Service is not available";break;default:n=`Failed to get adapter info: ${i}`;break}throw new Error(n)}return await e.json()}catch(e){let s;throw e instanceof Error&&e.message?e.message.includes("API key")||e.message.includes("Adapter")||e.message.includes("invalid")||e.message.includes("disabled")||e.message.includes("not found")||e.message.includes("Could not connect")?s=e.message:s=`Failed to get adapter info: ${e.message}`:e.name==="TypeError"&&((t=e.message)!=null&&t.includes("Failed to fetch"))?s="Could not connect to the server. Please check if the server is running.":s="Failed to get adapter information. Please try again.",console.warn(`[ApiClient] ${s}`),new Error(s)}}getFetchOptions(t={}){const e={};if(typeof window>"u"){const i=this.apiUrl.startsWith("https:")?$:I;i&&(e.agent=i)}else e.headers={Connection:"keep-alive"};const s={"X-Request-ID":Date.now().toString(36)+Math.random().toString(36).substring(2)};if(e.headers&&Object.assign(s,e.headers),t.headers){const r=t.headers;for(const[i,n]of Object.entries(r))(i.toLowerCase()!=="x-api-key"||!this.apiKey)&&(s[i]=n)}return this.apiKey&&(s["X-API-Key"]=this.apiKey),this.sessionId&&(s["X-Session-ID"]=this.sessionId),{...t,...e,headers:s}}createChatRequest(t,e=!0,s,r,i,n,l,h,u,p,v){const c={messages:[{role:"user",content:t}],stream:e};return s&&s.length>0&&(c.file_ids=s),r&&(c.thread_id=r),i&&(c.audio_input=i),n&&(c.audio_format=n),l&&(c.language=l),h!==void 0&&(c.return_audio=h),u&&(c.tts_voice=u),p&&(c.source_language=p),v&&(c.target_language=v),c}async*streamChat(t,e=!0,s,r,i,n,l,h,u,p,v){var c,x,b;try{const y=new AbortController,q=setTimeout(()=>y.abort(),6e4),g=await fetch(`${this.apiUrl}/v1/chat`,{...this.getFetchOptions({method:"POST",headers:{"Content-Type":"application/json",Accept:e?"text/event-stream":"application/json"},body:JSON.stringify(this.createChatRequest(t,e,s,r,i,n,l,h,u,p,v))}),signal:y.signal});if(clearTimeout(q),!g.ok){const w=await g.text();throw new Error(`Network response was not ok: ${g.status} ${w}`)}if(!e){const w=await g.json();w.response&&(yield{text:w.response,done:!0,audio:w.audio,audioFormat:w.audio_format});return}const P=(c=g.body)==null?void 0:c.getReader();if(!P)throw new Error("No reader available");const _=new TextDecoder;let f="",k=!1;try{for(;;){const{done:w,value:D}=await P.read();if(w)break;const j=_.decode(D,{stream:!0});f+=j;let E=0,T;for(;(T=f.indexOf(`
|
|
2
|
+
`,E))!==-1;){const m=f.slice(E,T).trim();if(E=T+1,m&&m.startsWith("data: ")){const d=m.slice(6).trim();if(!d||d==="[DONE]"){yield{text:"",done:!0};return}try{const a=JSON.parse(d);if(a.error){const K=`Server error: ${((x=a.error)==null?void 0:x.message)||a.error||"Unknown server error"}`;throw console.warn(`[ApiClient] ${K}`),new Error(K)}if(a.done===!0){k=!0,yield{text:"",done:!0,audio:a.audio,audioFormat:a.audio_format||a.audioFormat,threading:a.threading};return}const O=a.response||"";a.audio_chunk!==void 0&&(yield{text:"",done:!1,audio_chunk:a.audio_chunk,audioFormat:a.audioFormat||a.audio_format||"opus",chunk_index:a.chunk_index??0}),(O||a.audio)&&(k=!0,yield{text:O,done:a.done||!1,audio:a.audio,audioFormat:a.audio_format||a.audioFormat,threading:a.threading})}catch(a){if((b=a==null?void 0:a.message)!=null&&b.startsWith("Server error:"))throw a;console.warn("[ApiClient] Unable to parse server response. This may be a temporary issue."),console.warn("[ApiClient] Parse error details:",a==null?void 0:a.message),console.warn("[ApiClient] JSON text length:",d==null?void 0:d.length),console.warn("[ApiClient] JSON text preview (first 200 chars):",d==null?void 0:d.substring(0,200)),console.warn("[ApiClient] JSON text preview (last 200 chars):",d==null?void 0:d.substring(d.length-200))}}else m&&(k=!0,yield{text:m,done:!1})}f=f.slice(E),f.length>1e6&&(console.warn("[ApiClient] Buffer too large, truncating..."),f=f.slice(-5e5))}k&&(yield{text:"",done:!0})}finally{P.releaseLock()}}catch(y){throw y.name==="AbortError"?new Error("Connection timed out. Please check if the server is running."):y.name==="TypeError"&&y.message.includes("Failed to fetch")?new Error("Could not connect to the server. Please check if the server is running."):y}}async clearConversationHistory(t){const e=t||this.sessionId;if(!e)throw new Error("No session ID provided and no current session available");if(!this.apiKey)throw new Error("API key is required for clearing conversation history");const s={"Content-Type":"application/json","X-Session-ID":e,"X-API-Key":this.apiKey};try{const r=await fetch(`${this.apiUrl}/admin/chat-history/${e}`,{...this.getFetchOptions({method:"DELETE",headers:s})});if(!r.ok){const n=await r.text();throw new Error(`Failed to clear conversation history: ${r.status} ${n}`)}return await r.json()}catch(r){throw r.name==="TypeError"&&r.message.includes("Failed to fetch")?new Error("Could not connect to the server. Please check if the server is running."):r}}async deleteConversationWithFiles(t,e){const s=t||this.sessionId;if(!s)throw new Error("No session ID provided and no current session available");if(!this.apiKey)throw new Error("API key is required for deleting conversation");const r={"Content-Type":"application/json","X-Session-ID":s,"X-API-Key":this.apiKey},i=e&&e.length>0?`?file_ids=${e.join(",")}`:"",n=`${this.apiUrl}/admin/conversations/${s}${i}`;try{const l=await fetch(n,{...this.getFetchOptions({method:"DELETE",headers:r})});if(!l.ok){const u=await l.text();throw new Error(`Failed to delete conversation: ${l.status} ${u}`)}return await l.json()}catch(l){throw l.name==="TypeError"&&l.message.includes("Failed to fetch")?new Error("Could not connect to the server. Please check if the server is running."):l}}async createThread(t,e){if(!this.apiKey)throw new Error("API key is required for creating threads");const s={"Content-Type":"application/json","X-API-Key":this.apiKey};try{const r=await fetch(`${this.apiUrl}/api/threads`,{...this.getFetchOptions({method:"POST",headers:s,body:JSON.stringify({message_id:t,session_id:e})})});if(!r.ok){const n=await r.text();throw new Error(`Failed to create thread: ${r.status} ${n}`)}return await r.json()}catch(r){throw r.name==="TypeError"&&r.message.includes("Failed to fetch")?new Error("Could not connect to the server. Please check if the server is running."):r}}async getThreadInfo(t){if(!this.apiKey)throw new Error("API key is required for getting thread info");const e={"X-API-Key":this.apiKey};try{const s=await fetch(`${this.apiUrl}/api/threads/${t}`,{...this.getFetchOptions({method:"GET",headers:e})});if(!s.ok){const i=await s.text();throw new Error(`Failed to get thread info: ${s.status} ${i}`)}return await s.json()}catch(s){throw s.name==="TypeError"&&s.message.includes("Failed to fetch")?new Error("Could not connect to the server. Please check if the server is running."):s}}async deleteThread(t){if(!this.apiKey)throw new Error("API key is required for deleting threads");const e={"X-API-Key":this.apiKey};try{const s=await fetch(`${this.apiUrl}/api/threads/${t}`,{...this.getFetchOptions({method:"DELETE",headers:e})});if(!s.ok){const i=await s.text();throw new Error(`Failed to delete thread: ${s.status} ${i}`)}return await s.json()}catch(s){throw s.name==="TypeError"&&s.message.includes("Failed to fetch")?new Error("Could not connect to the server. Please check if the server is running."):s}}async uploadFile(t){if(!this.apiKey)throw new Error("API key is required for file upload");const e=new FormData;e.append("file",t);try{const s=await fetch(`${this.apiUrl}/api/files/upload`,{...this.getFetchOptions({method:"POST",body:e})});if(!s.ok){const r=await s.text();throw new Error(`Failed to upload file: ${s.status} ${r}`)}return await s.json()}catch(s){throw s.name==="TypeError"&&s.message.includes("Failed to fetch")?new Error("Could not connect to the server. Please check if the server is running."):s}}async listFiles(){if(!this.apiKey)throw new Error("API key is required for listing files");try{const t=await fetch(`${this.apiUrl}/api/files`,{...this.getFetchOptions({method:"GET"})});if(!t.ok){const e=await t.text();throw new Error(`Failed to list files: ${t.status} ${e}`)}return await t.json()}catch(t){throw t.name==="TypeError"&&t.message.includes("Failed to fetch")?new Error("Could not connect to the server. Please check if the server is running."):t}}async getFileInfo(t){if(!this.apiKey)throw new Error("API key is required for getting file info");try{const e=await fetch(`${this.apiUrl}/api/files/${t}`,{...this.getFetchOptions({method:"GET"})});if(!e.ok){const s=await e.text();throw new Error(`Failed to get file info: ${e.status} ${s}`)}return await e.json()}catch(e){throw e.name==="TypeError"&&e.message.includes("Failed to fetch")?new Error("Could not connect to the server. Please check if the server is running."):e}}async queryFile(t,e,s=10){if(!this.apiKey)throw new Error("API key is required for querying files");try{const r=await fetch(`${this.apiUrl}/api/files/${t}/query`,{...this.getFetchOptions({method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({query:e,max_results:s})})});if(!r.ok){const i=await r.text();throw new Error(`Failed to query file: ${r.status} ${i}`)}return await r.json()}catch(r){throw r.name==="TypeError"&&r.message.includes("Failed to fetch")?new Error("Could not connect to the server. Please check if the server is running."):r}}async deleteFile(t){if(!this.apiKey)throw new Error("API key is required for deleting files");const e=`${this.apiUrl}/api/files/${t}`,s=this.getFetchOptions({method:"DELETE"});try{const r=await fetch(e,s);if(!r.ok){const n=await r.text();let l;try{const h=JSON.parse(n);l=h.detail||h.message||`Failed to delete file (HTTP ${r.status})`}catch{l=`Failed to delete file (HTTP ${r.status})`}throw console.warn(`[ApiClient] ${l}`),new Error(l)}return await r.json()}catch(r){let i;throw r.name==="TypeError"&&r.message.includes("Failed to fetch")?i="Could not connect to the server. Please check if the server is running.":r.message&&!r.message.includes("Failed to delete file")?i=r.message:i="Failed to delete file. Please try again.",console.warn(`[ApiClient] ${i}`),new Error(i)}}}let C=null;const G=(o,t=null,e=null)=>{C=new U({apiUrl:o,apiKey:t,sessionId:e})};async function*W(o,t=!0,e,s,r,i,n,l,h,u,p){if(!C)throw new Error("API not configured. Please call configureApi() with your server URL before using any API functions.");yield*C.streamChat(o,t,e,s,r,i,n,l,h,u,p)}exports.ApiClient=U;exports.configureApi=G;exports.streamChat=W;
|
|
3
3
|
//# sourceMappingURL=api.cjs.map
|
package/dist/api.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"api.cjs","sources":["../api.ts"],"sourcesContent":["// For Node.js environments, we can use http.Agent for connection pooling\nlet httpAgent: any = null;\nlet httpsAgent: any = null;\n\n// Initialize agents for connection pooling in Node.js environments\nif (typeof window === 'undefined') {\n // Lazy load to avoid including 'http' in browser bundles\n Promise.all([\n // @ts-expect-error - Dynamic import of Node.js built-in module (only available in Node.js runtime)\n import('http').catch(() => null),\n // @ts-expect-error - Dynamic import of Node.js built-in module (only available in Node.js runtime)\n import('https').catch(() => null)\n ]).then(([http, https]) => {\n if (http?.default?.Agent) {\n httpAgent = new http.default.Agent({ keepAlive: true });\n } else if (http?.Agent) {\n httpAgent = new http.Agent({ keepAlive: true });\n }\n \n if (https?.default?.Agent) {\n httpsAgent = new https.default.Agent({ keepAlive: true });\n } else if (https?.Agent) {\n httpsAgent = new https.Agent({ keepAlive: true });\n }\n }).catch(err => {\n // Silently fail - connection pooling is optional\n console.warn('Failed to initialize HTTP agents:', err.message);\n });\n}\n\n// Define the StreamResponse interface\nexport interface StreamResponse {\n text: string;\n done: boolean;\n audio?: string; // Optional base64-encoded audio data (TTS response) - full audio\n audioFormat?: string; // Audio format (mp3, wav, etc.)\n audio_chunk?: string; // Optional streaming audio chunk (base64-encoded)\n chunk_index?: number; // Index of the audio chunk for ordering\n threading?: { // Optional threading metadata\n supports_threading: boolean;\n message_id: string;\n session_id: string;\n };\n}\n\n// The server now returns this directly for non-streaming chat\nexport interface ChatResponse {\n response: string;\n sources?: any[];\n audio?: string; // Optional base64-encoded audio data (TTS response)\n audio_format?: string; // Audio format (mp3, wav, etc.)\n}\n\n// Thread-related interfaces\nexport interface ThreadInfo {\n thread_id: string;\n thread_session_id: string;\n parent_message_id: string;\n parent_session_id: string;\n adapter_name: string;\n created_at: string;\n expires_at: string;\n}\n\n// The request body for the /v1/chat endpoint\ninterface ChatRequest {\n messages: Array<{ role: string; content: string; }>;\n stream: boolean;\n file_ids?: string[]; // Optional list of file IDs for file context\n thread_id?: string; // Optional thread ID for follow-up questions\n audio_input?: string; // Optional base64-encoded audio data for STT\n audio_format?: string; // Optional audio format (mp3, wav, etc.)\n language?: string; // Optional language code for STT (e.g., \"en-US\")\n return_audio?: boolean; // Whether to return audio response (TTS)\n tts_voice?: string; // Voice for TTS (e.g., \"alloy\", \"echo\" for OpenAI)\n source_language?: string; // Source language for translation\n target_language?: string; // Target language for translation\n}\n\n// File-related interfaces\nexport interface FileUploadResponse {\n file_id: string;\n filename: string;\n mime_type: string;\n file_size: number;\n status: string;\n chunk_count: number;\n message: string;\n}\n\nexport interface FileInfo {\n file_id: string;\n filename: string;\n mime_type: string;\n file_size: number;\n upload_timestamp: string;\n processing_status: string;\n chunk_count: number;\n storage_type: string;\n}\n\nexport interface FileQueryRequest {\n query: string;\n max_results?: number;\n}\n\nexport interface FileQueryResponse {\n file_id: string;\n filename: string;\n results: Array<{\n content: string;\n metadata: {\n chunk_id: string;\n file_id: string;\n chunk_index: number;\n confidence: number;\n };\n }>;\n}\n\n// API key status interface\nexport interface ApiKeyStatus {\n exists: boolean;\n active: boolean;\n adapter_name?: string | null;\n client_name?: string | null;\n created_at?: string | number | null;\n system_prompt?: {\n id: string;\n exists: boolean;\n } | null;\n message?: string;\n}\n\n// Adapter information interface\nexport interface AdapterInfo {\n client_name: string;\n adapter_name: string;\n model: string | null;\n isFileSupported?: boolean;\n}\n\nexport class ApiClient {\n private readonly apiUrl: string;\n private readonly apiKey: string | null;\n private sessionId: string | null; // Session ID can be mutable\n\n constructor(config: { apiUrl: string; apiKey?: string | null; sessionId?: string | null }) {\n if (!config.apiUrl || typeof config.apiUrl !== 'string') {\n throw new Error('API URL must be a valid string');\n }\n if (config.apiKey !== undefined && config.apiKey !== null && typeof config.apiKey !== 'string') {\n throw new Error('API key must be a valid string or null');\n }\n if (config.sessionId !== undefined && config.sessionId !== null && typeof config.sessionId !== 'string') {\n throw new Error('Session ID must be a valid string or null');\n }\n \n this.apiUrl = config.apiUrl;\n this.apiKey = config.apiKey ?? null;\n this.sessionId = config.sessionId ?? null;\n }\n\n public setSessionId(sessionId: string | null): void {\n if (sessionId !== null && typeof sessionId !== 'string') {\n throw new Error('Session ID must be a valid string or null');\n }\n this.sessionId = sessionId;\n }\n\n public getSessionId(): string | null {\n return this.sessionId;\n }\n\n /**\n * Validate that the API key exists and is active.\n *\n * @returns Promise resolving to API key status information\n * @throws Error if API key is not provided, invalid, inactive, or validation fails\n */\n public async validateApiKey(): Promise<ApiKeyStatus> {\n if (!this.apiKey) {\n throw new Error('API key is required for validation');\n }\n\n try {\n const response = await fetch(`${this.apiUrl}/admin/api-keys/${this.apiKey}/status`, {\n ...this.getFetchOptions({\n method: 'GET'\n })\n }).catch((fetchError: any) => {\n // Catch network errors before they bubble up\n if (fetchError.name === 'TypeError' && fetchError.message.includes('Failed to fetch')) {\n throw new Error('Could not connect to the server. Please check if the server is running.');\n }\n throw fetchError;\n });\n\n if (!response.ok) {\n // Read error response body\n let errorText = '';\n try {\n errorText = await response.text();\n } catch {\n // If we can't read the body, fall back to status code\n errorText = `HTTP ${response.status}`;\n }\n\n let errorDetail: string;\n let friendlyMessage: string;\n\n try {\n const errorJson = JSON.parse(errorText);\n errorDetail = errorJson.detail || errorJson.message || errorText;\n } catch {\n // If parsing fails, use the error text or status code\n errorDetail = errorText || `HTTP ${response.status}`;\n }\n\n // Generate user-friendly error messages based on HTTP status code\n switch (response.status) {\n case 401:\n friendlyMessage = 'API key is invalid or expired';\n break;\n case 403:\n friendlyMessage = 'Access denied: API key does not have required permissions';\n break;\n case 404:\n friendlyMessage = 'API key not found';\n break;\n case 503:\n friendlyMessage = 'API key management is not available in inference-only mode';\n break;\n default:\n friendlyMessage = `Failed to validate API key: ${errorDetail}`;\n break;\n }\n\n // Throw error - will be logged in catch block to avoid duplicates\n throw new Error(friendlyMessage);\n }\n\n const status: ApiKeyStatus = await response.json();\n\n // Check if the key exists\n if (!status.exists) {\n const friendlyMessage = 'API key does not exist';\n // Throw error - will be logged in catch block to avoid duplicates\n throw new Error(friendlyMessage);\n }\n\n // Check if the key is active\n if (!status.active) {\n const friendlyMessage = 'API key is inactive';\n // Throw error - will be logged in catch block to avoid duplicates\n throw new Error(friendlyMessage);\n }\n\n return status;\n } catch (error: any) {\n // Extract user-friendly error message\n let friendlyMessage: string;\n\n if (error instanceof Error && error.message) {\n // If it's already a user-friendly Error from above, use it directly\n if (error.message.includes('API key') ||\n error.message.includes('Access denied') ||\n error.message.includes('invalid') ||\n error.message.includes('expired') ||\n error.message.includes('inactive') ||\n error.message.includes('not found') ||\n error.message.includes('Could not connect')) {\n friendlyMessage = error.message;\n } else {\n friendlyMessage = `API key validation failed: ${error.message}`;\n }\n } else if (error.name === 'TypeError' && error.message?.includes('Failed to fetch')) {\n friendlyMessage = 'Could not connect to the server. Please check if the server is running.';\n } else {\n friendlyMessage = 'API key validation failed. Please check your API key and try again.';\n }\n\n // Only log warning if it's not a network error (those are already logged by browser)\n // For validation errors, we log once with a friendly message\n // Note: Browser will still log HTTP errors (401, 404, etc.) - this is unavoidable\n console.warn(`[ApiClient] ${friendlyMessage}`);\n\n // Throw the friendly error message\n throw new Error(friendlyMessage);\n }\n }\n\n /**\n * Get adapter information for the current API key.\n *\n * Returns information about the adapter and model being used by the API key.\n * This is useful for displaying configuration details to users.\n *\n * @returns Promise resolving to adapter information\n * @throws Error if API key is not provided, invalid, disabled, or request fails\n */\n public async getAdapterInfo(): Promise<AdapterInfo> {\n if (!this.apiKey) {\n throw new Error('API key is required to get adapter information');\n }\n\n try {\n const response = await fetch(`${this.apiUrl}/admin/api-keys/info`, {\n ...this.getFetchOptions({\n method: 'GET'\n })\n }).catch((fetchError: any) => {\n // Catch network errors before they bubble up\n if (fetchError.name === 'TypeError' && fetchError.message.includes('Failed to fetch')) {\n throw new Error('Could not connect to the server. Please check if the server is running.');\n }\n throw fetchError;\n });\n\n if (!response.ok) {\n // Read error response body\n let errorText = '';\n try {\n errorText = await response.text();\n } catch {\n // If we can't read the body, fall back to status code\n errorText = `HTTP ${response.status}`;\n }\n\n let errorDetail: string;\n let friendlyMessage: string;\n\n try {\n const errorJson = JSON.parse(errorText);\n errorDetail = errorJson.detail || errorJson.message || errorText;\n } catch {\n // If parsing fails, use the error text or status code\n errorDetail = errorText || `HTTP ${response.status}`;\n }\n\n // Generate user-friendly error messages based on HTTP status code\n switch (response.status) {\n case 401:\n friendlyMessage = 'API key is invalid, disabled, or has no associated adapter';\n break;\n case 404:\n friendlyMessage = 'Adapter configuration not found';\n break;\n case 503:\n friendlyMessage = 'Service is not available';\n break;\n default:\n friendlyMessage = `Failed to get adapter info: ${errorDetail}`;\n break;\n }\n\n // Throw error - will be logged in catch block to avoid duplicates\n throw new Error(friendlyMessage);\n }\n\n const adapterInfo: AdapterInfo = await response.json();\n return adapterInfo;\n } catch (error: any) {\n // Extract user-friendly error message\n let friendlyMessage: string;\n\n if (error instanceof Error && error.message) {\n // If it's already a user-friendly Error from above, use it directly\n if (error.message.includes('API key') ||\n error.message.includes('Adapter') ||\n error.message.includes('invalid') ||\n error.message.includes('disabled') ||\n error.message.includes('not found') ||\n error.message.includes('Could not connect')) {\n friendlyMessage = error.message;\n } else {\n friendlyMessage = `Failed to get adapter info: ${error.message}`;\n }\n } else if (error.name === 'TypeError' && error.message?.includes('Failed to fetch')) {\n friendlyMessage = 'Could not connect to the server. Please check if the server is running.';\n } else {\n friendlyMessage = 'Failed to get adapter information. Please try again.';\n }\n\n console.warn(`[ApiClient] ${friendlyMessage}`);\n\n // Throw the friendly error message\n throw new Error(friendlyMessage);\n }\n }\n\n // Helper to get fetch options with connection pooling if available\n private getFetchOptions(options: RequestInit = {}): RequestInit {\n const baseOptions: RequestInit = {};\n \n // Environment-specific options\n if (typeof window === 'undefined') {\n // Node.js: Use connection pooling agent\n const isHttps = this.apiUrl.startsWith('https:');\n const agent = isHttps ? httpsAgent : httpAgent;\n if (agent) {\n (baseOptions as any).agent = agent;\n }\n } else {\n // Browser: Use keep-alive header\n baseOptions.headers = { 'Connection': 'keep-alive' };\n }\n\n // Common headers\n const headers: Record<string, string> = {\n 'X-Request-ID': Date.now().toString(36) + Math.random().toString(36).substring(2),\n };\n\n // Merge base options headers (for browser keep-alive)\n if (baseOptions.headers) {\n Object.assign(headers, baseOptions.headers);\n }\n\n // Merge original request headers (but don't overwrite API key)\n if (options.headers) {\n const incomingHeaders = options.headers as Record<string, string>;\n for (const [key, value] of Object.entries(incomingHeaders)) {\n // Don't overwrite X-API-Key if we have one\n if (key.toLowerCase() !== 'x-api-key' || !this.apiKey) {\n headers[key] = value;\n }\n }\n }\n\n if (this.apiKey) {\n headers['X-API-Key'] = this.apiKey;\n }\n\n if (this.sessionId) {\n headers['X-Session-ID'] = this.sessionId;\n }\n\n return {\n ...options,\n ...baseOptions,\n headers,\n };\n }\n\n // Create Chat request\n private createChatRequest(\n message: string, \n stream: boolean = true, \n fileIds?: string[],\n threadId?: string,\n audioInput?: string,\n audioFormat?: string,\n language?: string,\n returnAudio?: boolean,\n ttsVoice?: string,\n sourceLanguage?: string,\n targetLanguage?: string\n ): ChatRequest {\n const request: ChatRequest = {\n messages: [\n { role: \"user\", content: message }\n ],\n stream\n };\n if (fileIds && fileIds.length > 0) {\n request.file_ids = fileIds;\n }\n if (threadId) {\n request.thread_id = threadId;\n }\n if (audioInput) {\n request.audio_input = audioInput;\n }\n if (audioFormat) {\n request.audio_format = audioFormat;\n }\n if (language) {\n request.language = language;\n }\n if (returnAudio !== undefined) {\n request.return_audio = returnAudio;\n }\n if (ttsVoice) {\n request.tts_voice = ttsVoice;\n }\n if (sourceLanguage) {\n request.source_language = sourceLanguage;\n }\n if (targetLanguage) {\n request.target_language = targetLanguage;\n }\n return request;\n }\n\n public async *streamChat(\n message: string,\n stream: boolean = true,\n fileIds?: string[],\n threadId?: string,\n audioInput?: string,\n audioFormat?: string,\n language?: string,\n returnAudio?: boolean,\n ttsVoice?: string,\n sourceLanguage?: string,\n targetLanguage?: string\n ): AsyncGenerator<StreamResponse> {\n try {\n // Add timeout to the fetch request\n const controller = new AbortController();\n const timeoutId = setTimeout(() => controller.abort(), 60000); // 60 second timeout\n\n const response = await fetch(`${this.apiUrl}/v1/chat`, {\n ...this.getFetchOptions({\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n 'Accept': stream ? 'text/event-stream' : 'application/json'\n },\n body: JSON.stringify(this.createChatRequest(\n message, \n stream, \n fileIds,\n threadId,\n audioInput,\n audioFormat,\n language,\n returnAudio,\n ttsVoice,\n sourceLanguage,\n targetLanguage\n )),\n }),\n signal: controller.signal\n });\n\n clearTimeout(timeoutId);\n\n if (!response.ok) {\n const errorText = await response.text();\n throw new Error(`Network response was not ok: ${response.status} ${errorText}`);\n }\n\n if (!stream) {\n // Handle non-streaming response\n const data = await response.json() as ChatResponse;\n if (data.response) {\n yield {\n text: data.response,\n done: true,\n audio: data.audio,\n audioFormat: data.audio_format\n } as StreamResponse & { audio?: string; audioFormat?: string };\n }\n return;\n }\n \n const reader = response.body?.getReader();\n if (!reader) throw new Error('No reader available');\n\n const decoder = new TextDecoder();\n let buffer = '';\n let hasReceivedContent = false;\n\n try {\n while (true) {\n const { done, value } = await reader.read();\n if (done) {\n break;\n }\n\n const chunk = decoder.decode(value, { stream: true });\n buffer += chunk;\n \n // Process complete lines immediately as they arrive\n let lineStartIndex = 0;\n let newlineIndex;\n \n while ((newlineIndex = buffer.indexOf('\\n', lineStartIndex)) !== -1) {\n const line = buffer.slice(lineStartIndex, newlineIndex).trim();\n lineStartIndex = newlineIndex + 1;\n \n if (line && line.startsWith('data: ')) {\n const jsonText = line.slice(6).trim();\n \n if (!jsonText || jsonText === '[DONE]') {\n yield { text: '', done: true };\n return;\n }\n\n try {\n const data = JSON.parse(jsonText);\n\n if (data.error) {\n const errorMessage = data.error?.message || data.error || 'Unknown server error';\n const friendlyMessage = `Server error: ${errorMessage}`;\n console.warn(`[ApiClient] ${friendlyMessage}`);\n throw new Error(friendlyMessage);\n }\n\n // Check for done chunk first - it may not have a response field\n // This handles the final done chunk that contains threading metadata\n if (data.done === true) {\n hasReceivedContent = true;\n yield {\n text: '',\n done: true,\n audio: data.audio,\n audioFormat: data.audio_format || data.audioFormat,\n threading: data.threading // Pass through threading metadata\n };\n return;\n }\n\n // Note: Base64 audio filtering is handled by chatStore's sanitizeMessageContent\n // We keep response text as-is here and let the application layer decide\n const responseText = data.response || '';\n\n // Handle streaming audio chunks\n if (data.audio_chunk !== undefined) {\n yield {\n text: '',\n done: false,\n audio_chunk: data.audio_chunk,\n audioFormat: data.audioFormat || data.audio_format || 'opus',\n chunk_index: data.chunk_index ?? 0\n };\n }\n\n if (responseText || data.audio) {\n hasReceivedContent = true;\n yield {\n text: responseText,\n done: data.done || false,\n audio: data.audio,\n audioFormat: data.audio_format || data.audioFormat,\n threading: data.threading // Include threading if present\n };\n }\n\n } catch (parseError: any) {\n // Log more details for debugging\n console.warn('[ApiClient] Unable to parse server response. This may be a temporary issue.');\n console.warn('[ApiClient] Parse error details:', parseError?.message);\n console.warn('[ApiClient] JSON text length:', jsonText?.length);\n console.warn('[ApiClient] JSON text preview (first 200 chars):', jsonText?.substring(0, 200));\n console.warn('[ApiClient] JSON text preview (last 200 chars):', jsonText?.substring(jsonText.length - 200));\n }\n } else if (line) {\n // Handle raw text chunks that are not in SSE format\n hasReceivedContent = true;\n yield { text: line, done: false };\n }\n }\n \n buffer = buffer.slice(lineStartIndex);\n\n if (buffer.length > 1000000) { // 1MB limit\n console.warn('[ApiClient] Buffer too large, truncating...');\n buffer = buffer.slice(-500000); // Keep last 500KB\n }\n }\n \n if (hasReceivedContent) {\n yield { text: '', done: true };\n }\n \n } finally {\n reader.releaseLock();\n }\n \n } catch (error: any) {\n if (error.name === 'AbortError') {\n throw new Error('Connection timed out. Please check if the server is running.');\n } else if (error.name === 'TypeError' && error.message.includes('Failed to fetch')) {\n throw new Error('Could not connect to the server. Please check if the server is running.');\n } else {\n throw error;\n }\n }\n }\n\n public async clearConversationHistory(sessionId?: string): Promise<{\n status: string;\n message: string;\n session_id: string;\n deleted_count: number;\n timestamp: string;\n }> {\n /**\n * Clear conversation history for a session.\n *\n * @param sessionId - Optional session ID to clear. If not provided, uses current session.\n * @returns Promise resolving to operation result\n * @throws Error if the operation fails\n */\n const targetSessionId = sessionId || this.sessionId;\n\n if (!targetSessionId) {\n throw new Error('No session ID provided and no current session available');\n }\n\n if (!this.apiKey) {\n throw new Error('API key is required for clearing conversation history');\n }\n\n const headers: Record<string, string> = {\n 'Content-Type': 'application/json',\n 'X-Session-ID': targetSessionId,\n 'X-API-Key': this.apiKey\n };\n\n try {\n const response = await fetch(`${this.apiUrl}/admin/chat-history/${targetSessionId}`, {\n ...this.getFetchOptions({\n method: 'DELETE',\n headers\n })\n });\n\n if (!response.ok) {\n const errorText = await response.text();\n throw new Error(`Failed to clear conversation history: ${response.status} ${errorText}`);\n }\n\n const result = await response.json();\n return result;\n\n } catch (error: any) {\n if (error.name === 'TypeError' && error.message.includes('Failed to fetch')) {\n throw new Error('Could not connect to the server. Please check if the server is running.');\n } else {\n throw error;\n }\n }\n }\n\n public async deleteConversationWithFiles(sessionId?: string, fileIds?: string[]): Promise<{\n status: string;\n message: string;\n session_id: string;\n deleted_messages: number;\n deleted_files: number;\n file_deletion_errors: string[] | null;\n timestamp: string;\n }> {\n /**\n * Delete a conversation and all associated files.\n *\n * This method performs a complete conversation deletion:\n * - Deletes each file provided in fileIds (metadata, content, and vector store chunks)\n * - Clears conversation history\n *\n * File tracking is managed by the frontend (localStorage). The backend is stateless\n * and requires fileIds to be provided explicitly.\n *\n * @param sessionId - Optional session ID to delete. If not provided, uses current session.\n * @param fileIds - Optional list of file IDs to delete (from conversation's attachedFiles)\n * @returns Promise resolving to deletion result with counts\n * @throws Error if the operation fails\n */\n const targetSessionId = sessionId || this.sessionId;\n\n if (!targetSessionId) {\n throw new Error('No session ID provided and no current session available');\n }\n\n if (!this.apiKey) {\n throw new Error('API key is required for deleting conversation');\n }\n\n const headers: Record<string, string> = {\n 'Content-Type': 'application/json',\n 'X-Session-ID': targetSessionId,\n 'X-API-Key': this.apiKey\n };\n\n // Build URL with file_ids query parameter\n const fileIdsParam = fileIds && fileIds.length > 0 ? `?file_ids=${fileIds.join(',')}` : '';\n const url = `${this.apiUrl}/admin/conversations/${targetSessionId}${fileIdsParam}`;\n\n try {\n const response = await fetch(url, {\n ...this.getFetchOptions({\n method: 'DELETE',\n headers\n })\n });\n\n if (!response.ok) {\n const errorText = await response.text();\n throw new Error(`Failed to delete conversation: ${response.status} ${errorText}`);\n }\n\n const result = await response.json();\n return result;\n\n } catch (error: any) {\n if (error.name === 'TypeError' && error.message.includes('Failed to fetch')) {\n throw new Error('Could not connect to the server. Please check if the server is running.');\n } else {\n throw error;\n }\n }\n }\n\n /**\n * Create a conversation thread from a parent message.\n *\n * @param messageId - ID of the parent message\n * @param sessionId - Session ID of the parent conversation\n * @returns Promise resolving to thread information\n * @throws Error if the operation fails\n */\n public async createThread(messageId: string, sessionId: string): Promise<ThreadInfo> {\n if (!this.apiKey) {\n throw new Error('API key is required for creating threads');\n }\n\n const headers: Record<string, string> = {\n 'Content-Type': 'application/json',\n 'X-API-Key': this.apiKey\n };\n\n try {\n const response = await fetch(`${this.apiUrl}/api/threads`, {\n ...this.getFetchOptions({\n method: 'POST',\n headers,\n body: JSON.stringify({\n message_id: messageId,\n session_id: sessionId\n })\n })\n });\n\n if (!response.ok) {\n const errorText = await response.text();\n throw new Error(`Failed to create thread: ${response.status} ${errorText}`);\n }\n\n const result = await response.json();\n return result;\n\n } catch (error: any) {\n if (error.name === 'TypeError' && error.message.includes('Failed to fetch')) {\n throw new Error('Could not connect to the server. Please check if the server is running.');\n } else {\n throw error;\n }\n }\n }\n\n /**\n * Get thread information by thread ID.\n *\n * @param threadId - Thread identifier\n * @returns Promise resolving to thread information\n * @throws Error if the operation fails\n */\n public async getThreadInfo(threadId: string): Promise<ThreadInfo> {\n if (!this.apiKey) {\n throw new Error('API key is required for getting thread info');\n }\n\n const headers: Record<string, string> = {\n 'X-API-Key': this.apiKey\n };\n\n try {\n const response = await fetch(`${this.apiUrl}/api/threads/${threadId}`, {\n ...this.getFetchOptions({\n method: 'GET',\n headers\n })\n });\n\n if (!response.ok) {\n const errorText = await response.text();\n throw new Error(`Failed to get thread info: ${response.status} ${errorText}`);\n }\n\n const result = await response.json();\n return result;\n\n } catch (error: any) {\n if (error.name === 'TypeError' && error.message.includes('Failed to fetch')) {\n throw new Error('Could not connect to the server. Please check if the server is running.');\n } else {\n throw error;\n }\n }\n }\n\n /**\n * Delete a thread and its associated dataset.\n *\n * @param threadId - Thread identifier\n * @returns Promise resolving to deletion result\n * @throws Error if the operation fails\n */\n public async deleteThread(threadId: string): Promise<{ status: string; message: string; thread_id: string }> {\n if (!this.apiKey) {\n throw new Error('API key is required for deleting threads');\n }\n\n const headers: Record<string, string> = {\n 'X-API-Key': this.apiKey\n };\n\n try {\n const response = await fetch(`${this.apiUrl}/api/threads/${threadId}`, {\n ...this.getFetchOptions({\n method: 'DELETE',\n headers\n })\n });\n\n if (!response.ok) {\n const errorText = await response.text();\n throw new Error(`Failed to delete thread: ${response.status} ${errorText}`);\n }\n\n const result = await response.json();\n return result;\n\n } catch (error: any) {\n if (error.name === 'TypeError' && error.message.includes('Failed to fetch')) {\n throw new Error('Could not connect to the server. Please check if the server is running.');\n } else {\n throw error;\n }\n }\n }\n\n /**\n * Upload a file for processing and indexing.\n *\n * @param file - The file to upload\n * @returns Promise resolving to upload response with file_id\n * @throws Error if upload fails\n */\n public async uploadFile(file: File): Promise<FileUploadResponse> {\n if (!this.apiKey) {\n throw new Error('API key is required for file upload');\n }\n\n const formData = new FormData();\n formData.append('file', file);\n\n try {\n const response = await fetch(`${this.apiUrl}/api/files/upload`, {\n ...this.getFetchOptions({\n method: 'POST',\n body: formData\n })\n });\n\n if (!response.ok) {\n const errorText = await response.text();\n throw new Error(`Failed to upload file: ${response.status} ${errorText}`);\n }\n\n return await response.json();\n } catch (error: any) {\n if (error.name === 'TypeError' && error.message.includes('Failed to fetch')) {\n throw new Error('Could not connect to the server. Please check if the server is running.');\n } else {\n throw error;\n }\n }\n }\n\n /**\n * List all files for the current API key.\n * \n * @returns Promise resolving to list of file information\n * @throws Error if request fails\n */\n public async listFiles(): Promise<FileInfo[]> {\n if (!this.apiKey) {\n throw new Error('API key is required for listing files');\n }\n\n try {\n const response = await fetch(`${this.apiUrl}/api/files`, {\n ...this.getFetchOptions({\n method: 'GET'\n })\n });\n\n if (!response.ok) {\n const errorText = await response.text();\n throw new Error(`Failed to list files: ${response.status} ${errorText}`);\n }\n\n return await response.json();\n } catch (error: any) {\n if (error.name === 'TypeError' && error.message.includes('Failed to fetch')) {\n throw new Error('Could not connect to the server. Please check if the server is running.');\n } else {\n throw error;\n }\n }\n }\n\n /**\n * Get information about a specific file.\n * \n * @param fileId - The file ID\n * @returns Promise resolving to file information\n * @throws Error if file not found or request fails\n */\n public async getFileInfo(fileId: string): Promise<FileInfo> {\n if (!this.apiKey) {\n throw new Error('API key is required for getting file info');\n }\n\n try {\n const response = await fetch(`${this.apiUrl}/api/files/${fileId}`, {\n ...this.getFetchOptions({\n method: 'GET'\n })\n });\n\n if (!response.ok) {\n const errorText = await response.text();\n throw new Error(`Failed to get file info: ${response.status} ${errorText}`);\n }\n\n return await response.json();\n } catch (error: any) {\n if (error.name === 'TypeError' && error.message.includes('Failed to fetch')) {\n throw new Error('Could not connect to the server. Please check if the server is running.');\n } else {\n throw error;\n }\n }\n }\n\n /**\n * Query a specific file using semantic search.\n * \n * @param fileId - The file ID\n * @param query - The search query\n * @param maxResults - Maximum number of results (default: 10)\n * @returns Promise resolving to query results\n * @throws Error if query fails\n */\n public async queryFile(fileId: string, query: string, maxResults: number = 10): Promise<FileQueryResponse> {\n if (!this.apiKey) {\n throw new Error('API key is required for querying files');\n }\n\n try {\n const response = await fetch(`${this.apiUrl}/api/files/${fileId}/query`, {\n ...this.getFetchOptions({\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json'\n },\n body: JSON.stringify({ query, max_results: maxResults })\n })\n });\n\n if (!response.ok) {\n const errorText = await response.text();\n throw new Error(`Failed to query file: ${response.status} ${errorText}`);\n }\n\n return await response.json();\n } catch (error: any) {\n if (error.name === 'TypeError' && error.message.includes('Failed to fetch')) {\n throw new Error('Could not connect to the server. Please check if the server is running.');\n } else {\n throw error;\n }\n }\n }\n\n /**\n * Delete a specific file.\n * \n * @param fileId - The file ID\n * @returns Promise resolving to deletion result\n * @throws Error if deletion fails\n */\n public async deleteFile(fileId: string): Promise<{ message: string; file_id: string }> {\n if (!this.apiKey) {\n throw new Error('API key is required for deleting files');\n }\n\n const url = `${this.apiUrl}/api/files/${fileId}`;\n const fetchOptions = this.getFetchOptions({\n method: 'DELETE'\n });\n\n try {\n const response = await fetch(url, fetchOptions);\n\n if (!response.ok) {\n const errorText = await response.text();\n let friendlyMessage: string;\n try {\n const errorJson = JSON.parse(errorText);\n friendlyMessage = errorJson.detail || errorJson.message || `Failed to delete file (HTTP ${response.status})`;\n } catch {\n friendlyMessage = `Failed to delete file (HTTP ${response.status})`;\n }\n console.warn(`[ApiClient] ${friendlyMessage}`);\n throw new Error(friendlyMessage);\n }\n\n const result = await response.json();\n return result;\n } catch (error: any) {\n // Extract user-friendly error message\n let friendlyMessage: string;\n \n if (error.name === 'TypeError' && error.message.includes('Failed to fetch')) {\n friendlyMessage = 'Could not connect to the server. Please check if the server is running.';\n } else if (error.message && !error.message.includes('Failed to delete file')) {\n // Use existing message if it's already user-friendly\n friendlyMessage = error.message;\n } else {\n friendlyMessage = `Failed to delete file. Please try again.`;\n }\n \n console.warn(`[ApiClient] ${friendlyMessage}`);\n throw new Error(friendlyMessage);\n }\n }\n}\n\n// Legacy compatibility functions - these create a default client instance\n// These are kept for backward compatibility but should be deprecated in favor of the class-based approach\n\nlet defaultClient: ApiClient | null = null;\n\n// Configure the API with a custom URL, API key (optional), and session ID (optional)\nexport const configureApi = (apiUrl: string, apiKey: string | null = null, sessionId: string | null = null): void => {\n defaultClient = new ApiClient({ apiUrl, apiKey, sessionId });\n}\n\n// Legacy streamChat function that uses the default client\nexport async function* streamChat(\n message: string,\n stream: boolean = true,\n fileIds?: string[],\n threadId?: string,\n audioInput?: string,\n audioFormat?: string,\n language?: string,\n returnAudio?: boolean,\n ttsVoice?: string,\n sourceLanguage?: string,\n targetLanguage?: string\n): AsyncGenerator<StreamResponse> {\n if (!defaultClient) {\n throw new Error('API not configured. Please call configureApi() with your server URL before using any API functions.');\n }\n \n yield* defaultClient.streamChat(\n message, \n stream, \n fileIds,\n threadId,\n audioInput,\n audioFormat,\n language,\n returnAudio,\n ttsVoice,\n sourceLanguage,\n targetLanguage\n );\n}\n\n"],"names":["httpAgent","httpsAgent","http","https","_a","_b","err","ApiClient","config","__publicField","sessionId","response","fetchError","errorText","errorDetail","friendlyMessage","errorJson","status","error","options","baseOptions","agent","headers","incomingHeaders","key","value","message","stream","fileIds","threadId","audioInput","audioFormat","language","returnAudio","ttsVoice","sourceLanguage","targetLanguage","request","controller","timeoutId","data","reader","decoder","buffer","hasReceivedContent","done","chunk","lineStartIndex","newlineIndex","line","jsonText","responseText","parseError","targetSessionId","fileIdsParam","url","messageId","file","formData","fileId","query","maxResults","fetchOptions","defaultClient","configureApi","apiUrl","apiKey","streamChat"],"mappings":"mqBACA,IAAIA,EAAiB,KACjBC,EAAkB,KAGlB,OAAO,OAAW,KAEpB,QAAQ,IAAI,CAEV,OAAO,MAAM,EAAE,MAAM,IAAM,IAAI,EAE/B,OAAO,OAAO,EAAE,MAAM,IAAM,IAAI,CAAA,CACjC,EAAE,KAAK,CAAC,CAACC,EAAMC,CAAK,IAAM,UACrBC,EAAAF,GAAA,YAAAA,EAAM,UAAN,MAAAE,EAAe,MACjBJ,EAAY,IAAIE,EAAK,QAAQ,MAAM,CAAE,UAAW,GAAM,EAC7CA,GAAA,MAAAA,EAAM,QACfF,EAAY,IAAIE,EAAK,MAAM,CAAE,UAAW,GAAM,IAG5CG,EAAAF,GAAA,YAAAA,EAAO,UAAP,MAAAE,EAAgB,MAClBJ,EAAa,IAAIE,EAAM,QAAQ,MAAM,CAAE,UAAW,GAAM,EAC/CA,GAAA,MAAAA,EAAO,QAChBF,EAAa,IAAIE,EAAM,MAAM,CAAE,UAAW,GAAM,EAEpD,CAAC,EAAE,MAAMG,GAAO,CAEd,QAAQ,KAAK,oCAAqCA,EAAI,OAAO,CAC/D,CAAC,EAmHI,MAAMC,CAAU,CAKrB,YAAYC,EAA+E,CAJ1EC,EAAA,eACAA,EAAA,eACTA,EAAA,kBAGN,GAAI,CAACD,EAAO,QAAU,OAAOA,EAAO,QAAW,SAC7C,MAAM,IAAI,MAAM,gCAAgC,EAElD,GAAIA,EAAO,SAAW,QAAaA,EAAO,SAAW,MAAQ,OAAOA,EAAO,QAAW,SACpF,MAAM,IAAI,MAAM,wCAAwC,EAE1D,GAAIA,EAAO,YAAc,QAAaA,EAAO,YAAc,MAAQ,OAAOA,EAAO,WAAc,SAC7F,MAAM,IAAI,MAAM,2CAA2C,EAG7D,KAAK,OAASA,EAAO,OACrB,KAAK,OAASA,EAAO,QAAU,KAC/B,KAAK,UAAYA,EAAO,WAAa,IACvC,CAEO,aAAaE,EAAgC,CAClD,GAAIA,IAAc,MAAQ,OAAOA,GAAc,SAC7C,MAAM,IAAI,MAAM,2CAA2C,EAE7D,KAAK,UAAYA,CACnB,CAEO,cAA8B,CACnC,OAAO,KAAK,SACd,CAQA,MAAa,gBAAwC,OACnD,GAAI,CAAC,KAAK,OACR,MAAM,IAAI,MAAM,oCAAoC,EAGtD,GAAI,CACF,MAAMC,EAAW,MAAM,MAAM,GAAG,KAAK,MAAM,mBAAmB,KAAK,MAAM,UAAW,CAClF,GAAG,KAAK,gBAAgB,CACtB,OAAQ,KAAA,CACT,CAAA,CACF,EAAE,MAAOC,GAAoB,CAE5B,MAAIA,EAAW,OAAS,aAAeA,EAAW,QAAQ,SAAS,iBAAiB,EAC5E,IAAI,MAAM,yEAAyE,EAErFA,CACR,CAAC,EAED,GAAI,CAACD,EAAS,GAAI,CAEhB,IAAIE,EAAY,GAChB,GAAI,CACFA,EAAY,MAAMF,EAAS,KAAA,CAC7B,MAAQ,CAENE,EAAY,QAAQF,EAAS,MAAM,EACrC,CAEA,IAAIG,EACAC,EAEJ,GAAI,CACF,MAAMC,EAAY,KAAK,MAAMH,CAAS,EACtCC,EAAcE,EAAU,QAAUA,EAAU,SAAWH,CACzD,MAAQ,CAENC,EAAcD,GAAa,QAAQF,EAAS,MAAM,EACpD,CAGA,OAAQA,EAAS,OAAA,CACf,IAAK,KACHI,EAAkB,gCAClB,MACF,IAAK,KACHA,EAAkB,4DAClB,MACF,IAAK,KACHA,EAAkB,oBAClB,MACF,IAAK,KACHA,EAAkB,6DAClB,MACF,QACEA,EAAkB,+BAA+BD,CAAW,GAC5D,KAAA,CAIJ,MAAM,IAAI,MAAMC,CAAe,CACjC,CAEA,MAAME,EAAuB,MAAMN,EAAS,KAAA,EAG5C,GAAI,CAACM,EAAO,OAAQ,CAClB,MAAMF,EAAkB,yBAExB,MAAM,IAAI,MAAMA,CAAe,CACjC,CAGA,GAAI,CAACE,EAAO,OAAQ,CAClB,MAAMF,EAAkB,sBAExB,MAAM,IAAI,MAAMA,CAAe,CACjC,CAEA,OAAOE,CACT,OAASC,EAAY,CAEnB,IAAIH,EAEJ,MAAIG,aAAiB,OAASA,EAAM,QAE9BA,EAAM,QAAQ,SAAS,SAAS,GAChCA,EAAM,QAAQ,SAAS,eAAe,GACtCA,EAAM,QAAQ,SAAS,SAAS,GAChCA,EAAM,QAAQ,SAAS,SAAS,GAChCA,EAAM,QAAQ,SAAS,UAAU,GACjCA,EAAM,QAAQ,SAAS,WAAW,GAClCA,EAAM,QAAQ,SAAS,mBAAmB,EAC5CH,EAAkBG,EAAM,QAExBH,EAAkB,8BAA8BG,EAAM,OAAO,GAEtDA,EAAM,OAAS,eAAed,EAAAc,EAAM,UAAN,MAAAd,EAAe,SAAS,oBAC/DW,EAAkB,0EAElBA,EAAkB,sEAMpB,QAAQ,KAAK,eAAeA,CAAe,EAAE,EAGvC,IAAI,MAAMA,CAAe,CACjC,CACF,CAWA,MAAa,gBAAuC,OAClD,GAAI,CAAC,KAAK,OACR,MAAM,IAAI,MAAM,gDAAgD,EAGlE,GAAI,CACF,MAAMJ,EAAW,MAAM,MAAM,GAAG,KAAK,MAAM,uBAAwB,CACjE,GAAG,KAAK,gBAAgB,CACtB,OAAQ,KAAA,CACT,CAAA,CACF,EAAE,MAAOC,GAAoB,CAE5B,MAAIA,EAAW,OAAS,aAAeA,EAAW,QAAQ,SAAS,iBAAiB,EAC5E,IAAI,MAAM,yEAAyE,EAErFA,CACR,CAAC,EAED,GAAI,CAACD,EAAS,GAAI,CAEhB,IAAIE,EAAY,GAChB,GAAI,CACFA,EAAY,MAAMF,EAAS,KAAA,CAC7B,MAAQ,CAENE,EAAY,QAAQF,EAAS,MAAM,EACrC,CAEA,IAAIG,EACAC,EAEJ,GAAI,CACF,MAAMC,EAAY,KAAK,MAAMH,CAAS,EACtCC,EAAcE,EAAU,QAAUA,EAAU,SAAWH,CACzD,MAAQ,CAENC,EAAcD,GAAa,QAAQF,EAAS,MAAM,EACpD,CAGA,OAAQA,EAAS,OAAA,CACf,IAAK,KACHI,EAAkB,6DAClB,MACF,IAAK,KACHA,EAAkB,kCAClB,MACF,IAAK,KACHA,EAAkB,2BAClB,MACF,QACEA,EAAkB,+BAA+BD,CAAW,GAC5D,KAAA,CAIJ,MAAM,IAAI,MAAMC,CAAe,CACjC,CAGA,OADiC,MAAMJ,EAAS,KAAA,CAElD,OAASO,EAAY,CAEnB,IAAIH,EAEJ,MAAIG,aAAiB,OAASA,EAAM,QAE9BA,EAAM,QAAQ,SAAS,SAAS,GAChCA,EAAM,QAAQ,SAAS,SAAS,GAChCA,EAAM,QAAQ,SAAS,SAAS,GAChCA,EAAM,QAAQ,SAAS,UAAU,GACjCA,EAAM,QAAQ,SAAS,WAAW,GAClCA,EAAM,QAAQ,SAAS,mBAAmB,EAC5CH,EAAkBG,EAAM,QAExBH,EAAkB,+BAA+BG,EAAM,OAAO,GAEvDA,EAAM,OAAS,eAAed,EAAAc,EAAM,UAAN,MAAAd,EAAe,SAAS,oBAC/DW,EAAkB,0EAElBA,EAAkB,uDAGpB,QAAQ,KAAK,eAAeA,CAAe,EAAE,EAGvC,IAAI,MAAMA,CAAe,CACjC,CACF,CAGQ,gBAAgBI,EAAuB,GAAiB,CAC9D,MAAMC,EAA2B,CAAA,EAGjC,GAAI,OAAO,OAAW,IAAa,CAGjC,MAAMC,EADU,KAAK,OAAO,WAAW,QAAQ,EACvBpB,EAAaD,EACjCqB,IACDD,EAAoB,MAAQC,EAEjC,MAEED,EAAY,QAAU,CAAE,WAAc,YAAA,EAIxC,MAAME,EAAkC,CACtC,eAAgB,KAAK,MAAM,SAAS,EAAE,EAAI,KAAK,OAAA,EAAS,SAAS,EAAE,EAAE,UAAU,CAAC,CAAA,EASlF,GALIF,EAAY,SACd,OAAO,OAAOE,EAASF,EAAY,OAAO,EAIxCD,EAAQ,QAAS,CACnB,MAAMI,EAAkBJ,EAAQ,QAChC,SAAW,CAACK,EAAKC,CAAK,IAAK,OAAO,QAAQF,CAAe,GAEnDC,EAAI,YAAA,IAAkB,aAAe,CAAC,KAAK,UAC7CF,EAAQE,CAAG,EAAIC,EAGrB,CAEA,OAAI,KAAK,SACPH,EAAQ,WAAW,EAAI,KAAK,QAG1B,KAAK,YACPA,EAAQ,cAAc,EAAI,KAAK,WAG1B,CACL,GAAGH,EACH,GAAGC,EACH,QAAAE,CAAA,CAEJ,CAGQ,kBACNI,EACAC,EAAkB,GAClBC,EACAC,EACAC,EACAC,EACAC,EACAC,EACAC,EACAC,EACAC,EACa,CACb,MAAMC,EAAuB,CAC3B,SAAU,CACR,CAAE,KAAM,OAAQ,QAASX,CAAA,CAAQ,EAEnC,OAAAC,CAAA,EAEF,OAAIC,GAAWA,EAAQ,OAAS,IAC9BS,EAAQ,SAAWT,GAEjBC,IACFQ,EAAQ,UAAYR,GAElBC,IACFO,EAAQ,YAAcP,GAEpBC,IACFM,EAAQ,aAAeN,GAErBC,IACFK,EAAQ,SAAWL,GAEjBC,IAAgB,SAClBI,EAAQ,aAAeJ,GAErBC,IACFG,EAAQ,UAAYH,GAElBC,IACFE,EAAQ,gBAAkBF,GAExBC,IACFC,EAAQ,gBAAkBD,GAErBC,CACT,CAEA,MAAc,WACZX,EACAC,EAAkB,GAClBC,EACAC,EACAC,EACAC,EACAC,EACAC,EACAC,EACAC,EACAC,EACgC,SAChC,GAAI,CAEF,MAAME,EAAa,IAAI,gBACjBC,EAAY,WAAW,IAAMD,EAAW,MAAA,EAAS,GAAK,EAEtD3B,EAAW,MAAM,MAAM,GAAG,KAAK,MAAM,WAAY,CACrD,GAAG,KAAK,gBAAgB,CACtB,OAAQ,OACR,QAAS,CACP,eAAgB,mBAChB,OAAUgB,EAAS,oBAAsB,kBAAA,EAE3C,KAAM,KAAK,UAAU,KAAK,kBACxBD,EACAC,EACAC,EACAC,EACAC,EACAC,EACAC,EACAC,EACAC,EACAC,EACAC,CAAA,CACD,CAAA,CACF,EACD,OAAQE,EAAW,MAAA,CACpB,EAID,GAFA,aAAaC,CAAS,EAElB,CAAC5B,EAAS,GAAI,CAChB,MAAME,EAAY,MAAMF,EAAS,KAAA,EACjC,MAAM,IAAI,MAAM,gCAAgCA,EAAS,MAAM,IAAIE,CAAS,EAAE,CAChF,CAEA,GAAI,CAACc,EAAQ,CAEX,MAAMa,EAAO,MAAM7B,EAAS,KAAA,EACxB6B,EAAK,WACP,KAAM,CACJ,KAAMA,EAAK,SACX,KAAM,GACN,MAAOA,EAAK,MACZ,YAAaA,EAAK,YAAA,GAGtB,MACF,CAEA,MAAMC,GAASrC,EAAAO,EAAS,OAAT,YAAAP,EAAe,YAC9B,GAAI,CAACqC,EAAQ,MAAM,IAAI,MAAM,qBAAqB,EAElD,MAAMC,EAAU,IAAI,YACpB,IAAIC,EAAS,GACTC,EAAqB,GAEzB,GAAI,CACF,OAAa,CACX,KAAM,CAAE,KAAAC,EAAM,MAAApB,CAAA,EAAU,MAAMgB,EAAO,KAAA,EACrC,GAAII,EACF,MAGF,MAAMC,EAAQJ,EAAQ,OAAOjB,EAAO,CAAE,OAAQ,GAAM,EACpDkB,GAAUG,EAGV,IAAIC,EAAiB,EACjBC,EAEJ,MAAQA,EAAeL,EAAO,QAAQ;AAAA,EAAMI,CAAc,KAAO,IAAI,CACnE,MAAME,EAAON,EAAO,MAAMI,EAAgBC,CAAY,EAAE,KAAA,EAGxD,GAFAD,EAAiBC,EAAe,EAE5BC,GAAQA,EAAK,WAAW,QAAQ,EAAG,CACrC,MAAMC,EAAWD,EAAK,MAAM,CAAC,EAAE,KAAA,EAE/B,GAAI,CAACC,GAAYA,IAAa,SAAU,CACtC,KAAM,CAAE,KAAM,GAAI,KAAM,EAAA,EACxB,MACF,CAEA,GAAI,CACF,MAAMV,EAAO,KAAK,MAAMU,CAAQ,EAEhC,GAAIV,EAAK,MAAO,CAEd,MAAMzB,EAAkB,mBADHV,EAAAmC,EAAK,QAAL,YAAAnC,EAAY,UAAWmC,EAAK,OAAS,sBACL,GACrD,cAAQ,KAAK,eAAezB,CAAe,EAAE,EACvC,IAAI,MAAMA,CAAe,CACjC,CAIA,GAAIyB,EAAK,OAAS,GAAM,CACpBI,EAAqB,GACrB,KAAM,CACJ,KAAM,GACN,KAAM,GACN,MAAOJ,EAAK,MACZ,YAAaA,EAAK,cAAgBA,EAAK,YACvC,UAAWA,EAAK,SAAA,EAElB,MACJ,CAIA,MAAMW,EAAeX,EAAK,UAAY,GAGlCA,EAAK,cAAgB,SACvB,KAAM,CACJ,KAAM,GACN,KAAM,GACN,YAAaA,EAAK,YAClB,YAAaA,EAAK,aAAeA,EAAK,cAAgB,OACtD,YAAaA,EAAK,aAAe,CAAA,IAIjCW,GAAgBX,EAAK,SACvBI,EAAqB,GACrB,KAAM,CACJ,KAAMO,EACN,KAAMX,EAAK,MAAQ,GACnB,MAAOA,EAAK,MACZ,YAAaA,EAAK,cAAgBA,EAAK,YACvC,UAAWA,EAAK,SAAA,EAItB,OAASY,EAAiB,CAExB,QAAQ,KAAK,6EAA6E,EAC1F,QAAQ,KAAK,mCAAoCA,GAAA,YAAAA,EAAY,OAAO,EACpE,QAAQ,KAAK,gCAAiCF,GAAA,YAAAA,EAAU,MAAM,EAC9D,QAAQ,KAAK,mDAAoDA,GAAA,YAAAA,EAAU,UAAU,EAAG,IAAI,EAC5F,QAAQ,KAAK,kDAAmDA,GAAA,YAAAA,EAAU,UAAUA,EAAS,OAAS,IAAI,CAC5G,CACF,MAAWD,IAEPL,EAAqB,GACrB,KAAM,CAAE,KAAMK,EAAM,KAAM,EAAA,EAEhC,CAEAN,EAASA,EAAO,MAAMI,CAAc,EAEhCJ,EAAO,OAAS,MAClB,QAAQ,KAAK,6CAA6C,EAC1DA,EAASA,EAAO,MAAM,IAAO,EAEjC,CAEIC,IACF,KAAM,CAAE,KAAM,GAAI,KAAM,EAAA,EAG5B,QAAA,CACEH,EAAO,YAAA,CACT,CAEF,OAASvB,EAAY,CACnB,MAAIA,EAAM,OAAS,aACX,IAAI,MAAM,8DAA8D,EACrEA,EAAM,OAAS,aAAeA,EAAM,QAAQ,SAAS,iBAAiB,EACzE,IAAI,MAAM,yEAAyE,EAEnFA,CAEV,CACF,CAEA,MAAa,yBAAyBR,EAMnC,CAQD,MAAM2C,EAAkB3C,GAAa,KAAK,UAE1C,GAAI,CAAC2C,EACH,MAAM,IAAI,MAAM,yDAAyD,EAG3E,GAAI,CAAC,KAAK,OACR,MAAM,IAAI,MAAM,uDAAuD,EAGzE,MAAM/B,EAAkC,CACtC,eAAgB,mBAChB,eAAgB+B,EAChB,YAAa,KAAK,MAAA,EAGpB,GAAI,CACF,MAAM1C,EAAW,MAAM,MAAM,GAAG,KAAK,MAAM,uBAAuB0C,CAAe,GAAI,CACnF,GAAG,KAAK,gBAAgB,CACtB,OAAQ,SACR,QAAA/B,CAAA,CACD,CAAA,CACF,EAED,GAAI,CAACX,EAAS,GAAI,CAChB,MAAME,EAAY,MAAMF,EAAS,KAAA,EACjC,MAAM,IAAI,MAAM,yCAAyCA,EAAS,MAAM,IAAIE,CAAS,EAAE,CACzF,CAGA,OADe,MAAMF,EAAS,KAAA,CAGhC,OAASO,EAAY,CACnB,MAAIA,EAAM,OAAS,aAAeA,EAAM,QAAQ,SAAS,iBAAiB,EAClE,IAAI,MAAM,yEAAyE,EAEnFA,CAEV,CACF,CAEA,MAAa,4BAA4BR,EAAoBkB,EAQ1D,CAgBD,MAAMyB,EAAkB3C,GAAa,KAAK,UAE1C,GAAI,CAAC2C,EACH,MAAM,IAAI,MAAM,yDAAyD,EAG3E,GAAI,CAAC,KAAK,OACR,MAAM,IAAI,MAAM,+CAA+C,EAGjE,MAAM/B,EAAkC,CACtC,eAAgB,mBAChB,eAAgB+B,EAChB,YAAa,KAAK,MAAA,EAIdC,EAAe1B,GAAWA,EAAQ,OAAS,EAAI,aAAaA,EAAQ,KAAK,GAAG,CAAC,GAAK,GAClF2B,EAAM,GAAG,KAAK,MAAM,wBAAwBF,CAAe,GAAGC,CAAY,GAEhF,GAAI,CACF,MAAM3C,EAAW,MAAM,MAAM4C,EAAK,CAChC,GAAG,KAAK,gBAAgB,CACtB,OAAQ,SACR,QAAAjC,CAAA,CACD,CAAA,CACF,EAED,GAAI,CAACX,EAAS,GAAI,CAChB,MAAME,EAAY,MAAMF,EAAS,KAAA,EACjC,MAAM,IAAI,MAAM,kCAAkCA,EAAS,MAAM,IAAIE,CAAS,EAAE,CAClF,CAGA,OADe,MAAMF,EAAS,KAAA,CAGhC,OAASO,EAAY,CACnB,MAAIA,EAAM,OAAS,aAAeA,EAAM,QAAQ,SAAS,iBAAiB,EAClE,IAAI,MAAM,yEAAyE,EAEnFA,CAEV,CACF,CAUA,MAAa,aAAasC,EAAmB9C,EAAwC,CACnF,GAAI,CAAC,KAAK,OACR,MAAM,IAAI,MAAM,0CAA0C,EAG5D,MAAMY,EAAkC,CACtC,eAAgB,mBAChB,YAAa,KAAK,MAAA,EAGpB,GAAI,CACF,MAAMX,EAAW,MAAM,MAAM,GAAG,KAAK,MAAM,eAAgB,CACzD,GAAG,KAAK,gBAAgB,CACtB,OAAQ,OACR,QAAAW,EACA,KAAM,KAAK,UAAU,CACnB,WAAYkC,EACZ,WAAY9C,CAAA,CACb,CAAA,CACF,CAAA,CACF,EAED,GAAI,CAACC,EAAS,GAAI,CAChB,MAAME,EAAY,MAAMF,EAAS,KAAA,EACjC,MAAM,IAAI,MAAM,4BAA4BA,EAAS,MAAM,IAAIE,CAAS,EAAE,CAC5E,CAGA,OADe,MAAMF,EAAS,KAAA,CAGhC,OAASO,EAAY,CACnB,MAAIA,EAAM,OAAS,aAAeA,EAAM,QAAQ,SAAS,iBAAiB,EAClE,IAAI,MAAM,yEAAyE,EAEnFA,CAEV,CACF,CASA,MAAa,cAAcW,EAAuC,CAChE,GAAI,CAAC,KAAK,OACR,MAAM,IAAI,MAAM,6CAA6C,EAG/D,MAAMP,EAAkC,CACtC,YAAa,KAAK,MAAA,EAGpB,GAAI,CACF,MAAMX,EAAW,MAAM,MAAM,GAAG,KAAK,MAAM,gBAAgBkB,CAAQ,GAAI,CACrE,GAAG,KAAK,gBAAgB,CACtB,OAAQ,MACR,QAAAP,CAAA,CACD,CAAA,CACF,EAED,GAAI,CAACX,EAAS,GAAI,CAChB,MAAME,EAAY,MAAMF,EAAS,KAAA,EACjC,MAAM,IAAI,MAAM,8BAA8BA,EAAS,MAAM,IAAIE,CAAS,EAAE,CAC9E,CAGA,OADe,MAAMF,EAAS,KAAA,CAGhC,OAASO,EAAY,CACnB,MAAIA,EAAM,OAAS,aAAeA,EAAM,QAAQ,SAAS,iBAAiB,EAClE,IAAI,MAAM,yEAAyE,EAEnFA,CAEV,CACF,CASA,MAAa,aAAaW,EAAmF,CAC3G,GAAI,CAAC,KAAK,OACR,MAAM,IAAI,MAAM,0CAA0C,EAG5D,MAAMP,EAAkC,CACtC,YAAa,KAAK,MAAA,EAGpB,GAAI,CACF,MAAMX,EAAW,MAAM,MAAM,GAAG,KAAK,MAAM,gBAAgBkB,CAAQ,GAAI,CACrE,GAAG,KAAK,gBAAgB,CACtB,OAAQ,SACR,QAAAP,CAAA,CACD,CAAA,CACF,EAED,GAAI,CAACX,EAAS,GAAI,CAChB,MAAME,EAAY,MAAMF,EAAS,KAAA,EACjC,MAAM,IAAI,MAAM,4BAA4BA,EAAS,MAAM,IAAIE,CAAS,EAAE,CAC5E,CAGA,OADe,MAAMF,EAAS,KAAA,CAGhC,OAASO,EAAY,CACnB,MAAIA,EAAM,OAAS,aAAeA,EAAM,QAAQ,SAAS,iBAAiB,EAClE,IAAI,MAAM,yEAAyE,EAEnFA,CAEV,CACF,CASA,MAAa,WAAWuC,EAAyC,CAC/D,GAAI,CAAC,KAAK,OACR,MAAM,IAAI,MAAM,qCAAqC,EAGvD,MAAMC,EAAW,IAAI,SACrBA,EAAS,OAAO,OAAQD,CAAI,EAE5B,GAAI,CACF,MAAM9C,EAAW,MAAM,MAAM,GAAG,KAAK,MAAM,oBAAqB,CAC9D,GAAG,KAAK,gBAAgB,CACtB,OAAQ,OACR,KAAM+C,CAAA,CACP,CAAA,CACF,EAED,GAAI,CAAC/C,EAAS,GAAI,CAChB,MAAME,EAAY,MAAMF,EAAS,KAAA,EACjC,MAAM,IAAI,MAAM,0BAA0BA,EAAS,MAAM,IAAIE,CAAS,EAAE,CAC1E,CAEA,OAAO,MAAMF,EAAS,KAAA,CACxB,OAASO,EAAY,CACnB,MAAIA,EAAM,OAAS,aAAeA,EAAM,QAAQ,SAAS,iBAAiB,EAClE,IAAI,MAAM,yEAAyE,EAEnFA,CAEV,CACF,CAQA,MAAa,WAAiC,CAC5C,GAAI,CAAC,KAAK,OACR,MAAM,IAAI,MAAM,uCAAuC,EAGzD,GAAI,CACF,MAAMP,EAAW,MAAM,MAAM,GAAG,KAAK,MAAM,aAAc,CACvD,GAAG,KAAK,gBAAgB,CACtB,OAAQ,KAAA,CACT,CAAA,CACF,EAED,GAAI,CAACA,EAAS,GAAI,CAChB,MAAME,EAAY,MAAMF,EAAS,KAAA,EACjC,MAAM,IAAI,MAAM,yBAAyBA,EAAS,MAAM,IAAIE,CAAS,EAAE,CACzE,CAEA,OAAO,MAAMF,EAAS,KAAA,CACxB,OAASO,EAAY,CACnB,MAAIA,EAAM,OAAS,aAAeA,EAAM,QAAQ,SAAS,iBAAiB,EAClE,IAAI,MAAM,yEAAyE,EAEnFA,CAEV,CACF,CASA,MAAa,YAAYyC,EAAmC,CAC1D,GAAI,CAAC,KAAK,OACR,MAAM,IAAI,MAAM,2CAA2C,EAG7D,GAAI,CACF,MAAMhD,EAAW,MAAM,MAAM,GAAG,KAAK,MAAM,cAAcgD,CAAM,GAAI,CACjE,GAAG,KAAK,gBAAgB,CACtB,OAAQ,KAAA,CACT,CAAA,CACF,EAED,GAAI,CAAChD,EAAS,GAAI,CAChB,MAAME,EAAY,MAAMF,EAAS,KAAA,EACjC,MAAM,IAAI,MAAM,4BAA4BA,EAAS,MAAM,IAAIE,CAAS,EAAE,CAC5E,CAEA,OAAO,MAAMF,EAAS,KAAA,CACxB,OAASO,EAAY,CACnB,MAAIA,EAAM,OAAS,aAAeA,EAAM,QAAQ,SAAS,iBAAiB,EAClE,IAAI,MAAM,yEAAyE,EAEnFA,CAEV,CACF,CAWA,MAAa,UAAUyC,EAAgBC,EAAeC,EAAqB,GAAgC,CACzG,GAAI,CAAC,KAAK,OACR,MAAM,IAAI,MAAM,wCAAwC,EAG1D,GAAI,CACF,MAAMlD,EAAW,MAAM,MAAM,GAAG,KAAK,MAAM,cAAcgD,CAAM,SAAU,CACvE,GAAG,KAAK,gBAAgB,CACtB,OAAQ,OACR,QAAS,CACP,eAAgB,kBAAA,EAElB,KAAM,KAAK,UAAU,CAAE,MAAAC,EAAO,YAAaC,EAAY,CAAA,CACxD,CAAA,CACF,EAED,GAAI,CAAClD,EAAS,GAAI,CAChB,MAAME,EAAY,MAAMF,EAAS,KAAA,EACjC,MAAM,IAAI,MAAM,yBAAyBA,EAAS,MAAM,IAAIE,CAAS,EAAE,CACzE,CAEA,OAAO,MAAMF,EAAS,KAAA,CACxB,OAASO,EAAY,CACnB,MAAIA,EAAM,OAAS,aAAeA,EAAM,QAAQ,SAAS,iBAAiB,EAClE,IAAI,MAAM,yEAAyE,EAEnFA,CAEV,CACF,CASA,MAAa,WAAWyC,EAA+D,CACrF,GAAI,CAAC,KAAK,OACR,MAAM,IAAI,MAAM,wCAAwC,EAG1D,MAAMJ,EAAM,GAAG,KAAK,MAAM,cAAcI,CAAM,GACxCG,EAAe,KAAK,gBAAgB,CACxC,OAAQ,QAAA,CACT,EAED,GAAI,CACF,MAAMnD,EAAW,MAAM,MAAM4C,EAAKO,CAAY,EAE9C,GAAI,CAACnD,EAAS,GAAI,CAChB,MAAME,EAAY,MAAMF,EAAS,KAAA,EACjC,IAAII,EACJ,GAAI,CACF,MAAMC,EAAY,KAAK,MAAMH,CAAS,EACtCE,EAAkBC,EAAU,QAAUA,EAAU,SAAW,+BAA+BL,EAAS,MAAM,GAC3G,MAAQ,CACNI,EAAkB,+BAA+BJ,EAAS,MAAM,GAClE,CACA,cAAQ,KAAK,eAAeI,CAAe,EAAE,EACvC,IAAI,MAAMA,CAAe,CACjC,CAGA,OADe,MAAMJ,EAAS,KAAA,CAEhC,OAASO,EAAY,CAEnB,IAAIH,EAEJ,MAAIG,EAAM,OAAS,aAAeA,EAAM,QAAQ,SAAS,iBAAiB,EACxEH,EAAkB,0EACTG,EAAM,SAAW,CAACA,EAAM,QAAQ,SAAS,uBAAuB,EAEzEH,EAAkBG,EAAM,QAExBH,EAAkB,2CAGpB,QAAQ,KAAK,eAAeA,CAAe,EAAE,EACvC,IAAI,MAAMA,CAAe,CACjC,CACF,CACF,CAKA,IAAIgD,EAAkC,KAG/B,MAAMC,EAAe,CAACC,EAAgBC,EAAwB,KAAMxD,EAA2B,OAAe,CACnHqD,EAAgB,IAAIxD,EAAU,CAAE,OAAA0D,EAAQ,OAAAC,EAAQ,UAAAxD,EAAW,CAC7D,EAGA,eAAuByD,EACrBzC,EACAC,EAAkB,GAClBC,EACAC,EACAC,EACAC,EACAC,EACAC,EACAC,EACAC,EACAC,EACgC,CAChC,GAAI,CAAC2B,EACH,MAAM,IAAI,MAAM,qGAAqG,EAGvH,MAAOA,EAAc,WACnBrC,EACAC,EACAC,EACAC,EACAC,EACAC,EACAC,EACAC,EACAC,EACAC,EACAC,CAAA,CAEJ"}
|
|
1
|
+
{"version":3,"file":"api.cjs","sources":["../api.ts"],"sourcesContent":["// For Node.js environments, we can use http.Agent for connection pooling\nlet httpAgent: any = null;\nlet httpsAgent: any = null;\n\n// Initialize agents for connection pooling in Node.js environments\nif (typeof window === 'undefined') {\n // Lazy load to avoid including 'http' in browser bundles\n Promise.all([\n // @ts-expect-error - Dynamic import of Node.js built-in module (only available in Node.js runtime)\n import('http').catch(() => null),\n // @ts-expect-error - Dynamic import of Node.js built-in module (only available in Node.js runtime)\n import('https').catch(() => null)\n ]).then(([http, https]) => {\n if (http?.default?.Agent) {\n httpAgent = new http.default.Agent({ keepAlive: true });\n } else if (http?.Agent) {\n httpAgent = new http.Agent({ keepAlive: true });\n }\n \n if (https?.default?.Agent) {\n httpsAgent = new https.default.Agent({ keepAlive: true });\n } else if (https?.Agent) {\n httpsAgent = new https.Agent({ keepAlive: true });\n }\n }).catch(err => {\n // Silently fail - connection pooling is optional\n console.warn('Failed to initialize HTTP agents:', err.message);\n });\n}\n\n// Define the StreamResponse interface\nexport interface StreamResponse {\n text: string;\n done: boolean;\n audio?: string; // Optional base64-encoded audio data (TTS response) - full audio\n audioFormat?: string; // Audio format (mp3, wav, etc.)\n audio_chunk?: string; // Optional streaming audio chunk (base64-encoded)\n chunk_index?: number; // Index of the audio chunk for ordering\n threading?: { // Optional threading metadata\n supports_threading: boolean;\n message_id: string;\n session_id: string;\n };\n}\n\n// The server now returns this directly for non-streaming chat\nexport interface ChatResponse {\n response: string;\n sources?: any[];\n audio?: string; // Optional base64-encoded audio data (TTS response)\n audio_format?: string; // Audio format (mp3, wav, etc.)\n}\n\n// Thread-related interfaces\nexport interface ThreadInfo {\n thread_id: string;\n thread_session_id: string;\n parent_message_id: string;\n parent_session_id: string;\n adapter_name: string;\n created_at: string;\n expires_at: string;\n}\n\n// The request body for the /v1/chat endpoint\ninterface ChatRequest {\n messages: Array<{ role: string; content: string; }>;\n stream: boolean;\n file_ids?: string[]; // Optional list of file IDs for file context\n thread_id?: string; // Optional thread ID for follow-up questions\n audio_input?: string; // Optional base64-encoded audio data for STT\n audio_format?: string; // Optional audio format (mp3, wav, etc.)\n language?: string; // Optional language code for STT (e.g., \"en-US\")\n return_audio?: boolean; // Whether to return audio response (TTS)\n tts_voice?: string; // Voice for TTS (e.g., \"alloy\", \"echo\" for OpenAI)\n source_language?: string; // Source language for translation\n target_language?: string; // Target language for translation\n}\n\n// File-related interfaces\nexport interface FileUploadResponse {\n file_id: string;\n filename: string;\n mime_type: string;\n file_size: number;\n status: string;\n chunk_count: number;\n message: string;\n}\n\nexport interface FileInfo {\n file_id: string;\n filename: string;\n mime_type: string;\n file_size: number;\n upload_timestamp: string;\n processing_status: string;\n chunk_count: number;\n storage_type: string;\n}\n\nexport interface FileQueryRequest {\n query: string;\n max_results?: number;\n}\n\nexport interface FileQueryResponse {\n file_id: string;\n filename: string;\n results: Array<{\n content: string;\n metadata: {\n chunk_id: string;\n file_id: string;\n chunk_index: number;\n confidence: number;\n };\n }>;\n}\n\n// API key status interface\nexport interface ApiKeyStatus {\n exists: boolean;\n active: boolean;\n adapter_name?: string | null;\n client_name?: string | null;\n created_at?: string | number | null;\n system_prompt?: {\n id: string;\n exists: boolean;\n } | null;\n message?: string;\n}\n\n// Adapter information interface\nexport interface AdapterInfo {\n client_name: string;\n adapter_name: string;\n model: string | null;\n isFileSupported?: boolean;\n notes?: string | null;\n}\n\nexport class ApiClient {\n private readonly apiUrl: string;\n private readonly apiKey: string | null;\n private sessionId: string | null; // Session ID can be mutable\n\n constructor(config: { apiUrl: string; apiKey?: string | null; sessionId?: string | null }) {\n if (!config.apiUrl || typeof config.apiUrl !== 'string') {\n throw new Error('API URL must be a valid string');\n }\n if (config.apiKey !== undefined && config.apiKey !== null && typeof config.apiKey !== 'string') {\n throw new Error('API key must be a valid string or null');\n }\n if (config.sessionId !== undefined && config.sessionId !== null && typeof config.sessionId !== 'string') {\n throw new Error('Session ID must be a valid string or null');\n }\n \n this.apiUrl = config.apiUrl;\n this.apiKey = config.apiKey ?? null;\n this.sessionId = config.sessionId ?? null;\n }\n\n public setSessionId(sessionId: string | null): void {\n if (sessionId !== null && typeof sessionId !== 'string') {\n throw new Error('Session ID must be a valid string or null');\n }\n this.sessionId = sessionId;\n }\n\n public getSessionId(): string | null {\n return this.sessionId;\n }\n\n /**\n * Validate that the API key exists and is active.\n *\n * @returns Promise resolving to API key status information\n * @throws Error if API key is not provided, invalid, inactive, or validation fails\n */\n public async validateApiKey(): Promise<ApiKeyStatus> {\n if (!this.apiKey) {\n throw new Error('API key is required for validation');\n }\n\n try {\n const response = await fetch(`${this.apiUrl}/admin/api-keys/${this.apiKey}/status`, {\n ...this.getFetchOptions({\n method: 'GET'\n })\n }).catch((fetchError: any) => {\n // Catch network errors before they bubble up\n if (fetchError.name === 'TypeError' && fetchError.message.includes('Failed to fetch')) {\n throw new Error('Could not connect to the server. Please check if the server is running.');\n }\n throw fetchError;\n });\n\n if (!response.ok) {\n // Read error response body\n let errorText = '';\n try {\n errorText = await response.text();\n } catch {\n // If we can't read the body, fall back to status code\n errorText = `HTTP ${response.status}`;\n }\n\n let errorDetail: string;\n let friendlyMessage: string;\n\n try {\n const errorJson = JSON.parse(errorText);\n errorDetail = errorJson.detail || errorJson.message || errorText;\n } catch {\n // If parsing fails, use the error text or status code\n errorDetail = errorText || `HTTP ${response.status}`;\n }\n\n // Generate user-friendly error messages based on HTTP status code\n switch (response.status) {\n case 401:\n friendlyMessage = 'API key is invalid or expired';\n break;\n case 403:\n friendlyMessage = 'Access denied: API key does not have required permissions';\n break;\n case 404:\n friendlyMessage = 'API key not found';\n break;\n case 503:\n friendlyMessage = 'API key management is not available in inference-only mode';\n break;\n default:\n friendlyMessage = `Failed to validate API key: ${errorDetail}`;\n break;\n }\n\n // Throw error - will be logged in catch block to avoid duplicates\n throw new Error(friendlyMessage);\n }\n\n const status: ApiKeyStatus = await response.json();\n\n // Check if the key exists\n if (!status.exists) {\n const friendlyMessage = 'API key does not exist';\n // Throw error - will be logged in catch block to avoid duplicates\n throw new Error(friendlyMessage);\n }\n\n // Check if the key is active\n if (!status.active) {\n const friendlyMessage = 'API key is inactive';\n // Throw error - will be logged in catch block to avoid duplicates\n throw new Error(friendlyMessage);\n }\n\n return status;\n } catch (error: any) {\n // Extract user-friendly error message\n let friendlyMessage: string;\n\n if (error instanceof Error && error.message) {\n // If it's already a user-friendly Error from above, use it directly\n if (error.message.includes('API key') ||\n error.message.includes('Access denied') ||\n error.message.includes('invalid') ||\n error.message.includes('expired') ||\n error.message.includes('inactive') ||\n error.message.includes('not found') ||\n error.message.includes('Could not connect')) {\n friendlyMessage = error.message;\n } else {\n friendlyMessage = `API key validation failed: ${error.message}`;\n }\n } else if (error.name === 'TypeError' && error.message?.includes('Failed to fetch')) {\n friendlyMessage = 'Could not connect to the server. Please check if the server is running.';\n } else {\n friendlyMessage = 'API key validation failed. Please check your API key and try again.';\n }\n\n // Only log warning if it's not a network error (those are already logged by browser)\n // For validation errors, we log once with a friendly message\n // Note: Browser will still log HTTP errors (401, 404, etc.) - this is unavoidable\n console.warn(`[ApiClient] ${friendlyMessage}`);\n\n // Throw the friendly error message\n throw new Error(friendlyMessage);\n }\n }\n\n /**\n * Get adapter information for the current API key.\n *\n * Returns information about the adapter and model being used by the API key.\n * This is useful for displaying configuration details to users.\n *\n * @returns Promise resolving to adapter information\n * @throws Error if API key is not provided, invalid, disabled, or request fails\n */\n public async getAdapterInfo(): Promise<AdapterInfo> {\n if (!this.apiKey) {\n throw new Error('API key is required to get adapter information');\n }\n\n try {\n const response = await fetch(`${this.apiUrl}/admin/api-keys/info`, {\n ...this.getFetchOptions({\n method: 'GET'\n })\n }).catch((fetchError: any) => {\n // Catch network errors before they bubble up\n if (fetchError.name === 'TypeError' && fetchError.message.includes('Failed to fetch')) {\n throw new Error('Could not connect to the server. Please check if the server is running.');\n }\n throw fetchError;\n });\n\n if (!response.ok) {\n // Read error response body\n let errorText = '';\n try {\n errorText = await response.text();\n } catch {\n // If we can't read the body, fall back to status code\n errorText = `HTTP ${response.status}`;\n }\n\n let errorDetail: string;\n let friendlyMessage: string;\n\n try {\n const errorJson = JSON.parse(errorText);\n errorDetail = errorJson.detail || errorJson.message || errorText;\n } catch {\n // If parsing fails, use the error text or status code\n errorDetail = errorText || `HTTP ${response.status}`;\n }\n\n // Generate user-friendly error messages based on HTTP status code\n switch (response.status) {\n case 401:\n friendlyMessage = 'API key is invalid, disabled, or has no associated adapter';\n break;\n case 404:\n friendlyMessage = 'Adapter configuration not found';\n break;\n case 503:\n friendlyMessage = 'Service is not available';\n break;\n default:\n friendlyMessage = `Failed to get adapter info: ${errorDetail}`;\n break;\n }\n\n // Throw error - will be logged in catch block to avoid duplicates\n throw new Error(friendlyMessage);\n }\n\n const adapterInfo: AdapterInfo = await response.json();\n return adapterInfo;\n } catch (error: any) {\n // Extract user-friendly error message\n let friendlyMessage: string;\n\n if (error instanceof Error && error.message) {\n // If it's already a user-friendly Error from above, use it directly\n if (error.message.includes('API key') ||\n error.message.includes('Adapter') ||\n error.message.includes('invalid') ||\n error.message.includes('disabled') ||\n error.message.includes('not found') ||\n error.message.includes('Could not connect')) {\n friendlyMessage = error.message;\n } else {\n friendlyMessage = `Failed to get adapter info: ${error.message}`;\n }\n } else if (error.name === 'TypeError' && error.message?.includes('Failed to fetch')) {\n friendlyMessage = 'Could not connect to the server. Please check if the server is running.';\n } else {\n friendlyMessage = 'Failed to get adapter information. Please try again.';\n }\n\n console.warn(`[ApiClient] ${friendlyMessage}`);\n\n // Throw the friendly error message\n throw new Error(friendlyMessage);\n }\n }\n\n // Helper to get fetch options with connection pooling if available\n private getFetchOptions(options: RequestInit = {}): RequestInit {\n const baseOptions: RequestInit = {};\n \n // Environment-specific options\n if (typeof window === 'undefined') {\n // Node.js: Use connection pooling agent\n const isHttps = this.apiUrl.startsWith('https:');\n const agent = isHttps ? httpsAgent : httpAgent;\n if (agent) {\n (baseOptions as any).agent = agent;\n }\n } else {\n // Browser: Use keep-alive header\n baseOptions.headers = { 'Connection': 'keep-alive' };\n }\n\n // Common headers\n const headers: Record<string, string> = {\n 'X-Request-ID': Date.now().toString(36) + Math.random().toString(36).substring(2),\n };\n\n // Merge base options headers (for browser keep-alive)\n if (baseOptions.headers) {\n Object.assign(headers, baseOptions.headers);\n }\n\n // Merge original request headers (but don't overwrite API key)\n if (options.headers) {\n const incomingHeaders = options.headers as Record<string, string>;\n for (const [key, value] of Object.entries(incomingHeaders)) {\n // Don't overwrite X-API-Key if we have one\n if (key.toLowerCase() !== 'x-api-key' || !this.apiKey) {\n headers[key] = value;\n }\n }\n }\n\n if (this.apiKey) {\n headers['X-API-Key'] = this.apiKey;\n }\n\n if (this.sessionId) {\n headers['X-Session-ID'] = this.sessionId;\n }\n\n return {\n ...options,\n ...baseOptions,\n headers,\n };\n }\n\n // Create Chat request\n private createChatRequest(\n message: string, \n stream: boolean = true, \n fileIds?: string[],\n threadId?: string,\n audioInput?: string,\n audioFormat?: string,\n language?: string,\n returnAudio?: boolean,\n ttsVoice?: string,\n sourceLanguage?: string,\n targetLanguage?: string\n ): ChatRequest {\n const request: ChatRequest = {\n messages: [\n { role: \"user\", content: message }\n ],\n stream\n };\n if (fileIds && fileIds.length > 0) {\n request.file_ids = fileIds;\n }\n if (threadId) {\n request.thread_id = threadId;\n }\n if (audioInput) {\n request.audio_input = audioInput;\n }\n if (audioFormat) {\n request.audio_format = audioFormat;\n }\n if (language) {\n request.language = language;\n }\n if (returnAudio !== undefined) {\n request.return_audio = returnAudio;\n }\n if (ttsVoice) {\n request.tts_voice = ttsVoice;\n }\n if (sourceLanguage) {\n request.source_language = sourceLanguage;\n }\n if (targetLanguage) {\n request.target_language = targetLanguage;\n }\n return request;\n }\n\n public async *streamChat(\n message: string,\n stream: boolean = true,\n fileIds?: string[],\n threadId?: string,\n audioInput?: string,\n audioFormat?: string,\n language?: string,\n returnAudio?: boolean,\n ttsVoice?: string,\n sourceLanguage?: string,\n targetLanguage?: string\n ): AsyncGenerator<StreamResponse> {\n try {\n // Add timeout to the fetch request\n const controller = new AbortController();\n const timeoutId = setTimeout(() => controller.abort(), 60000); // 60 second timeout\n\n const response = await fetch(`${this.apiUrl}/v1/chat`, {\n ...this.getFetchOptions({\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n 'Accept': stream ? 'text/event-stream' : 'application/json'\n },\n body: JSON.stringify(this.createChatRequest(\n message, \n stream, \n fileIds,\n threadId,\n audioInput,\n audioFormat,\n language,\n returnAudio,\n ttsVoice,\n sourceLanguage,\n targetLanguage\n )),\n }),\n signal: controller.signal\n });\n\n clearTimeout(timeoutId);\n\n if (!response.ok) {\n const errorText = await response.text();\n throw new Error(`Network response was not ok: ${response.status} ${errorText}`);\n }\n\n if (!stream) {\n // Handle non-streaming response\n const data = await response.json() as ChatResponse;\n if (data.response) {\n yield {\n text: data.response,\n done: true,\n audio: data.audio,\n audioFormat: data.audio_format\n } as StreamResponse & { audio?: string; audioFormat?: string };\n }\n return;\n }\n \n const reader = response.body?.getReader();\n if (!reader) throw new Error('No reader available');\n\n const decoder = new TextDecoder();\n let buffer = '';\n let hasReceivedContent = false;\n\n try {\n while (true) {\n const { done, value } = await reader.read();\n if (done) {\n break;\n }\n\n const chunk = decoder.decode(value, { stream: true });\n buffer += chunk;\n \n // Process complete lines immediately as they arrive\n let lineStartIndex = 0;\n let newlineIndex;\n \n while ((newlineIndex = buffer.indexOf('\\n', lineStartIndex)) !== -1) {\n const line = buffer.slice(lineStartIndex, newlineIndex).trim();\n lineStartIndex = newlineIndex + 1;\n \n if (line && line.startsWith('data: ')) {\n const jsonText = line.slice(6).trim();\n \n if (!jsonText || jsonText === '[DONE]') {\n yield { text: '', done: true };\n return;\n }\n\n try {\n const data = JSON.parse(jsonText);\n\n if (data.error) {\n const errorMessage = data.error?.message || data.error || 'Unknown server error';\n const friendlyMessage = `Server error: ${errorMessage}`;\n console.warn(`[ApiClient] ${friendlyMessage}`);\n throw new Error(friendlyMessage);\n }\n\n // Check for done chunk first - it may not have a response field\n // This handles the final done chunk that contains threading metadata\n if (data.done === true) {\n hasReceivedContent = true;\n yield {\n text: '',\n done: true,\n audio: data.audio,\n audioFormat: data.audio_format || data.audioFormat,\n threading: data.threading // Pass through threading metadata\n };\n return;\n }\n\n // Note: Base64 audio filtering is handled by chatStore's sanitizeMessageContent\n // We keep response text as-is here and let the application layer decide\n const responseText = data.response || '';\n\n // Handle streaming audio chunks\n if (data.audio_chunk !== undefined) {\n yield {\n text: '',\n done: false,\n audio_chunk: data.audio_chunk,\n audioFormat: data.audioFormat || data.audio_format || 'opus',\n chunk_index: data.chunk_index ?? 0\n };\n }\n\n if (responseText || data.audio) {\n hasReceivedContent = true;\n yield {\n text: responseText,\n done: data.done || false,\n audio: data.audio,\n audioFormat: data.audio_format || data.audioFormat,\n threading: data.threading // Include threading if present\n };\n }\n\n } catch (parseError: any) {\n // Re-throw intentional errors (like moderation blocks) - don't swallow them\n if (parseError?.message?.startsWith('Server error:')) {\n throw parseError;\n }\n // Log JSON parse errors for debugging\n console.warn('[ApiClient] Unable to parse server response. This may be a temporary issue.');\n console.warn('[ApiClient] Parse error details:', parseError?.message);\n console.warn('[ApiClient] JSON text length:', jsonText?.length);\n console.warn('[ApiClient] JSON text preview (first 200 chars):', jsonText?.substring(0, 200));\n console.warn('[ApiClient] JSON text preview (last 200 chars):', jsonText?.substring(jsonText.length - 200));\n }\n } else if (line) {\n // Handle raw text chunks that are not in SSE format\n hasReceivedContent = true;\n yield { text: line, done: false };\n }\n }\n \n buffer = buffer.slice(lineStartIndex);\n\n if (buffer.length > 1000000) { // 1MB limit\n console.warn('[ApiClient] Buffer too large, truncating...');\n buffer = buffer.slice(-500000); // Keep last 500KB\n }\n }\n \n if (hasReceivedContent) {\n yield { text: '', done: true };\n }\n \n } finally {\n reader.releaseLock();\n }\n \n } catch (error: any) {\n if (error.name === 'AbortError') {\n throw new Error('Connection timed out. Please check if the server is running.');\n } else if (error.name === 'TypeError' && error.message.includes('Failed to fetch')) {\n throw new Error('Could not connect to the server. Please check if the server is running.');\n } else {\n throw error;\n }\n }\n }\n\n public async clearConversationHistory(sessionId?: string): Promise<{\n status: string;\n message: string;\n session_id: string;\n deleted_count: number;\n timestamp: string;\n }> {\n /**\n * Clear conversation history for a session.\n *\n * @param sessionId - Optional session ID to clear. If not provided, uses current session.\n * @returns Promise resolving to operation result\n * @throws Error if the operation fails\n */\n const targetSessionId = sessionId || this.sessionId;\n\n if (!targetSessionId) {\n throw new Error('No session ID provided and no current session available');\n }\n\n if (!this.apiKey) {\n throw new Error('API key is required for clearing conversation history');\n }\n\n const headers: Record<string, string> = {\n 'Content-Type': 'application/json',\n 'X-Session-ID': targetSessionId,\n 'X-API-Key': this.apiKey\n };\n\n try {\n const response = await fetch(`${this.apiUrl}/admin/chat-history/${targetSessionId}`, {\n ...this.getFetchOptions({\n method: 'DELETE',\n headers\n })\n });\n\n if (!response.ok) {\n const errorText = await response.text();\n throw new Error(`Failed to clear conversation history: ${response.status} ${errorText}`);\n }\n\n const result = await response.json();\n return result;\n\n } catch (error: any) {\n if (error.name === 'TypeError' && error.message.includes('Failed to fetch')) {\n throw new Error('Could not connect to the server. Please check if the server is running.');\n } else {\n throw error;\n }\n }\n }\n\n public async deleteConversationWithFiles(sessionId?: string, fileIds?: string[]): Promise<{\n status: string;\n message: string;\n session_id: string;\n deleted_messages: number;\n deleted_files: number;\n file_deletion_errors: string[] | null;\n timestamp: string;\n }> {\n /**\n * Delete a conversation and all associated files.\n *\n * This method performs a complete conversation deletion:\n * - Deletes each file provided in fileIds (metadata, content, and vector store chunks)\n * - Clears conversation history\n *\n * File tracking is managed by the frontend (localStorage). The backend is stateless\n * and requires fileIds to be provided explicitly.\n *\n * @param sessionId - Optional session ID to delete. If not provided, uses current session.\n * @param fileIds - Optional list of file IDs to delete (from conversation's attachedFiles)\n * @returns Promise resolving to deletion result with counts\n * @throws Error if the operation fails\n */\n const targetSessionId = sessionId || this.sessionId;\n\n if (!targetSessionId) {\n throw new Error('No session ID provided and no current session available');\n }\n\n if (!this.apiKey) {\n throw new Error('API key is required for deleting conversation');\n }\n\n const headers: Record<string, string> = {\n 'Content-Type': 'application/json',\n 'X-Session-ID': targetSessionId,\n 'X-API-Key': this.apiKey\n };\n\n // Build URL with file_ids query parameter\n const fileIdsParam = fileIds && fileIds.length > 0 ? `?file_ids=${fileIds.join(',')}` : '';\n const url = `${this.apiUrl}/admin/conversations/${targetSessionId}${fileIdsParam}`;\n\n try {\n const response = await fetch(url, {\n ...this.getFetchOptions({\n method: 'DELETE',\n headers\n })\n });\n\n if (!response.ok) {\n const errorText = await response.text();\n throw new Error(`Failed to delete conversation: ${response.status} ${errorText}`);\n }\n\n const result = await response.json();\n return result;\n\n } catch (error: any) {\n if (error.name === 'TypeError' && error.message.includes('Failed to fetch')) {\n throw new Error('Could not connect to the server. Please check if the server is running.');\n } else {\n throw error;\n }\n }\n }\n\n /**\n * Create a conversation thread from a parent message.\n *\n * @param messageId - ID of the parent message\n * @param sessionId - Session ID of the parent conversation\n * @returns Promise resolving to thread information\n * @throws Error if the operation fails\n */\n public async createThread(messageId: string, sessionId: string): Promise<ThreadInfo> {\n if (!this.apiKey) {\n throw new Error('API key is required for creating threads');\n }\n\n const headers: Record<string, string> = {\n 'Content-Type': 'application/json',\n 'X-API-Key': this.apiKey\n };\n\n try {\n const response = await fetch(`${this.apiUrl}/api/threads`, {\n ...this.getFetchOptions({\n method: 'POST',\n headers,\n body: JSON.stringify({\n message_id: messageId,\n session_id: sessionId\n })\n })\n });\n\n if (!response.ok) {\n const errorText = await response.text();\n throw new Error(`Failed to create thread: ${response.status} ${errorText}`);\n }\n\n const result = await response.json();\n return result;\n\n } catch (error: any) {\n if (error.name === 'TypeError' && error.message.includes('Failed to fetch')) {\n throw new Error('Could not connect to the server. Please check if the server is running.');\n } else {\n throw error;\n }\n }\n }\n\n /**\n * Get thread information by thread ID.\n *\n * @param threadId - Thread identifier\n * @returns Promise resolving to thread information\n * @throws Error if the operation fails\n */\n public async getThreadInfo(threadId: string): Promise<ThreadInfo> {\n if (!this.apiKey) {\n throw new Error('API key is required for getting thread info');\n }\n\n const headers: Record<string, string> = {\n 'X-API-Key': this.apiKey\n };\n\n try {\n const response = await fetch(`${this.apiUrl}/api/threads/${threadId}`, {\n ...this.getFetchOptions({\n method: 'GET',\n headers\n })\n });\n\n if (!response.ok) {\n const errorText = await response.text();\n throw new Error(`Failed to get thread info: ${response.status} ${errorText}`);\n }\n\n const result = await response.json();\n return result;\n\n } catch (error: any) {\n if (error.name === 'TypeError' && error.message.includes('Failed to fetch')) {\n throw new Error('Could not connect to the server. Please check if the server is running.');\n } else {\n throw error;\n }\n }\n }\n\n /**\n * Delete a thread and its associated dataset.\n *\n * @param threadId - Thread identifier\n * @returns Promise resolving to deletion result\n * @throws Error if the operation fails\n */\n public async deleteThread(threadId: string): Promise<{ status: string; message: string; thread_id: string }> {\n if (!this.apiKey) {\n throw new Error('API key is required for deleting threads');\n }\n\n const headers: Record<string, string> = {\n 'X-API-Key': this.apiKey\n };\n\n try {\n const response = await fetch(`${this.apiUrl}/api/threads/${threadId}`, {\n ...this.getFetchOptions({\n method: 'DELETE',\n headers\n })\n });\n\n if (!response.ok) {\n const errorText = await response.text();\n throw new Error(`Failed to delete thread: ${response.status} ${errorText}`);\n }\n\n const result = await response.json();\n return result;\n\n } catch (error: any) {\n if (error.name === 'TypeError' && error.message.includes('Failed to fetch')) {\n throw new Error('Could not connect to the server. Please check if the server is running.');\n } else {\n throw error;\n }\n }\n }\n\n /**\n * Upload a file for processing and indexing.\n *\n * @param file - The file to upload\n * @returns Promise resolving to upload response with file_id\n * @throws Error if upload fails\n */\n public async uploadFile(file: File): Promise<FileUploadResponse> {\n if (!this.apiKey) {\n throw new Error('API key is required for file upload');\n }\n\n const formData = new FormData();\n formData.append('file', file);\n\n try {\n const response = await fetch(`${this.apiUrl}/api/files/upload`, {\n ...this.getFetchOptions({\n method: 'POST',\n body: formData\n })\n });\n\n if (!response.ok) {\n const errorText = await response.text();\n throw new Error(`Failed to upload file: ${response.status} ${errorText}`);\n }\n\n return await response.json();\n } catch (error: any) {\n if (error.name === 'TypeError' && error.message.includes('Failed to fetch')) {\n throw new Error('Could not connect to the server. Please check if the server is running.');\n } else {\n throw error;\n }\n }\n }\n\n /**\n * List all files for the current API key.\n * \n * @returns Promise resolving to list of file information\n * @throws Error if request fails\n */\n public async listFiles(): Promise<FileInfo[]> {\n if (!this.apiKey) {\n throw new Error('API key is required for listing files');\n }\n\n try {\n const response = await fetch(`${this.apiUrl}/api/files`, {\n ...this.getFetchOptions({\n method: 'GET'\n })\n });\n\n if (!response.ok) {\n const errorText = await response.text();\n throw new Error(`Failed to list files: ${response.status} ${errorText}`);\n }\n\n return await response.json();\n } catch (error: any) {\n if (error.name === 'TypeError' && error.message.includes('Failed to fetch')) {\n throw new Error('Could not connect to the server. Please check if the server is running.');\n } else {\n throw error;\n }\n }\n }\n\n /**\n * Get information about a specific file.\n * \n * @param fileId - The file ID\n * @returns Promise resolving to file information\n * @throws Error if file not found or request fails\n */\n public async getFileInfo(fileId: string): Promise<FileInfo> {\n if (!this.apiKey) {\n throw new Error('API key is required for getting file info');\n }\n\n try {\n const response = await fetch(`${this.apiUrl}/api/files/${fileId}`, {\n ...this.getFetchOptions({\n method: 'GET'\n })\n });\n\n if (!response.ok) {\n const errorText = await response.text();\n throw new Error(`Failed to get file info: ${response.status} ${errorText}`);\n }\n\n return await response.json();\n } catch (error: any) {\n if (error.name === 'TypeError' && error.message.includes('Failed to fetch')) {\n throw new Error('Could not connect to the server. Please check if the server is running.');\n } else {\n throw error;\n }\n }\n }\n\n /**\n * Query a specific file using semantic search.\n * \n * @param fileId - The file ID\n * @param query - The search query\n * @param maxResults - Maximum number of results (default: 10)\n * @returns Promise resolving to query results\n * @throws Error if query fails\n */\n public async queryFile(fileId: string, query: string, maxResults: number = 10): Promise<FileQueryResponse> {\n if (!this.apiKey) {\n throw new Error('API key is required for querying files');\n }\n\n try {\n const response = await fetch(`${this.apiUrl}/api/files/${fileId}/query`, {\n ...this.getFetchOptions({\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json'\n },\n body: JSON.stringify({ query, max_results: maxResults })\n })\n });\n\n if (!response.ok) {\n const errorText = await response.text();\n throw new Error(`Failed to query file: ${response.status} ${errorText}`);\n }\n\n return await response.json();\n } catch (error: any) {\n if (error.name === 'TypeError' && error.message.includes('Failed to fetch')) {\n throw new Error('Could not connect to the server. Please check if the server is running.');\n } else {\n throw error;\n }\n }\n }\n\n /**\n * Delete a specific file.\n * \n * @param fileId - The file ID\n * @returns Promise resolving to deletion result\n * @throws Error if deletion fails\n */\n public async deleteFile(fileId: string): Promise<{ message: string; file_id: string }> {\n if (!this.apiKey) {\n throw new Error('API key is required for deleting files');\n }\n\n const url = `${this.apiUrl}/api/files/${fileId}`;\n const fetchOptions = this.getFetchOptions({\n method: 'DELETE'\n });\n\n try {\n const response = await fetch(url, fetchOptions);\n\n if (!response.ok) {\n const errorText = await response.text();\n let friendlyMessage: string;\n try {\n const errorJson = JSON.parse(errorText);\n friendlyMessage = errorJson.detail || errorJson.message || `Failed to delete file (HTTP ${response.status})`;\n } catch {\n friendlyMessage = `Failed to delete file (HTTP ${response.status})`;\n }\n console.warn(`[ApiClient] ${friendlyMessage}`);\n throw new Error(friendlyMessage);\n }\n\n const result = await response.json();\n return result;\n } catch (error: any) {\n // Extract user-friendly error message\n let friendlyMessage: string;\n \n if (error.name === 'TypeError' && error.message.includes('Failed to fetch')) {\n friendlyMessage = 'Could not connect to the server. Please check if the server is running.';\n } else if (error.message && !error.message.includes('Failed to delete file')) {\n // Use existing message if it's already user-friendly\n friendlyMessage = error.message;\n } else {\n friendlyMessage = `Failed to delete file. Please try again.`;\n }\n \n console.warn(`[ApiClient] ${friendlyMessage}`);\n throw new Error(friendlyMessage);\n }\n }\n}\n\n// Legacy compatibility functions - these create a default client instance\n// These are kept for backward compatibility but should be deprecated in favor of the class-based approach\n\nlet defaultClient: ApiClient | null = null;\n\n// Configure the API with a custom URL, API key (optional), and session ID (optional)\nexport const configureApi = (apiUrl: string, apiKey: string | null = null, sessionId: string | null = null): void => {\n defaultClient = new ApiClient({ apiUrl, apiKey, sessionId });\n}\n\n// Legacy streamChat function that uses the default client\nexport async function* streamChat(\n message: string,\n stream: boolean = true,\n fileIds?: string[],\n threadId?: string,\n audioInput?: string,\n audioFormat?: string,\n language?: string,\n returnAudio?: boolean,\n ttsVoice?: string,\n sourceLanguage?: string,\n targetLanguage?: string\n): AsyncGenerator<StreamResponse> {\n if (!defaultClient) {\n throw new Error('API not configured. Please call configureApi() with your server URL before using any API functions.');\n }\n \n yield* defaultClient.streamChat(\n message, \n stream, \n fileIds,\n threadId,\n audioInput,\n audioFormat,\n language,\n returnAudio,\n ttsVoice,\n sourceLanguage,\n targetLanguage\n );\n}\n\n"],"names":["httpAgent","httpsAgent","http","https","_a","_b","err","ApiClient","config","__publicField","sessionId","response","fetchError","errorText","errorDetail","friendlyMessage","errorJson","status","error","options","baseOptions","agent","headers","incomingHeaders","key","value","message","stream","fileIds","threadId","audioInput","audioFormat","language","returnAudio","ttsVoice","sourceLanguage","targetLanguage","request","controller","timeoutId","data","reader","decoder","buffer","hasReceivedContent","done","chunk","lineStartIndex","newlineIndex","line","jsonText","responseText","parseError","_c","targetSessionId","fileIdsParam","url","messageId","file","formData","fileId","query","maxResults","fetchOptions","defaultClient","configureApi","apiUrl","apiKey","streamChat"],"mappings":"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"}
|
package/dist/api.d.ts
CHANGED
package/dist/api.mjs
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
var
|
|
2
|
-
var
|
|
3
|
-
var A = (l, r, e) =>
|
|
1
|
+
var _ = Object.defineProperty;
|
|
2
|
+
var D = (l, r, e) => r in l ? _(l, r, { enumerable: !0, configurable: !0, writable: !0, value: e }) : l[r] = e;
|
|
3
|
+
var A = (l, r, e) => D(l, typeof r != "symbol" ? r + "" : r, e);
|
|
4
4
|
let F = null, I = null;
|
|
5
5
|
typeof window > "u" && Promise.all([
|
|
6
6
|
// @ts-expect-error - Dynamic import of Node.js built-in module (only available in Node.js runtime)
|
|
@@ -13,7 +13,7 @@ typeof window > "u" && Promise.all([
|
|
|
13
13
|
}).catch((l) => {
|
|
14
14
|
console.warn("Failed to initialize HTTP agents:", l.message);
|
|
15
15
|
});
|
|
16
|
-
class
|
|
16
|
+
class j {
|
|
17
17
|
// Session ID can be mutable
|
|
18
18
|
constructor(r) {
|
|
19
19
|
A(this, "apiUrl");
|
|
@@ -62,8 +62,8 @@ class D {
|
|
|
62
62
|
}
|
|
63
63
|
let i, o;
|
|
64
64
|
try {
|
|
65
|
-
const
|
|
66
|
-
i =
|
|
65
|
+
const a = JSON.parse(t);
|
|
66
|
+
i = a.detail || a.message || t;
|
|
67
67
|
} catch {
|
|
68
68
|
i = t || `HTTP ${e.status}`;
|
|
69
69
|
}
|
|
@@ -131,8 +131,8 @@ class D {
|
|
|
131
131
|
}
|
|
132
132
|
let i, o;
|
|
133
133
|
try {
|
|
134
|
-
const
|
|
135
|
-
i =
|
|
134
|
+
const a = JSON.parse(t);
|
|
135
|
+
i = a.detail || a.message || t;
|
|
136
136
|
} catch {
|
|
137
137
|
i = t || `HTTP ${e.status}`;
|
|
138
138
|
}
|
|
@@ -181,19 +181,19 @@ class D {
|
|
|
181
181
|
};
|
|
182
182
|
}
|
|
183
183
|
// Create Chat request
|
|
184
|
-
createChatRequest(r, e = !0, s, t, i, o,
|
|
184
|
+
createChatRequest(r, e = !0, s, t, i, o, a, h, u, p, k) {
|
|
185
185
|
const c = {
|
|
186
186
|
messages: [
|
|
187
187
|
{ role: "user", content: r }
|
|
188
188
|
],
|
|
189
189
|
stream: e
|
|
190
190
|
};
|
|
191
|
-
return s && s.length > 0 && (c.file_ids = s), t && (c.thread_id = t), i && (c.audio_input = i), o && (c.audio_format = o),
|
|
191
|
+
return s && s.length > 0 && (c.file_ids = s), t && (c.thread_id = t), i && (c.audio_input = i), o && (c.audio_format = o), a && (c.language = a), h !== void 0 && (c.return_audio = h), u && (c.tts_voice = u), p && (c.source_language = p), k && (c.target_language = k), c;
|
|
192
192
|
}
|
|
193
|
-
async *streamChat(r, e = !0, s, t, i, o,
|
|
194
|
-
var c, C;
|
|
193
|
+
async *streamChat(r, e = !0, s, t, i, o, a, h, u, p, k) {
|
|
194
|
+
var c, C, x;
|
|
195
195
|
try {
|
|
196
|
-
const y = new AbortController(),
|
|
196
|
+
const y = new AbortController(), O = setTimeout(() => y.abort(), 6e4), g = await fetch(`${this.apiUrl}/v1/chat`, {
|
|
197
197
|
...this.getFetchOptions({
|
|
198
198
|
method: "POST",
|
|
199
199
|
headers: {
|
|
@@ -207,7 +207,7 @@ class D {
|
|
|
207
207
|
t,
|
|
208
208
|
i,
|
|
209
209
|
o,
|
|
210
|
-
|
|
210
|
+
a,
|
|
211
211
|
h,
|
|
212
212
|
u,
|
|
213
213
|
p,
|
|
@@ -216,7 +216,7 @@ class D {
|
|
|
216
216
|
}),
|
|
217
217
|
signal: y.signal
|
|
218
218
|
});
|
|
219
|
-
if (clearTimeout(
|
|
219
|
+
if (clearTimeout(O), !g.ok) {
|
|
220
220
|
const w = await g.text();
|
|
221
221
|
throw new Error(`Network response was not ok: ${g.status} ${w}`);
|
|
222
222
|
}
|
|
@@ -232,15 +232,15 @@ class D {
|
|
|
232
232
|
}
|
|
233
233
|
const P = (c = g.body) == null ? void 0 : c.getReader();
|
|
234
234
|
if (!P) throw new Error("No reader available");
|
|
235
|
-
const
|
|
235
|
+
const S = new TextDecoder();
|
|
236
236
|
let f = "", v = !1;
|
|
237
237
|
try {
|
|
238
238
|
for (; ; ) {
|
|
239
|
-
const { done: w, value:
|
|
239
|
+
const { done: w, value: U } = await P.read();
|
|
240
240
|
if (w)
|
|
241
241
|
break;
|
|
242
|
-
const
|
|
243
|
-
f +=
|
|
242
|
+
const q = S.decode(U, { stream: !0 });
|
|
243
|
+
f += q;
|
|
244
244
|
let E = 0, T;
|
|
245
245
|
for (; (T = f.indexOf(`
|
|
246
246
|
`, E)) !== -1; ) {
|
|
@@ -252,39 +252,41 @@ class D {
|
|
|
252
252
|
return;
|
|
253
253
|
}
|
|
254
254
|
try {
|
|
255
|
-
const
|
|
256
|
-
if (
|
|
257
|
-
const
|
|
258
|
-
throw console.warn(`[ApiClient] ${
|
|
255
|
+
const n = JSON.parse(d);
|
|
256
|
+
if (n.error) {
|
|
257
|
+
const K = `Server error: ${((C = n.error) == null ? void 0 : C.message) || n.error || "Unknown server error"}`;
|
|
258
|
+
throw console.warn(`[ApiClient] ${K}`), new Error(K);
|
|
259
259
|
}
|
|
260
|
-
if (
|
|
260
|
+
if (n.done === !0) {
|
|
261
261
|
v = !0, yield {
|
|
262
262
|
text: "",
|
|
263
263
|
done: !0,
|
|
264
|
-
audio:
|
|
265
|
-
audioFormat:
|
|
266
|
-
threading:
|
|
264
|
+
audio: n.audio,
|
|
265
|
+
audioFormat: n.audio_format || n.audioFormat,
|
|
266
|
+
threading: n.threading
|
|
267
267
|
// Pass through threading metadata
|
|
268
268
|
};
|
|
269
269
|
return;
|
|
270
270
|
}
|
|
271
|
-
const
|
|
272
|
-
|
|
271
|
+
const b = n.response || "";
|
|
272
|
+
n.audio_chunk !== void 0 && (yield {
|
|
273
273
|
text: "",
|
|
274
274
|
done: !1,
|
|
275
|
-
audio_chunk:
|
|
276
|
-
audioFormat:
|
|
277
|
-
chunk_index:
|
|
278
|
-
}), (
|
|
279
|
-
text:
|
|
280
|
-
done:
|
|
281
|
-
audio:
|
|
282
|
-
audioFormat:
|
|
283
|
-
threading:
|
|
275
|
+
audio_chunk: n.audio_chunk,
|
|
276
|
+
audioFormat: n.audioFormat || n.audio_format || "opus",
|
|
277
|
+
chunk_index: n.chunk_index ?? 0
|
|
278
|
+
}), (b || n.audio) && (v = !0, yield {
|
|
279
|
+
text: b,
|
|
280
|
+
done: n.done || !1,
|
|
281
|
+
audio: n.audio,
|
|
282
|
+
audioFormat: n.audio_format || n.audioFormat,
|
|
283
|
+
threading: n.threading
|
|
284
284
|
// Include threading if present
|
|
285
285
|
});
|
|
286
|
-
} catch (
|
|
287
|
-
|
|
286
|
+
} catch (n) {
|
|
287
|
+
if ((x = n == null ? void 0 : n.message) != null && x.startsWith("Server error:"))
|
|
288
|
+
throw n;
|
|
289
|
+
console.warn("[ApiClient] Unable to parse server response. This may be a temporary issue."), console.warn("[ApiClient] Parse error details:", n == null ? void 0 : n.message), console.warn("[ApiClient] JSON text length:", d == null ? void 0 : d.length), console.warn("[ApiClient] JSON text preview (first 200 chars):", d == null ? void 0 : d.substring(0, 200)), console.warn("[ApiClient] JSON text preview (last 200 chars):", d == null ? void 0 : d.substring(d.length - 200));
|
|
288
290
|
}
|
|
289
291
|
} else m && (v = !0, yield { text: m, done: !1 });
|
|
290
292
|
}
|
|
@@ -337,19 +339,19 @@ class D {
|
|
|
337
339
|
"X-API-Key": this.apiKey
|
|
338
340
|
}, i = e && e.length > 0 ? `?file_ids=${e.join(",")}` : "", o = `${this.apiUrl}/admin/conversations/${s}${i}`;
|
|
339
341
|
try {
|
|
340
|
-
const
|
|
342
|
+
const a = await fetch(o, {
|
|
341
343
|
...this.getFetchOptions({
|
|
342
344
|
method: "DELETE",
|
|
343
345
|
headers: t
|
|
344
346
|
})
|
|
345
347
|
});
|
|
346
|
-
if (!
|
|
347
|
-
const u = await
|
|
348
|
-
throw new Error(`Failed to delete conversation: ${
|
|
348
|
+
if (!a.ok) {
|
|
349
|
+
const u = await a.text();
|
|
350
|
+
throw new Error(`Failed to delete conversation: ${a.status} ${u}`);
|
|
349
351
|
}
|
|
350
|
-
return await
|
|
351
|
-
} catch (
|
|
352
|
-
throw
|
|
352
|
+
return await a.json();
|
|
353
|
+
} catch (a) {
|
|
354
|
+
throw a.name === "TypeError" && a.message.includes("Failed to fetch") ? new Error("Could not connect to the server. Please check if the server is running.") : a;
|
|
353
355
|
}
|
|
354
356
|
}
|
|
355
357
|
/**
|
|
@@ -570,14 +572,14 @@ class D {
|
|
|
570
572
|
const t = await fetch(e, s);
|
|
571
573
|
if (!t.ok) {
|
|
572
574
|
const o = await t.text();
|
|
573
|
-
let
|
|
575
|
+
let a;
|
|
574
576
|
try {
|
|
575
577
|
const h = JSON.parse(o);
|
|
576
|
-
|
|
578
|
+
a = h.detail || h.message || `Failed to delete file (HTTP ${t.status})`;
|
|
577
579
|
} catch {
|
|
578
|
-
|
|
580
|
+
a = `Failed to delete file (HTTP ${t.status})`;
|
|
579
581
|
}
|
|
580
|
-
throw console.warn(`[ApiClient] ${
|
|
582
|
+
throw console.warn(`[ApiClient] ${a}`), new Error(a);
|
|
581
583
|
}
|
|
582
584
|
return await t.json();
|
|
583
585
|
} catch (t) {
|
|
@@ -587,10 +589,10 @@ class D {
|
|
|
587
589
|
}
|
|
588
590
|
}
|
|
589
591
|
let $ = null;
|
|
590
|
-
const
|
|
591
|
-
$ = new
|
|
592
|
+
const M = (l, r = null, e = null) => {
|
|
593
|
+
$ = new j({ apiUrl: l, apiKey: r, sessionId: e });
|
|
592
594
|
};
|
|
593
|
-
async function*
|
|
595
|
+
async function* H(l, r = !0, e, s, t, i, o, a, h, u, p) {
|
|
594
596
|
if (!$)
|
|
595
597
|
throw new Error("API not configured. Please call configureApi() with your server URL before using any API functions.");
|
|
596
598
|
yield* $.streamChat(
|
|
@@ -601,15 +603,15 @@ async function* M(l, r = !0, e, s, t, i, o, n, h, u, p) {
|
|
|
601
603
|
t,
|
|
602
604
|
i,
|
|
603
605
|
o,
|
|
604
|
-
|
|
606
|
+
a,
|
|
605
607
|
h,
|
|
606
608
|
u,
|
|
607
609
|
p
|
|
608
610
|
);
|
|
609
611
|
}
|
|
610
612
|
export {
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
613
|
+
j as ApiClient,
|
|
614
|
+
M as configureApi,
|
|
615
|
+
H as streamChat
|
|
614
616
|
};
|
|
615
617
|
//# sourceMappingURL=api.mjs.map
|
package/dist/api.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"api.mjs","sources":["../api.ts"],"sourcesContent":["// For Node.js environments, we can use http.Agent for connection pooling\nlet httpAgent: any = null;\nlet httpsAgent: any = null;\n\n// Initialize agents for connection pooling in Node.js environments\nif (typeof window === 'undefined') {\n // Lazy load to avoid including 'http' in browser bundles\n Promise.all([\n // @ts-expect-error - Dynamic import of Node.js built-in module (only available in Node.js runtime)\n import('http').catch(() => null),\n // @ts-expect-error - Dynamic import of Node.js built-in module (only available in Node.js runtime)\n import('https').catch(() => null)\n ]).then(([http, https]) => {\n if (http?.default?.Agent) {\n httpAgent = new http.default.Agent({ keepAlive: true });\n } else if (http?.Agent) {\n httpAgent = new http.Agent({ keepAlive: true });\n }\n \n if (https?.default?.Agent) {\n httpsAgent = new https.default.Agent({ keepAlive: true });\n } else if (https?.Agent) {\n httpsAgent = new https.Agent({ keepAlive: true });\n }\n }).catch(err => {\n // Silently fail - connection pooling is optional\n console.warn('Failed to initialize HTTP agents:', err.message);\n });\n}\n\n// Define the StreamResponse interface\nexport interface StreamResponse {\n text: string;\n done: boolean;\n audio?: string; // Optional base64-encoded audio data (TTS response) - full audio\n audioFormat?: string; // Audio format (mp3, wav, etc.)\n audio_chunk?: string; // Optional streaming audio chunk (base64-encoded)\n chunk_index?: number; // Index of the audio chunk for ordering\n threading?: { // Optional threading metadata\n supports_threading: boolean;\n message_id: string;\n session_id: string;\n };\n}\n\n// The server now returns this directly for non-streaming chat\nexport interface ChatResponse {\n response: string;\n sources?: any[];\n audio?: string; // Optional base64-encoded audio data (TTS response)\n audio_format?: string; // Audio format (mp3, wav, etc.)\n}\n\n// Thread-related interfaces\nexport interface ThreadInfo {\n thread_id: string;\n thread_session_id: string;\n parent_message_id: string;\n parent_session_id: string;\n adapter_name: string;\n created_at: string;\n expires_at: string;\n}\n\n// The request body for the /v1/chat endpoint\ninterface ChatRequest {\n messages: Array<{ role: string; content: string; }>;\n stream: boolean;\n file_ids?: string[]; // Optional list of file IDs for file context\n thread_id?: string; // Optional thread ID for follow-up questions\n audio_input?: string; // Optional base64-encoded audio data for STT\n audio_format?: string; // Optional audio format (mp3, wav, etc.)\n language?: string; // Optional language code for STT (e.g., \"en-US\")\n return_audio?: boolean; // Whether to return audio response (TTS)\n tts_voice?: string; // Voice for TTS (e.g., \"alloy\", \"echo\" for OpenAI)\n source_language?: string; // Source language for translation\n target_language?: string; // Target language for translation\n}\n\n// File-related interfaces\nexport interface FileUploadResponse {\n file_id: string;\n filename: string;\n mime_type: string;\n file_size: number;\n status: string;\n chunk_count: number;\n message: string;\n}\n\nexport interface FileInfo {\n file_id: string;\n filename: string;\n mime_type: string;\n file_size: number;\n upload_timestamp: string;\n processing_status: string;\n chunk_count: number;\n storage_type: string;\n}\n\nexport interface FileQueryRequest {\n query: string;\n max_results?: number;\n}\n\nexport interface FileQueryResponse {\n file_id: string;\n filename: string;\n results: Array<{\n content: string;\n metadata: {\n chunk_id: string;\n file_id: string;\n chunk_index: number;\n confidence: number;\n };\n }>;\n}\n\n// API key status interface\nexport interface ApiKeyStatus {\n exists: boolean;\n active: boolean;\n adapter_name?: string | null;\n client_name?: string | null;\n created_at?: string | number | null;\n system_prompt?: {\n id: string;\n exists: boolean;\n } | null;\n message?: string;\n}\n\n// Adapter information interface\nexport interface AdapterInfo {\n client_name: string;\n adapter_name: string;\n model: string | null;\n isFileSupported?: boolean;\n}\n\nexport class ApiClient {\n private readonly apiUrl: string;\n private readonly apiKey: string | null;\n private sessionId: string | null; // Session ID can be mutable\n\n constructor(config: { apiUrl: string; apiKey?: string | null; sessionId?: string | null }) {\n if (!config.apiUrl || typeof config.apiUrl !== 'string') {\n throw new Error('API URL must be a valid string');\n }\n if (config.apiKey !== undefined && config.apiKey !== null && typeof config.apiKey !== 'string') {\n throw new Error('API key must be a valid string or null');\n }\n if (config.sessionId !== undefined && config.sessionId !== null && typeof config.sessionId !== 'string') {\n throw new Error('Session ID must be a valid string or null');\n }\n \n this.apiUrl = config.apiUrl;\n this.apiKey = config.apiKey ?? null;\n this.sessionId = config.sessionId ?? null;\n }\n\n public setSessionId(sessionId: string | null): void {\n if (sessionId !== null && typeof sessionId !== 'string') {\n throw new Error('Session ID must be a valid string or null');\n }\n this.sessionId = sessionId;\n }\n\n public getSessionId(): string | null {\n return this.sessionId;\n }\n\n /**\n * Validate that the API key exists and is active.\n *\n * @returns Promise resolving to API key status information\n * @throws Error if API key is not provided, invalid, inactive, or validation fails\n */\n public async validateApiKey(): Promise<ApiKeyStatus> {\n if (!this.apiKey) {\n throw new Error('API key is required for validation');\n }\n\n try {\n const response = await fetch(`${this.apiUrl}/admin/api-keys/${this.apiKey}/status`, {\n ...this.getFetchOptions({\n method: 'GET'\n })\n }).catch((fetchError: any) => {\n // Catch network errors before they bubble up\n if (fetchError.name === 'TypeError' && fetchError.message.includes('Failed to fetch')) {\n throw new Error('Could not connect to the server. Please check if the server is running.');\n }\n throw fetchError;\n });\n\n if (!response.ok) {\n // Read error response body\n let errorText = '';\n try {\n errorText = await response.text();\n } catch {\n // If we can't read the body, fall back to status code\n errorText = `HTTP ${response.status}`;\n }\n\n let errorDetail: string;\n let friendlyMessage: string;\n\n try {\n const errorJson = JSON.parse(errorText);\n errorDetail = errorJson.detail || errorJson.message || errorText;\n } catch {\n // If parsing fails, use the error text or status code\n errorDetail = errorText || `HTTP ${response.status}`;\n }\n\n // Generate user-friendly error messages based on HTTP status code\n switch (response.status) {\n case 401:\n friendlyMessage = 'API key is invalid or expired';\n break;\n case 403:\n friendlyMessage = 'Access denied: API key does not have required permissions';\n break;\n case 404:\n friendlyMessage = 'API key not found';\n break;\n case 503:\n friendlyMessage = 'API key management is not available in inference-only mode';\n break;\n default:\n friendlyMessage = `Failed to validate API key: ${errorDetail}`;\n break;\n }\n\n // Throw error - will be logged in catch block to avoid duplicates\n throw new Error(friendlyMessage);\n }\n\n const status: ApiKeyStatus = await response.json();\n\n // Check if the key exists\n if (!status.exists) {\n const friendlyMessage = 'API key does not exist';\n // Throw error - will be logged in catch block to avoid duplicates\n throw new Error(friendlyMessage);\n }\n\n // Check if the key is active\n if (!status.active) {\n const friendlyMessage = 'API key is inactive';\n // Throw error - will be logged in catch block to avoid duplicates\n throw new Error(friendlyMessage);\n }\n\n return status;\n } catch (error: any) {\n // Extract user-friendly error message\n let friendlyMessage: string;\n\n if (error instanceof Error && error.message) {\n // If it's already a user-friendly Error from above, use it directly\n if (error.message.includes('API key') ||\n error.message.includes('Access denied') ||\n error.message.includes('invalid') ||\n error.message.includes('expired') ||\n error.message.includes('inactive') ||\n error.message.includes('not found') ||\n error.message.includes('Could not connect')) {\n friendlyMessage = error.message;\n } else {\n friendlyMessage = `API key validation failed: ${error.message}`;\n }\n } else if (error.name === 'TypeError' && error.message?.includes('Failed to fetch')) {\n friendlyMessage = 'Could not connect to the server. Please check if the server is running.';\n } else {\n friendlyMessage = 'API key validation failed. Please check your API key and try again.';\n }\n\n // Only log warning if it's not a network error (those are already logged by browser)\n // For validation errors, we log once with a friendly message\n // Note: Browser will still log HTTP errors (401, 404, etc.) - this is unavoidable\n console.warn(`[ApiClient] ${friendlyMessage}`);\n\n // Throw the friendly error message\n throw new Error(friendlyMessage);\n }\n }\n\n /**\n * Get adapter information for the current API key.\n *\n * Returns information about the adapter and model being used by the API key.\n * This is useful for displaying configuration details to users.\n *\n * @returns Promise resolving to adapter information\n * @throws Error if API key is not provided, invalid, disabled, or request fails\n */\n public async getAdapterInfo(): Promise<AdapterInfo> {\n if (!this.apiKey) {\n throw new Error('API key is required to get adapter information');\n }\n\n try {\n const response = await fetch(`${this.apiUrl}/admin/api-keys/info`, {\n ...this.getFetchOptions({\n method: 'GET'\n })\n }).catch((fetchError: any) => {\n // Catch network errors before they bubble up\n if (fetchError.name === 'TypeError' && fetchError.message.includes('Failed to fetch')) {\n throw new Error('Could not connect to the server. Please check if the server is running.');\n }\n throw fetchError;\n });\n\n if (!response.ok) {\n // Read error response body\n let errorText = '';\n try {\n errorText = await response.text();\n } catch {\n // If we can't read the body, fall back to status code\n errorText = `HTTP ${response.status}`;\n }\n\n let errorDetail: string;\n let friendlyMessage: string;\n\n try {\n const errorJson = JSON.parse(errorText);\n errorDetail = errorJson.detail || errorJson.message || errorText;\n } catch {\n // If parsing fails, use the error text or status code\n errorDetail = errorText || `HTTP ${response.status}`;\n }\n\n // Generate user-friendly error messages based on HTTP status code\n switch (response.status) {\n case 401:\n friendlyMessage = 'API key is invalid, disabled, or has no associated adapter';\n break;\n case 404:\n friendlyMessage = 'Adapter configuration not found';\n break;\n case 503:\n friendlyMessage = 'Service is not available';\n break;\n default:\n friendlyMessage = `Failed to get adapter info: ${errorDetail}`;\n break;\n }\n\n // Throw error - will be logged in catch block to avoid duplicates\n throw new Error(friendlyMessage);\n }\n\n const adapterInfo: AdapterInfo = await response.json();\n return adapterInfo;\n } catch (error: any) {\n // Extract user-friendly error message\n let friendlyMessage: string;\n\n if (error instanceof Error && error.message) {\n // If it's already a user-friendly Error from above, use it directly\n if (error.message.includes('API key') ||\n error.message.includes('Adapter') ||\n error.message.includes('invalid') ||\n error.message.includes('disabled') ||\n error.message.includes('not found') ||\n error.message.includes('Could not connect')) {\n friendlyMessage = error.message;\n } else {\n friendlyMessage = `Failed to get adapter info: ${error.message}`;\n }\n } else if (error.name === 'TypeError' && error.message?.includes('Failed to fetch')) {\n friendlyMessage = 'Could not connect to the server. Please check if the server is running.';\n } else {\n friendlyMessage = 'Failed to get adapter information. Please try again.';\n }\n\n console.warn(`[ApiClient] ${friendlyMessage}`);\n\n // Throw the friendly error message\n throw new Error(friendlyMessage);\n }\n }\n\n // Helper to get fetch options with connection pooling if available\n private getFetchOptions(options: RequestInit = {}): RequestInit {\n const baseOptions: RequestInit = {};\n \n // Environment-specific options\n if (typeof window === 'undefined') {\n // Node.js: Use connection pooling agent\n const isHttps = this.apiUrl.startsWith('https:');\n const agent = isHttps ? httpsAgent : httpAgent;\n if (agent) {\n (baseOptions as any).agent = agent;\n }\n } else {\n // Browser: Use keep-alive header\n baseOptions.headers = { 'Connection': 'keep-alive' };\n }\n\n // Common headers\n const headers: Record<string, string> = {\n 'X-Request-ID': Date.now().toString(36) + Math.random().toString(36).substring(2),\n };\n\n // Merge base options headers (for browser keep-alive)\n if (baseOptions.headers) {\n Object.assign(headers, baseOptions.headers);\n }\n\n // Merge original request headers (but don't overwrite API key)\n if (options.headers) {\n const incomingHeaders = options.headers as Record<string, string>;\n for (const [key, value] of Object.entries(incomingHeaders)) {\n // Don't overwrite X-API-Key if we have one\n if (key.toLowerCase() !== 'x-api-key' || !this.apiKey) {\n headers[key] = value;\n }\n }\n }\n\n if (this.apiKey) {\n headers['X-API-Key'] = this.apiKey;\n }\n\n if (this.sessionId) {\n headers['X-Session-ID'] = this.sessionId;\n }\n\n return {\n ...options,\n ...baseOptions,\n headers,\n };\n }\n\n // Create Chat request\n private createChatRequest(\n message: string, \n stream: boolean = true, \n fileIds?: string[],\n threadId?: string,\n audioInput?: string,\n audioFormat?: string,\n language?: string,\n returnAudio?: boolean,\n ttsVoice?: string,\n sourceLanguage?: string,\n targetLanguage?: string\n ): ChatRequest {\n const request: ChatRequest = {\n messages: [\n { role: \"user\", content: message }\n ],\n stream\n };\n if (fileIds && fileIds.length > 0) {\n request.file_ids = fileIds;\n }\n if (threadId) {\n request.thread_id = threadId;\n }\n if (audioInput) {\n request.audio_input = audioInput;\n }\n if (audioFormat) {\n request.audio_format = audioFormat;\n }\n if (language) {\n request.language = language;\n }\n if (returnAudio !== undefined) {\n request.return_audio = returnAudio;\n }\n if (ttsVoice) {\n request.tts_voice = ttsVoice;\n }\n if (sourceLanguage) {\n request.source_language = sourceLanguage;\n }\n if (targetLanguage) {\n request.target_language = targetLanguage;\n }\n return request;\n }\n\n public async *streamChat(\n message: string,\n stream: boolean = true,\n fileIds?: string[],\n threadId?: string,\n audioInput?: string,\n audioFormat?: string,\n language?: string,\n returnAudio?: boolean,\n ttsVoice?: string,\n sourceLanguage?: string,\n targetLanguage?: string\n ): AsyncGenerator<StreamResponse> {\n try {\n // Add timeout to the fetch request\n const controller = new AbortController();\n const timeoutId = setTimeout(() => controller.abort(), 60000); // 60 second timeout\n\n const response = await fetch(`${this.apiUrl}/v1/chat`, {\n ...this.getFetchOptions({\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n 'Accept': stream ? 'text/event-stream' : 'application/json'\n },\n body: JSON.stringify(this.createChatRequest(\n message, \n stream, \n fileIds,\n threadId,\n audioInput,\n audioFormat,\n language,\n returnAudio,\n ttsVoice,\n sourceLanguage,\n targetLanguage\n )),\n }),\n signal: controller.signal\n });\n\n clearTimeout(timeoutId);\n\n if (!response.ok) {\n const errorText = await response.text();\n throw new Error(`Network response was not ok: ${response.status} ${errorText}`);\n }\n\n if (!stream) {\n // Handle non-streaming response\n const data = await response.json() as ChatResponse;\n if (data.response) {\n yield {\n text: data.response,\n done: true,\n audio: data.audio,\n audioFormat: data.audio_format\n } as StreamResponse & { audio?: string; audioFormat?: string };\n }\n return;\n }\n \n const reader = response.body?.getReader();\n if (!reader) throw new Error('No reader available');\n\n const decoder = new TextDecoder();\n let buffer = '';\n let hasReceivedContent = false;\n\n try {\n while (true) {\n const { done, value } = await reader.read();\n if (done) {\n break;\n }\n\n const chunk = decoder.decode(value, { stream: true });\n buffer += chunk;\n \n // Process complete lines immediately as they arrive\n let lineStartIndex = 0;\n let newlineIndex;\n \n while ((newlineIndex = buffer.indexOf('\\n', lineStartIndex)) !== -1) {\n const line = buffer.slice(lineStartIndex, newlineIndex).trim();\n lineStartIndex = newlineIndex + 1;\n \n if (line && line.startsWith('data: ')) {\n const jsonText = line.slice(6).trim();\n \n if (!jsonText || jsonText === '[DONE]') {\n yield { text: '', done: true };\n return;\n }\n\n try {\n const data = JSON.parse(jsonText);\n\n if (data.error) {\n const errorMessage = data.error?.message || data.error || 'Unknown server error';\n const friendlyMessage = `Server error: ${errorMessage}`;\n console.warn(`[ApiClient] ${friendlyMessage}`);\n throw new Error(friendlyMessage);\n }\n\n // Check for done chunk first - it may not have a response field\n // This handles the final done chunk that contains threading metadata\n if (data.done === true) {\n hasReceivedContent = true;\n yield {\n text: '',\n done: true,\n audio: data.audio,\n audioFormat: data.audio_format || data.audioFormat,\n threading: data.threading // Pass through threading metadata\n };\n return;\n }\n\n // Note: Base64 audio filtering is handled by chatStore's sanitizeMessageContent\n // We keep response text as-is here and let the application layer decide\n const responseText = data.response || '';\n\n // Handle streaming audio chunks\n if (data.audio_chunk !== undefined) {\n yield {\n text: '',\n done: false,\n audio_chunk: data.audio_chunk,\n audioFormat: data.audioFormat || data.audio_format || 'opus',\n chunk_index: data.chunk_index ?? 0\n };\n }\n\n if (responseText || data.audio) {\n hasReceivedContent = true;\n yield {\n text: responseText,\n done: data.done || false,\n audio: data.audio,\n audioFormat: data.audio_format || data.audioFormat,\n threading: data.threading // Include threading if present\n };\n }\n\n } catch (parseError: any) {\n // Log more details for debugging\n console.warn('[ApiClient] Unable to parse server response. This may be a temporary issue.');\n console.warn('[ApiClient] Parse error details:', parseError?.message);\n console.warn('[ApiClient] JSON text length:', jsonText?.length);\n console.warn('[ApiClient] JSON text preview (first 200 chars):', jsonText?.substring(0, 200));\n console.warn('[ApiClient] JSON text preview (last 200 chars):', jsonText?.substring(jsonText.length - 200));\n }\n } else if (line) {\n // Handle raw text chunks that are not in SSE format\n hasReceivedContent = true;\n yield { text: line, done: false };\n }\n }\n \n buffer = buffer.slice(lineStartIndex);\n\n if (buffer.length > 1000000) { // 1MB limit\n console.warn('[ApiClient] Buffer too large, truncating...');\n buffer = buffer.slice(-500000); // Keep last 500KB\n }\n }\n \n if (hasReceivedContent) {\n yield { text: '', done: true };\n }\n \n } finally {\n reader.releaseLock();\n }\n \n } catch (error: any) {\n if (error.name === 'AbortError') {\n throw new Error('Connection timed out. Please check if the server is running.');\n } else if (error.name === 'TypeError' && error.message.includes('Failed to fetch')) {\n throw new Error('Could not connect to the server. Please check if the server is running.');\n } else {\n throw error;\n }\n }\n }\n\n public async clearConversationHistory(sessionId?: string): Promise<{\n status: string;\n message: string;\n session_id: string;\n deleted_count: number;\n timestamp: string;\n }> {\n /**\n * Clear conversation history for a session.\n *\n * @param sessionId - Optional session ID to clear. If not provided, uses current session.\n * @returns Promise resolving to operation result\n * @throws Error if the operation fails\n */\n const targetSessionId = sessionId || this.sessionId;\n\n if (!targetSessionId) {\n throw new Error('No session ID provided and no current session available');\n }\n\n if (!this.apiKey) {\n throw new Error('API key is required for clearing conversation history');\n }\n\n const headers: Record<string, string> = {\n 'Content-Type': 'application/json',\n 'X-Session-ID': targetSessionId,\n 'X-API-Key': this.apiKey\n };\n\n try {\n const response = await fetch(`${this.apiUrl}/admin/chat-history/${targetSessionId}`, {\n ...this.getFetchOptions({\n method: 'DELETE',\n headers\n })\n });\n\n if (!response.ok) {\n const errorText = await response.text();\n throw new Error(`Failed to clear conversation history: ${response.status} ${errorText}`);\n }\n\n const result = await response.json();\n return result;\n\n } catch (error: any) {\n if (error.name === 'TypeError' && error.message.includes('Failed to fetch')) {\n throw new Error('Could not connect to the server. Please check if the server is running.');\n } else {\n throw error;\n }\n }\n }\n\n public async deleteConversationWithFiles(sessionId?: string, fileIds?: string[]): Promise<{\n status: string;\n message: string;\n session_id: string;\n deleted_messages: number;\n deleted_files: number;\n file_deletion_errors: string[] | null;\n timestamp: string;\n }> {\n /**\n * Delete a conversation and all associated files.\n *\n * This method performs a complete conversation deletion:\n * - Deletes each file provided in fileIds (metadata, content, and vector store chunks)\n * - Clears conversation history\n *\n * File tracking is managed by the frontend (localStorage). The backend is stateless\n * and requires fileIds to be provided explicitly.\n *\n * @param sessionId - Optional session ID to delete. If not provided, uses current session.\n * @param fileIds - Optional list of file IDs to delete (from conversation's attachedFiles)\n * @returns Promise resolving to deletion result with counts\n * @throws Error if the operation fails\n */\n const targetSessionId = sessionId || this.sessionId;\n\n if (!targetSessionId) {\n throw new Error('No session ID provided and no current session available');\n }\n\n if (!this.apiKey) {\n throw new Error('API key is required for deleting conversation');\n }\n\n const headers: Record<string, string> = {\n 'Content-Type': 'application/json',\n 'X-Session-ID': targetSessionId,\n 'X-API-Key': this.apiKey\n };\n\n // Build URL with file_ids query parameter\n const fileIdsParam = fileIds && fileIds.length > 0 ? `?file_ids=${fileIds.join(',')}` : '';\n const url = `${this.apiUrl}/admin/conversations/${targetSessionId}${fileIdsParam}`;\n\n try {\n const response = await fetch(url, {\n ...this.getFetchOptions({\n method: 'DELETE',\n headers\n })\n });\n\n if (!response.ok) {\n const errorText = await response.text();\n throw new Error(`Failed to delete conversation: ${response.status} ${errorText}`);\n }\n\n const result = await response.json();\n return result;\n\n } catch (error: any) {\n if (error.name === 'TypeError' && error.message.includes('Failed to fetch')) {\n throw new Error('Could not connect to the server. Please check if the server is running.');\n } else {\n throw error;\n }\n }\n }\n\n /**\n * Create a conversation thread from a parent message.\n *\n * @param messageId - ID of the parent message\n * @param sessionId - Session ID of the parent conversation\n * @returns Promise resolving to thread information\n * @throws Error if the operation fails\n */\n public async createThread(messageId: string, sessionId: string): Promise<ThreadInfo> {\n if (!this.apiKey) {\n throw new Error('API key is required for creating threads');\n }\n\n const headers: Record<string, string> = {\n 'Content-Type': 'application/json',\n 'X-API-Key': this.apiKey\n };\n\n try {\n const response = await fetch(`${this.apiUrl}/api/threads`, {\n ...this.getFetchOptions({\n method: 'POST',\n headers,\n body: JSON.stringify({\n message_id: messageId,\n session_id: sessionId\n })\n })\n });\n\n if (!response.ok) {\n const errorText = await response.text();\n throw new Error(`Failed to create thread: ${response.status} ${errorText}`);\n }\n\n const result = await response.json();\n return result;\n\n } catch (error: any) {\n if (error.name === 'TypeError' && error.message.includes('Failed to fetch')) {\n throw new Error('Could not connect to the server. Please check if the server is running.');\n } else {\n throw error;\n }\n }\n }\n\n /**\n * Get thread information by thread ID.\n *\n * @param threadId - Thread identifier\n * @returns Promise resolving to thread information\n * @throws Error if the operation fails\n */\n public async getThreadInfo(threadId: string): Promise<ThreadInfo> {\n if (!this.apiKey) {\n throw new Error('API key is required for getting thread info');\n }\n\n const headers: Record<string, string> = {\n 'X-API-Key': this.apiKey\n };\n\n try {\n const response = await fetch(`${this.apiUrl}/api/threads/${threadId}`, {\n ...this.getFetchOptions({\n method: 'GET',\n headers\n })\n });\n\n if (!response.ok) {\n const errorText = await response.text();\n throw new Error(`Failed to get thread info: ${response.status} ${errorText}`);\n }\n\n const result = await response.json();\n return result;\n\n } catch (error: any) {\n if (error.name === 'TypeError' && error.message.includes('Failed to fetch')) {\n throw new Error('Could not connect to the server. Please check if the server is running.');\n } else {\n throw error;\n }\n }\n }\n\n /**\n * Delete a thread and its associated dataset.\n *\n * @param threadId - Thread identifier\n * @returns Promise resolving to deletion result\n * @throws Error if the operation fails\n */\n public async deleteThread(threadId: string): Promise<{ status: string; message: string; thread_id: string }> {\n if (!this.apiKey) {\n throw new Error('API key is required for deleting threads');\n }\n\n const headers: Record<string, string> = {\n 'X-API-Key': this.apiKey\n };\n\n try {\n const response = await fetch(`${this.apiUrl}/api/threads/${threadId}`, {\n ...this.getFetchOptions({\n method: 'DELETE',\n headers\n })\n });\n\n if (!response.ok) {\n const errorText = await response.text();\n throw new Error(`Failed to delete thread: ${response.status} ${errorText}`);\n }\n\n const result = await response.json();\n return result;\n\n } catch (error: any) {\n if (error.name === 'TypeError' && error.message.includes('Failed to fetch')) {\n throw new Error('Could not connect to the server. Please check if the server is running.');\n } else {\n throw error;\n }\n }\n }\n\n /**\n * Upload a file for processing and indexing.\n *\n * @param file - The file to upload\n * @returns Promise resolving to upload response with file_id\n * @throws Error if upload fails\n */\n public async uploadFile(file: File): Promise<FileUploadResponse> {\n if (!this.apiKey) {\n throw new Error('API key is required for file upload');\n }\n\n const formData = new FormData();\n formData.append('file', file);\n\n try {\n const response = await fetch(`${this.apiUrl}/api/files/upload`, {\n ...this.getFetchOptions({\n method: 'POST',\n body: formData\n })\n });\n\n if (!response.ok) {\n const errorText = await response.text();\n throw new Error(`Failed to upload file: ${response.status} ${errorText}`);\n }\n\n return await response.json();\n } catch (error: any) {\n if (error.name === 'TypeError' && error.message.includes('Failed to fetch')) {\n throw new Error('Could not connect to the server. Please check if the server is running.');\n } else {\n throw error;\n }\n }\n }\n\n /**\n * List all files for the current API key.\n * \n * @returns Promise resolving to list of file information\n * @throws Error if request fails\n */\n public async listFiles(): Promise<FileInfo[]> {\n if (!this.apiKey) {\n throw new Error('API key is required for listing files');\n }\n\n try {\n const response = await fetch(`${this.apiUrl}/api/files`, {\n ...this.getFetchOptions({\n method: 'GET'\n })\n });\n\n if (!response.ok) {\n const errorText = await response.text();\n throw new Error(`Failed to list files: ${response.status} ${errorText}`);\n }\n\n return await response.json();\n } catch (error: any) {\n if (error.name === 'TypeError' && error.message.includes('Failed to fetch')) {\n throw new Error('Could not connect to the server. Please check if the server is running.');\n } else {\n throw error;\n }\n }\n }\n\n /**\n * Get information about a specific file.\n * \n * @param fileId - The file ID\n * @returns Promise resolving to file information\n * @throws Error if file not found or request fails\n */\n public async getFileInfo(fileId: string): Promise<FileInfo> {\n if (!this.apiKey) {\n throw new Error('API key is required for getting file info');\n }\n\n try {\n const response = await fetch(`${this.apiUrl}/api/files/${fileId}`, {\n ...this.getFetchOptions({\n method: 'GET'\n })\n });\n\n if (!response.ok) {\n const errorText = await response.text();\n throw new Error(`Failed to get file info: ${response.status} ${errorText}`);\n }\n\n return await response.json();\n } catch (error: any) {\n if (error.name === 'TypeError' && error.message.includes('Failed to fetch')) {\n throw new Error('Could not connect to the server. Please check if the server is running.');\n } else {\n throw error;\n }\n }\n }\n\n /**\n * Query a specific file using semantic search.\n * \n * @param fileId - The file ID\n * @param query - The search query\n * @param maxResults - Maximum number of results (default: 10)\n * @returns Promise resolving to query results\n * @throws Error if query fails\n */\n public async queryFile(fileId: string, query: string, maxResults: number = 10): Promise<FileQueryResponse> {\n if (!this.apiKey) {\n throw new Error('API key is required for querying files');\n }\n\n try {\n const response = await fetch(`${this.apiUrl}/api/files/${fileId}/query`, {\n ...this.getFetchOptions({\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json'\n },\n body: JSON.stringify({ query, max_results: maxResults })\n })\n });\n\n if (!response.ok) {\n const errorText = await response.text();\n throw new Error(`Failed to query file: ${response.status} ${errorText}`);\n }\n\n return await response.json();\n } catch (error: any) {\n if (error.name === 'TypeError' && error.message.includes('Failed to fetch')) {\n throw new Error('Could not connect to the server. Please check if the server is running.');\n } else {\n throw error;\n }\n }\n }\n\n /**\n * Delete a specific file.\n * \n * @param fileId - The file ID\n * @returns Promise resolving to deletion result\n * @throws Error if deletion fails\n */\n public async deleteFile(fileId: string): Promise<{ message: string; file_id: string }> {\n if (!this.apiKey) {\n throw new Error('API key is required for deleting files');\n }\n\n const url = `${this.apiUrl}/api/files/${fileId}`;\n const fetchOptions = this.getFetchOptions({\n method: 'DELETE'\n });\n\n try {\n const response = await fetch(url, fetchOptions);\n\n if (!response.ok) {\n const errorText = await response.text();\n let friendlyMessage: string;\n try {\n const errorJson = JSON.parse(errorText);\n friendlyMessage = errorJson.detail || errorJson.message || `Failed to delete file (HTTP ${response.status})`;\n } catch {\n friendlyMessage = `Failed to delete file (HTTP ${response.status})`;\n }\n console.warn(`[ApiClient] ${friendlyMessage}`);\n throw new Error(friendlyMessage);\n }\n\n const result = await response.json();\n return result;\n } catch (error: any) {\n // Extract user-friendly error message\n let friendlyMessage: string;\n \n if (error.name === 'TypeError' && error.message.includes('Failed to fetch')) {\n friendlyMessage = 'Could not connect to the server. Please check if the server is running.';\n } else if (error.message && !error.message.includes('Failed to delete file')) {\n // Use existing message if it's already user-friendly\n friendlyMessage = error.message;\n } else {\n friendlyMessage = `Failed to delete file. Please try again.`;\n }\n \n console.warn(`[ApiClient] ${friendlyMessage}`);\n throw new Error(friendlyMessage);\n }\n }\n}\n\n// Legacy compatibility functions - these create a default client instance\n// These are kept for backward compatibility but should be deprecated in favor of the class-based approach\n\nlet defaultClient: ApiClient | null = null;\n\n// Configure the API with a custom URL, API key (optional), and session ID (optional)\nexport const configureApi = (apiUrl: string, apiKey: string | null = null, sessionId: string | null = null): void => {\n defaultClient = new ApiClient({ apiUrl, apiKey, sessionId });\n}\n\n// Legacy streamChat function that uses the default client\nexport async function* streamChat(\n message: string,\n stream: boolean = true,\n fileIds?: string[],\n threadId?: string,\n audioInput?: string,\n audioFormat?: string,\n language?: string,\n returnAudio?: boolean,\n ttsVoice?: string,\n sourceLanguage?: string,\n targetLanguage?: string\n): AsyncGenerator<StreamResponse> {\n if (!defaultClient) {\n throw new Error('API not configured. Please call configureApi() with your server URL before using any API functions.');\n }\n \n yield* defaultClient.streamChat(\n message, \n stream, \n fileIds,\n threadId,\n audioInput,\n audioFormat,\n language,\n returnAudio,\n ttsVoice,\n sourceLanguage,\n targetLanguage\n );\n}\n\n"],"names":["httpAgent","httpsAgent","http","https","_a","_b","err","ApiClient","config","__publicField","sessionId","response","fetchError","errorText","errorDetail","friendlyMessage","errorJson","status","error","options","baseOptions","agent","headers","incomingHeaders","key","value","message","stream","fileIds","threadId","audioInput","audioFormat","language","returnAudio","ttsVoice","sourceLanguage","targetLanguage","request","controller","timeoutId","data","reader","decoder","buffer","hasReceivedContent","done","chunk","lineStartIndex","newlineIndex","line","jsonText","responseText","parseError","targetSessionId","fileIdsParam","url","messageId","file","formData","fileId","query","maxResults","fetchOptions","defaultClient","configureApi","apiUrl","apiKey","streamChat"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"api.mjs","sources":["../api.ts"],"sourcesContent":["// For Node.js environments, we can use http.Agent for connection pooling\nlet httpAgent: any = null;\nlet httpsAgent: any = null;\n\n// Initialize agents for connection pooling in Node.js environments\nif (typeof window === 'undefined') {\n // Lazy load to avoid including 'http' in browser bundles\n Promise.all([\n // @ts-expect-error - Dynamic import of Node.js built-in module (only available in Node.js runtime)\n import('http').catch(() => null),\n // @ts-expect-error - Dynamic import of Node.js built-in module (only available in Node.js runtime)\n import('https').catch(() => null)\n ]).then(([http, https]) => {\n if (http?.default?.Agent) {\n httpAgent = new http.default.Agent({ keepAlive: true });\n } else if (http?.Agent) {\n httpAgent = new http.Agent({ keepAlive: true });\n }\n \n if (https?.default?.Agent) {\n httpsAgent = new https.default.Agent({ keepAlive: true });\n } else if (https?.Agent) {\n httpsAgent = new https.Agent({ keepAlive: true });\n }\n }).catch(err => {\n // Silently fail - connection pooling is optional\n console.warn('Failed to initialize HTTP agents:', err.message);\n });\n}\n\n// Define the StreamResponse interface\nexport interface StreamResponse {\n text: string;\n done: boolean;\n audio?: string; // Optional base64-encoded audio data (TTS response) - full audio\n audioFormat?: string; // Audio format (mp3, wav, etc.)\n audio_chunk?: string; // Optional streaming audio chunk (base64-encoded)\n chunk_index?: number; // Index of the audio chunk for ordering\n threading?: { // Optional threading metadata\n supports_threading: boolean;\n message_id: string;\n session_id: string;\n };\n}\n\n// The server now returns this directly for non-streaming chat\nexport interface ChatResponse {\n response: string;\n sources?: any[];\n audio?: string; // Optional base64-encoded audio data (TTS response)\n audio_format?: string; // Audio format (mp3, wav, etc.)\n}\n\n// Thread-related interfaces\nexport interface ThreadInfo {\n thread_id: string;\n thread_session_id: string;\n parent_message_id: string;\n parent_session_id: string;\n adapter_name: string;\n created_at: string;\n expires_at: string;\n}\n\n// The request body for the /v1/chat endpoint\ninterface ChatRequest {\n messages: Array<{ role: string; content: string; }>;\n stream: boolean;\n file_ids?: string[]; // Optional list of file IDs for file context\n thread_id?: string; // Optional thread ID for follow-up questions\n audio_input?: string; // Optional base64-encoded audio data for STT\n audio_format?: string; // Optional audio format (mp3, wav, etc.)\n language?: string; // Optional language code for STT (e.g., \"en-US\")\n return_audio?: boolean; // Whether to return audio response (TTS)\n tts_voice?: string; // Voice for TTS (e.g., \"alloy\", \"echo\" for OpenAI)\n source_language?: string; // Source language for translation\n target_language?: string; // Target language for translation\n}\n\n// File-related interfaces\nexport interface FileUploadResponse {\n file_id: string;\n filename: string;\n mime_type: string;\n file_size: number;\n status: string;\n chunk_count: number;\n message: string;\n}\n\nexport interface FileInfo {\n file_id: string;\n filename: string;\n mime_type: string;\n file_size: number;\n upload_timestamp: string;\n processing_status: string;\n chunk_count: number;\n storage_type: string;\n}\n\nexport interface FileQueryRequest {\n query: string;\n max_results?: number;\n}\n\nexport interface FileQueryResponse {\n file_id: string;\n filename: string;\n results: Array<{\n content: string;\n metadata: {\n chunk_id: string;\n file_id: string;\n chunk_index: number;\n confidence: number;\n };\n }>;\n}\n\n// API key status interface\nexport interface ApiKeyStatus {\n exists: boolean;\n active: boolean;\n adapter_name?: string | null;\n client_name?: string | null;\n created_at?: string | number | null;\n system_prompt?: {\n id: string;\n exists: boolean;\n } | null;\n message?: string;\n}\n\n// Adapter information interface\nexport interface AdapterInfo {\n client_name: string;\n adapter_name: string;\n model: string | null;\n isFileSupported?: boolean;\n notes?: string | null;\n}\n\nexport class ApiClient {\n private readonly apiUrl: string;\n private readonly apiKey: string | null;\n private sessionId: string | null; // Session ID can be mutable\n\n constructor(config: { apiUrl: string; apiKey?: string | null; sessionId?: string | null }) {\n if (!config.apiUrl || typeof config.apiUrl !== 'string') {\n throw new Error('API URL must be a valid string');\n }\n if (config.apiKey !== undefined && config.apiKey !== null && typeof config.apiKey !== 'string') {\n throw new Error('API key must be a valid string or null');\n }\n if (config.sessionId !== undefined && config.sessionId !== null && typeof config.sessionId !== 'string') {\n throw new Error('Session ID must be a valid string or null');\n }\n \n this.apiUrl = config.apiUrl;\n this.apiKey = config.apiKey ?? null;\n this.sessionId = config.sessionId ?? null;\n }\n\n public setSessionId(sessionId: string | null): void {\n if (sessionId !== null && typeof sessionId !== 'string') {\n throw new Error('Session ID must be a valid string or null');\n }\n this.sessionId = sessionId;\n }\n\n public getSessionId(): string | null {\n return this.sessionId;\n }\n\n /**\n * Validate that the API key exists and is active.\n *\n * @returns Promise resolving to API key status information\n * @throws Error if API key is not provided, invalid, inactive, or validation fails\n */\n public async validateApiKey(): Promise<ApiKeyStatus> {\n if (!this.apiKey) {\n throw new Error('API key is required for validation');\n }\n\n try {\n const response = await fetch(`${this.apiUrl}/admin/api-keys/${this.apiKey}/status`, {\n ...this.getFetchOptions({\n method: 'GET'\n })\n }).catch((fetchError: any) => {\n // Catch network errors before they bubble up\n if (fetchError.name === 'TypeError' && fetchError.message.includes('Failed to fetch')) {\n throw new Error('Could not connect to the server. Please check if the server is running.');\n }\n throw fetchError;\n });\n\n if (!response.ok) {\n // Read error response body\n let errorText = '';\n try {\n errorText = await response.text();\n } catch {\n // If we can't read the body, fall back to status code\n errorText = `HTTP ${response.status}`;\n }\n\n let errorDetail: string;\n let friendlyMessage: string;\n\n try {\n const errorJson = JSON.parse(errorText);\n errorDetail = errorJson.detail || errorJson.message || errorText;\n } catch {\n // If parsing fails, use the error text or status code\n errorDetail = errorText || `HTTP ${response.status}`;\n }\n\n // Generate user-friendly error messages based on HTTP status code\n switch (response.status) {\n case 401:\n friendlyMessage = 'API key is invalid or expired';\n break;\n case 403:\n friendlyMessage = 'Access denied: API key does not have required permissions';\n break;\n case 404:\n friendlyMessage = 'API key not found';\n break;\n case 503:\n friendlyMessage = 'API key management is not available in inference-only mode';\n break;\n default:\n friendlyMessage = `Failed to validate API key: ${errorDetail}`;\n break;\n }\n\n // Throw error - will be logged in catch block to avoid duplicates\n throw new Error(friendlyMessage);\n }\n\n const status: ApiKeyStatus = await response.json();\n\n // Check if the key exists\n if (!status.exists) {\n const friendlyMessage = 'API key does not exist';\n // Throw error - will be logged in catch block to avoid duplicates\n throw new Error(friendlyMessage);\n }\n\n // Check if the key is active\n if (!status.active) {\n const friendlyMessage = 'API key is inactive';\n // Throw error - will be logged in catch block to avoid duplicates\n throw new Error(friendlyMessage);\n }\n\n return status;\n } catch (error: any) {\n // Extract user-friendly error message\n let friendlyMessage: string;\n\n if (error instanceof Error && error.message) {\n // If it's already a user-friendly Error from above, use it directly\n if (error.message.includes('API key') ||\n error.message.includes('Access denied') ||\n error.message.includes('invalid') ||\n error.message.includes('expired') ||\n error.message.includes('inactive') ||\n error.message.includes('not found') ||\n error.message.includes('Could not connect')) {\n friendlyMessage = error.message;\n } else {\n friendlyMessage = `API key validation failed: ${error.message}`;\n }\n } else if (error.name === 'TypeError' && error.message?.includes('Failed to fetch')) {\n friendlyMessage = 'Could not connect to the server. Please check if the server is running.';\n } else {\n friendlyMessage = 'API key validation failed. Please check your API key and try again.';\n }\n\n // Only log warning if it's not a network error (those are already logged by browser)\n // For validation errors, we log once with a friendly message\n // Note: Browser will still log HTTP errors (401, 404, etc.) - this is unavoidable\n console.warn(`[ApiClient] ${friendlyMessage}`);\n\n // Throw the friendly error message\n throw new Error(friendlyMessage);\n }\n }\n\n /**\n * Get adapter information for the current API key.\n *\n * Returns information about the adapter and model being used by the API key.\n * This is useful for displaying configuration details to users.\n *\n * @returns Promise resolving to adapter information\n * @throws Error if API key is not provided, invalid, disabled, or request fails\n */\n public async getAdapterInfo(): Promise<AdapterInfo> {\n if (!this.apiKey) {\n throw new Error('API key is required to get adapter information');\n }\n\n try {\n const response = await fetch(`${this.apiUrl}/admin/api-keys/info`, {\n ...this.getFetchOptions({\n method: 'GET'\n })\n }).catch((fetchError: any) => {\n // Catch network errors before they bubble up\n if (fetchError.name === 'TypeError' && fetchError.message.includes('Failed to fetch')) {\n throw new Error('Could not connect to the server. Please check if the server is running.');\n }\n throw fetchError;\n });\n\n if (!response.ok) {\n // Read error response body\n let errorText = '';\n try {\n errorText = await response.text();\n } catch {\n // If we can't read the body, fall back to status code\n errorText = `HTTP ${response.status}`;\n }\n\n let errorDetail: string;\n let friendlyMessage: string;\n\n try {\n const errorJson = JSON.parse(errorText);\n errorDetail = errorJson.detail || errorJson.message || errorText;\n } catch {\n // If parsing fails, use the error text or status code\n errorDetail = errorText || `HTTP ${response.status}`;\n }\n\n // Generate user-friendly error messages based on HTTP status code\n switch (response.status) {\n case 401:\n friendlyMessage = 'API key is invalid, disabled, or has no associated adapter';\n break;\n case 404:\n friendlyMessage = 'Adapter configuration not found';\n break;\n case 503:\n friendlyMessage = 'Service is not available';\n break;\n default:\n friendlyMessage = `Failed to get adapter info: ${errorDetail}`;\n break;\n }\n\n // Throw error - will be logged in catch block to avoid duplicates\n throw new Error(friendlyMessage);\n }\n\n const adapterInfo: AdapterInfo = await response.json();\n return adapterInfo;\n } catch (error: any) {\n // Extract user-friendly error message\n let friendlyMessage: string;\n\n if (error instanceof Error && error.message) {\n // If it's already a user-friendly Error from above, use it directly\n if (error.message.includes('API key') ||\n error.message.includes('Adapter') ||\n error.message.includes('invalid') ||\n error.message.includes('disabled') ||\n error.message.includes('not found') ||\n error.message.includes('Could not connect')) {\n friendlyMessage = error.message;\n } else {\n friendlyMessage = `Failed to get adapter info: ${error.message}`;\n }\n } else if (error.name === 'TypeError' && error.message?.includes('Failed to fetch')) {\n friendlyMessage = 'Could not connect to the server. Please check if the server is running.';\n } else {\n friendlyMessage = 'Failed to get adapter information. Please try again.';\n }\n\n console.warn(`[ApiClient] ${friendlyMessage}`);\n\n // Throw the friendly error message\n throw new Error(friendlyMessage);\n }\n }\n\n // Helper to get fetch options with connection pooling if available\n private getFetchOptions(options: RequestInit = {}): RequestInit {\n const baseOptions: RequestInit = {};\n \n // Environment-specific options\n if (typeof window === 'undefined') {\n // Node.js: Use connection pooling agent\n const isHttps = this.apiUrl.startsWith('https:');\n const agent = isHttps ? httpsAgent : httpAgent;\n if (agent) {\n (baseOptions as any).agent = agent;\n }\n } else {\n // Browser: Use keep-alive header\n baseOptions.headers = { 'Connection': 'keep-alive' };\n }\n\n // Common headers\n const headers: Record<string, string> = {\n 'X-Request-ID': Date.now().toString(36) + Math.random().toString(36).substring(2),\n };\n\n // Merge base options headers (for browser keep-alive)\n if (baseOptions.headers) {\n Object.assign(headers, baseOptions.headers);\n }\n\n // Merge original request headers (but don't overwrite API key)\n if (options.headers) {\n const incomingHeaders = options.headers as Record<string, string>;\n for (const [key, value] of Object.entries(incomingHeaders)) {\n // Don't overwrite X-API-Key if we have one\n if (key.toLowerCase() !== 'x-api-key' || !this.apiKey) {\n headers[key] = value;\n }\n }\n }\n\n if (this.apiKey) {\n headers['X-API-Key'] = this.apiKey;\n }\n\n if (this.sessionId) {\n headers['X-Session-ID'] = this.sessionId;\n }\n\n return {\n ...options,\n ...baseOptions,\n headers,\n };\n }\n\n // Create Chat request\n private createChatRequest(\n message: string, \n stream: boolean = true, \n fileIds?: string[],\n threadId?: string,\n audioInput?: string,\n audioFormat?: string,\n language?: string,\n returnAudio?: boolean,\n ttsVoice?: string,\n sourceLanguage?: string,\n targetLanguage?: string\n ): ChatRequest {\n const request: ChatRequest = {\n messages: [\n { role: \"user\", content: message }\n ],\n stream\n };\n if (fileIds && fileIds.length > 0) {\n request.file_ids = fileIds;\n }\n if (threadId) {\n request.thread_id = threadId;\n }\n if (audioInput) {\n request.audio_input = audioInput;\n }\n if (audioFormat) {\n request.audio_format = audioFormat;\n }\n if (language) {\n request.language = language;\n }\n if (returnAudio !== undefined) {\n request.return_audio = returnAudio;\n }\n if (ttsVoice) {\n request.tts_voice = ttsVoice;\n }\n if (sourceLanguage) {\n request.source_language = sourceLanguage;\n }\n if (targetLanguage) {\n request.target_language = targetLanguage;\n }\n return request;\n }\n\n public async *streamChat(\n message: string,\n stream: boolean = true,\n fileIds?: string[],\n threadId?: string,\n audioInput?: string,\n audioFormat?: string,\n language?: string,\n returnAudio?: boolean,\n ttsVoice?: string,\n sourceLanguage?: string,\n targetLanguage?: string\n ): AsyncGenerator<StreamResponse> {\n try {\n // Add timeout to the fetch request\n const controller = new AbortController();\n const timeoutId = setTimeout(() => controller.abort(), 60000); // 60 second timeout\n\n const response = await fetch(`${this.apiUrl}/v1/chat`, {\n ...this.getFetchOptions({\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n 'Accept': stream ? 'text/event-stream' : 'application/json'\n },\n body: JSON.stringify(this.createChatRequest(\n message, \n stream, \n fileIds,\n threadId,\n audioInput,\n audioFormat,\n language,\n returnAudio,\n ttsVoice,\n sourceLanguage,\n targetLanguage\n )),\n }),\n signal: controller.signal\n });\n\n clearTimeout(timeoutId);\n\n if (!response.ok) {\n const errorText = await response.text();\n throw new Error(`Network response was not ok: ${response.status} ${errorText}`);\n }\n\n if (!stream) {\n // Handle non-streaming response\n const data = await response.json() as ChatResponse;\n if (data.response) {\n yield {\n text: data.response,\n done: true,\n audio: data.audio,\n audioFormat: data.audio_format\n } as StreamResponse & { audio?: string; audioFormat?: string };\n }\n return;\n }\n \n const reader = response.body?.getReader();\n if (!reader) throw new Error('No reader available');\n\n const decoder = new TextDecoder();\n let buffer = '';\n let hasReceivedContent = false;\n\n try {\n while (true) {\n const { done, value } = await reader.read();\n if (done) {\n break;\n }\n\n const chunk = decoder.decode(value, { stream: true });\n buffer += chunk;\n \n // Process complete lines immediately as they arrive\n let lineStartIndex = 0;\n let newlineIndex;\n \n while ((newlineIndex = buffer.indexOf('\\n', lineStartIndex)) !== -1) {\n const line = buffer.slice(lineStartIndex, newlineIndex).trim();\n lineStartIndex = newlineIndex + 1;\n \n if (line && line.startsWith('data: ')) {\n const jsonText = line.slice(6).trim();\n \n if (!jsonText || jsonText === '[DONE]') {\n yield { text: '', done: true };\n return;\n }\n\n try {\n const data = JSON.parse(jsonText);\n\n if (data.error) {\n const errorMessage = data.error?.message || data.error || 'Unknown server error';\n const friendlyMessage = `Server error: ${errorMessage}`;\n console.warn(`[ApiClient] ${friendlyMessage}`);\n throw new Error(friendlyMessage);\n }\n\n // Check for done chunk first - it may not have a response field\n // This handles the final done chunk that contains threading metadata\n if (data.done === true) {\n hasReceivedContent = true;\n yield {\n text: '',\n done: true,\n audio: data.audio,\n audioFormat: data.audio_format || data.audioFormat,\n threading: data.threading // Pass through threading metadata\n };\n return;\n }\n\n // Note: Base64 audio filtering is handled by chatStore's sanitizeMessageContent\n // We keep response text as-is here and let the application layer decide\n const responseText = data.response || '';\n\n // Handle streaming audio chunks\n if (data.audio_chunk !== undefined) {\n yield {\n text: '',\n done: false,\n audio_chunk: data.audio_chunk,\n audioFormat: data.audioFormat || data.audio_format || 'opus',\n chunk_index: data.chunk_index ?? 0\n };\n }\n\n if (responseText || data.audio) {\n hasReceivedContent = true;\n yield {\n text: responseText,\n done: data.done || false,\n audio: data.audio,\n audioFormat: data.audio_format || data.audioFormat,\n threading: data.threading // Include threading if present\n };\n }\n\n } catch (parseError: any) {\n // Re-throw intentional errors (like moderation blocks) - don't swallow them\n if (parseError?.message?.startsWith('Server error:')) {\n throw parseError;\n }\n // Log JSON parse errors for debugging\n console.warn('[ApiClient] Unable to parse server response. This may be a temporary issue.');\n console.warn('[ApiClient] Parse error details:', parseError?.message);\n console.warn('[ApiClient] JSON text length:', jsonText?.length);\n console.warn('[ApiClient] JSON text preview (first 200 chars):', jsonText?.substring(0, 200));\n console.warn('[ApiClient] JSON text preview (last 200 chars):', jsonText?.substring(jsonText.length - 200));\n }\n } else if (line) {\n // Handle raw text chunks that are not in SSE format\n hasReceivedContent = true;\n yield { text: line, done: false };\n }\n }\n \n buffer = buffer.slice(lineStartIndex);\n\n if (buffer.length > 1000000) { // 1MB limit\n console.warn('[ApiClient] Buffer too large, truncating...');\n buffer = buffer.slice(-500000); // Keep last 500KB\n }\n }\n \n if (hasReceivedContent) {\n yield { text: '', done: true };\n }\n \n } finally {\n reader.releaseLock();\n }\n \n } catch (error: any) {\n if (error.name === 'AbortError') {\n throw new Error('Connection timed out. Please check if the server is running.');\n } else if (error.name === 'TypeError' && error.message.includes('Failed to fetch')) {\n throw new Error('Could not connect to the server. Please check if the server is running.');\n } else {\n throw error;\n }\n }\n }\n\n public async clearConversationHistory(sessionId?: string): Promise<{\n status: string;\n message: string;\n session_id: string;\n deleted_count: number;\n timestamp: string;\n }> {\n /**\n * Clear conversation history for a session.\n *\n * @param sessionId - Optional session ID to clear. If not provided, uses current session.\n * @returns Promise resolving to operation result\n * @throws Error if the operation fails\n */\n const targetSessionId = sessionId || this.sessionId;\n\n if (!targetSessionId) {\n throw new Error('No session ID provided and no current session available');\n }\n\n if (!this.apiKey) {\n throw new Error('API key is required for clearing conversation history');\n }\n\n const headers: Record<string, string> = {\n 'Content-Type': 'application/json',\n 'X-Session-ID': targetSessionId,\n 'X-API-Key': this.apiKey\n };\n\n try {\n const response = await fetch(`${this.apiUrl}/admin/chat-history/${targetSessionId}`, {\n ...this.getFetchOptions({\n method: 'DELETE',\n headers\n })\n });\n\n if (!response.ok) {\n const errorText = await response.text();\n throw new Error(`Failed to clear conversation history: ${response.status} ${errorText}`);\n }\n\n const result = await response.json();\n return result;\n\n } catch (error: any) {\n if (error.name === 'TypeError' && error.message.includes('Failed to fetch')) {\n throw new Error('Could not connect to the server. Please check if the server is running.');\n } else {\n throw error;\n }\n }\n }\n\n public async deleteConversationWithFiles(sessionId?: string, fileIds?: string[]): Promise<{\n status: string;\n message: string;\n session_id: string;\n deleted_messages: number;\n deleted_files: number;\n file_deletion_errors: string[] | null;\n timestamp: string;\n }> {\n /**\n * Delete a conversation and all associated files.\n *\n * This method performs a complete conversation deletion:\n * - Deletes each file provided in fileIds (metadata, content, and vector store chunks)\n * - Clears conversation history\n *\n * File tracking is managed by the frontend (localStorage). The backend is stateless\n * and requires fileIds to be provided explicitly.\n *\n * @param sessionId - Optional session ID to delete. If not provided, uses current session.\n * @param fileIds - Optional list of file IDs to delete (from conversation's attachedFiles)\n * @returns Promise resolving to deletion result with counts\n * @throws Error if the operation fails\n */\n const targetSessionId = sessionId || this.sessionId;\n\n if (!targetSessionId) {\n throw new Error('No session ID provided and no current session available');\n }\n\n if (!this.apiKey) {\n throw new Error('API key is required for deleting conversation');\n }\n\n const headers: Record<string, string> = {\n 'Content-Type': 'application/json',\n 'X-Session-ID': targetSessionId,\n 'X-API-Key': this.apiKey\n };\n\n // Build URL with file_ids query parameter\n const fileIdsParam = fileIds && fileIds.length > 0 ? `?file_ids=${fileIds.join(',')}` : '';\n const url = `${this.apiUrl}/admin/conversations/${targetSessionId}${fileIdsParam}`;\n\n try {\n const response = await fetch(url, {\n ...this.getFetchOptions({\n method: 'DELETE',\n headers\n })\n });\n\n if (!response.ok) {\n const errorText = await response.text();\n throw new Error(`Failed to delete conversation: ${response.status} ${errorText}`);\n }\n\n const result = await response.json();\n return result;\n\n } catch (error: any) {\n if (error.name === 'TypeError' && error.message.includes('Failed to fetch')) {\n throw new Error('Could not connect to the server. Please check if the server is running.');\n } else {\n throw error;\n }\n }\n }\n\n /**\n * Create a conversation thread from a parent message.\n *\n * @param messageId - ID of the parent message\n * @param sessionId - Session ID of the parent conversation\n * @returns Promise resolving to thread information\n * @throws Error if the operation fails\n */\n public async createThread(messageId: string, sessionId: string): Promise<ThreadInfo> {\n if (!this.apiKey) {\n throw new Error('API key is required for creating threads');\n }\n\n const headers: Record<string, string> = {\n 'Content-Type': 'application/json',\n 'X-API-Key': this.apiKey\n };\n\n try {\n const response = await fetch(`${this.apiUrl}/api/threads`, {\n ...this.getFetchOptions({\n method: 'POST',\n headers,\n body: JSON.stringify({\n message_id: messageId,\n session_id: sessionId\n })\n })\n });\n\n if (!response.ok) {\n const errorText = await response.text();\n throw new Error(`Failed to create thread: ${response.status} ${errorText}`);\n }\n\n const result = await response.json();\n return result;\n\n } catch (error: any) {\n if (error.name === 'TypeError' && error.message.includes('Failed to fetch')) {\n throw new Error('Could not connect to the server. Please check if the server is running.');\n } else {\n throw error;\n }\n }\n }\n\n /**\n * Get thread information by thread ID.\n *\n * @param threadId - Thread identifier\n * @returns Promise resolving to thread information\n * @throws Error if the operation fails\n */\n public async getThreadInfo(threadId: string): Promise<ThreadInfo> {\n if (!this.apiKey) {\n throw new Error('API key is required for getting thread info');\n }\n\n const headers: Record<string, string> = {\n 'X-API-Key': this.apiKey\n };\n\n try {\n const response = await fetch(`${this.apiUrl}/api/threads/${threadId}`, {\n ...this.getFetchOptions({\n method: 'GET',\n headers\n })\n });\n\n if (!response.ok) {\n const errorText = await response.text();\n throw new Error(`Failed to get thread info: ${response.status} ${errorText}`);\n }\n\n const result = await response.json();\n return result;\n\n } catch (error: any) {\n if (error.name === 'TypeError' && error.message.includes('Failed to fetch')) {\n throw new Error('Could not connect to the server. Please check if the server is running.');\n } else {\n throw error;\n }\n }\n }\n\n /**\n * Delete a thread and its associated dataset.\n *\n * @param threadId - Thread identifier\n * @returns Promise resolving to deletion result\n * @throws Error if the operation fails\n */\n public async deleteThread(threadId: string): Promise<{ status: string; message: string; thread_id: string }> {\n if (!this.apiKey) {\n throw new Error('API key is required for deleting threads');\n }\n\n const headers: Record<string, string> = {\n 'X-API-Key': this.apiKey\n };\n\n try {\n const response = await fetch(`${this.apiUrl}/api/threads/${threadId}`, {\n ...this.getFetchOptions({\n method: 'DELETE',\n headers\n })\n });\n\n if (!response.ok) {\n const errorText = await response.text();\n throw new Error(`Failed to delete thread: ${response.status} ${errorText}`);\n }\n\n const result = await response.json();\n return result;\n\n } catch (error: any) {\n if (error.name === 'TypeError' && error.message.includes('Failed to fetch')) {\n throw new Error('Could not connect to the server. Please check if the server is running.');\n } else {\n throw error;\n }\n }\n }\n\n /**\n * Upload a file for processing and indexing.\n *\n * @param file - The file to upload\n * @returns Promise resolving to upload response with file_id\n * @throws Error if upload fails\n */\n public async uploadFile(file: File): Promise<FileUploadResponse> {\n if (!this.apiKey) {\n throw new Error('API key is required for file upload');\n }\n\n const formData = new FormData();\n formData.append('file', file);\n\n try {\n const response = await fetch(`${this.apiUrl}/api/files/upload`, {\n ...this.getFetchOptions({\n method: 'POST',\n body: formData\n })\n });\n\n if (!response.ok) {\n const errorText = await response.text();\n throw new Error(`Failed to upload file: ${response.status} ${errorText}`);\n }\n\n return await response.json();\n } catch (error: any) {\n if (error.name === 'TypeError' && error.message.includes('Failed to fetch')) {\n throw new Error('Could not connect to the server. Please check if the server is running.');\n } else {\n throw error;\n }\n }\n }\n\n /**\n * List all files for the current API key.\n * \n * @returns Promise resolving to list of file information\n * @throws Error if request fails\n */\n public async listFiles(): Promise<FileInfo[]> {\n if (!this.apiKey) {\n throw new Error('API key is required for listing files');\n }\n\n try {\n const response = await fetch(`${this.apiUrl}/api/files`, {\n ...this.getFetchOptions({\n method: 'GET'\n })\n });\n\n if (!response.ok) {\n const errorText = await response.text();\n throw new Error(`Failed to list files: ${response.status} ${errorText}`);\n }\n\n return await response.json();\n } catch (error: any) {\n if (error.name === 'TypeError' && error.message.includes('Failed to fetch')) {\n throw new Error('Could not connect to the server. Please check if the server is running.');\n } else {\n throw error;\n }\n }\n }\n\n /**\n * Get information about a specific file.\n * \n * @param fileId - The file ID\n * @returns Promise resolving to file information\n * @throws Error if file not found or request fails\n */\n public async getFileInfo(fileId: string): Promise<FileInfo> {\n if (!this.apiKey) {\n throw new Error('API key is required for getting file info');\n }\n\n try {\n const response = await fetch(`${this.apiUrl}/api/files/${fileId}`, {\n ...this.getFetchOptions({\n method: 'GET'\n })\n });\n\n if (!response.ok) {\n const errorText = await response.text();\n throw new Error(`Failed to get file info: ${response.status} ${errorText}`);\n }\n\n return await response.json();\n } catch (error: any) {\n if (error.name === 'TypeError' && error.message.includes('Failed to fetch')) {\n throw new Error('Could not connect to the server. Please check if the server is running.');\n } else {\n throw error;\n }\n }\n }\n\n /**\n * Query a specific file using semantic search.\n * \n * @param fileId - The file ID\n * @param query - The search query\n * @param maxResults - Maximum number of results (default: 10)\n * @returns Promise resolving to query results\n * @throws Error if query fails\n */\n public async queryFile(fileId: string, query: string, maxResults: number = 10): Promise<FileQueryResponse> {\n if (!this.apiKey) {\n throw new Error('API key is required for querying files');\n }\n\n try {\n const response = await fetch(`${this.apiUrl}/api/files/${fileId}/query`, {\n ...this.getFetchOptions({\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json'\n },\n body: JSON.stringify({ query, max_results: maxResults })\n })\n });\n\n if (!response.ok) {\n const errorText = await response.text();\n throw new Error(`Failed to query file: ${response.status} ${errorText}`);\n }\n\n return await response.json();\n } catch (error: any) {\n if (error.name === 'TypeError' && error.message.includes('Failed to fetch')) {\n throw new Error('Could not connect to the server. Please check if the server is running.');\n } else {\n throw error;\n }\n }\n }\n\n /**\n * Delete a specific file.\n * \n * @param fileId - The file ID\n * @returns Promise resolving to deletion result\n * @throws Error if deletion fails\n */\n public async deleteFile(fileId: string): Promise<{ message: string; file_id: string }> {\n if (!this.apiKey) {\n throw new Error('API key is required for deleting files');\n }\n\n const url = `${this.apiUrl}/api/files/${fileId}`;\n const fetchOptions = this.getFetchOptions({\n method: 'DELETE'\n });\n\n try {\n const response = await fetch(url, fetchOptions);\n\n if (!response.ok) {\n const errorText = await response.text();\n let friendlyMessage: string;\n try {\n const errorJson = JSON.parse(errorText);\n friendlyMessage = errorJson.detail || errorJson.message || `Failed to delete file (HTTP ${response.status})`;\n } catch {\n friendlyMessage = `Failed to delete file (HTTP ${response.status})`;\n }\n console.warn(`[ApiClient] ${friendlyMessage}`);\n throw new Error(friendlyMessage);\n }\n\n const result = await response.json();\n return result;\n } catch (error: any) {\n // Extract user-friendly error message\n let friendlyMessage: string;\n \n if (error.name === 'TypeError' && error.message.includes('Failed to fetch')) {\n friendlyMessage = 'Could not connect to the server. Please check if the server is running.';\n } else if (error.message && !error.message.includes('Failed to delete file')) {\n // Use existing message if it's already user-friendly\n friendlyMessage = error.message;\n } else {\n friendlyMessage = `Failed to delete file. Please try again.`;\n }\n \n console.warn(`[ApiClient] ${friendlyMessage}`);\n throw new Error(friendlyMessage);\n }\n }\n}\n\n// Legacy compatibility functions - these create a default client instance\n// These are kept for backward compatibility but should be deprecated in favor of the class-based approach\n\nlet defaultClient: ApiClient | null = null;\n\n// Configure the API with a custom URL, API key (optional), and session ID (optional)\nexport const configureApi = (apiUrl: string, apiKey: string | null = null, sessionId: string | null = null): void => {\n defaultClient = new ApiClient({ apiUrl, apiKey, sessionId });\n}\n\n// Legacy streamChat function that uses the default client\nexport async function* streamChat(\n message: string,\n stream: boolean = true,\n fileIds?: string[],\n threadId?: string,\n audioInput?: string,\n audioFormat?: string,\n language?: string,\n returnAudio?: boolean,\n ttsVoice?: string,\n sourceLanguage?: string,\n targetLanguage?: string\n): AsyncGenerator<StreamResponse> {\n if (!defaultClient) {\n throw new Error('API not configured. Please call configureApi() with your server URL before using any API functions.');\n }\n \n yield* defaultClient.streamChat(\n message, \n stream, \n fileIds,\n threadId,\n audioInput,\n audioFormat,\n language,\n returnAudio,\n ttsVoice,\n sourceLanguage,\n targetLanguage\n );\n}\n\n"],"names":["httpAgent","httpsAgent","http","https","_a","_b","err","ApiClient","config","__publicField","sessionId","response","fetchError","errorText","errorDetail","friendlyMessage","errorJson","status","error","options","baseOptions","agent","headers","incomingHeaders","key","value","message","stream","fileIds","threadId","audioInput","audioFormat","language","returnAudio","ttsVoice","sourceLanguage","targetLanguage","request","_c","controller","timeoutId","data","reader","decoder","buffer","hasReceivedContent","done","chunk","lineStartIndex","newlineIndex","line","jsonText","responseText","parseError","targetSessionId","fileIdsParam","url","messageId","file","formData","fileId","query","maxResults","fetchOptions","defaultClient","configureApi","apiUrl","apiKey","streamChat"],"mappings":"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"}
|