@schmitech/chatbot-api 0.1.1 → 0.3.0

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 CHANGED
@@ -238,6 +238,107 @@ function ChatComponent() {
238
238
  export default ChatComponent;
239
239
  ```
240
240
 
241
+ ## Usage with Native Mobile Platforms
242
+
243
+ ### React Native
244
+
245
+ If using React Native, you can use the npm package directly:
246
+
247
+ ```javascript
248
+ import { configureApi, streamChat } from '@schmitech/chatbot-api';
249
+
250
+ // Configure API
251
+ configureApi('https://your-api-server.com');
252
+
253
+ // Usage in React Native component
254
+ async function handleChat(message) {
255
+ try {
256
+ for await (const response of streamChat(message, false)) {
257
+ // Update UI with response.text
258
+ }
259
+ } catch (error) {
260
+ console.error('Chat error:', error);
261
+ }
262
+ }
263
+ ```
264
+
265
+ ### Native iOS (Swift)
266
+
267
+ For pure native iOS, you'll need to create a Swift wrapper:
268
+
269
+ ```swift
270
+ // ChatService.swift
271
+ import Foundation
272
+
273
+ class ChatService {
274
+ private let apiUrl: String
275
+
276
+ init(apiUrl: String) {
277
+ self.apiUrl = apiUrl
278
+ }
279
+
280
+ func sendMessage(_ message: String, voiceEnabled: Bool,
281
+ onResponse: @escaping (String, Bool) -> Void,
282
+ onError: @escaping (Error) -> Void) {
283
+
284
+ guard let url = URL(string: "\(apiUrl)/chat") else {
285
+ onError(NSError(domain: "Invalid URL", code: 0))
286
+ return
287
+ }
288
+
289
+ var request = URLRequest(url: url)
290
+ request.httpMethod = "POST"
291
+ request.addValue("application/json", forHTTPHeaderField: "Content-Type")
292
+
293
+ let body: [String: Any] = ["message": message, "voiceEnabled": voiceEnabled]
294
+ request.httpBody = try? JSONSerialization.data(withJSONObject: body)
295
+
296
+ let task = URLSession.shared.dataTask(with: request) { data, response, error in
297
+ // Handle streaming responses
298
+ // ...
299
+ }
300
+ task.resume()
301
+ }
302
+ }
303
+ ```
304
+
305
+ ### Native Android (Kotlin)
306
+
307
+ For pure native Android, you'd implement:
308
+
309
+ ```kotlin
310
+ // ChatService.kt
311
+ class ChatService(private val apiUrl: String) {
312
+ fun sendMessage(
313
+ message: String,
314
+ voiceEnabled: Boolean,
315
+ onResponse: (text: String, isDone: Boolean) -> Unit,
316
+ onError: (error: Throwable) -> Unit
317
+ ) {
318
+ val client = OkHttpClient()
319
+
320
+ val requestBody = JSONObject().apply {
321
+ put("message", message)
322
+ put("voiceEnabled", voiceEnabled)
323
+ }.toString().toRequestBody("application/json".toMediaType())
324
+
325
+ val request = Request.Builder()
326
+ .url("$apiUrl/chat")
327
+ .post(requestBody)
328
+ .build()
329
+
330
+ // Set up streaming response handling
331
+ // ...
332
+ }
333
+ }
334
+ ```
335
+
336
+ ### Alternative Approaches
337
+
338
+ 1. **Flutter**: Create Dart implementation using http package
339
+ 2. **WebView**: Embed a web component in your app that uses the JS client
340
+ 3. **Capacitor/Cordova**: Create a hybrid app, use the npm package directly
341
+
241
342
  ## API Reference
242
343
 
243
344
  ### configureApi(apiUrl)
package/api.d.ts CHANGED
@@ -1,8 +1,10 @@
1
1
  export interface StreamResponse {
2
- text?: string;
3
- content?: string;
4
- done?: boolean;
5
- type?: string;
2
+ text: string;
3
+ done: boolean;
4
+ }
5
+ export interface ChatResponse {
6
+ response: string;
7
+ audio: string | null;
6
8
  }
7
9
  export declare const configureApi: (apiUrl: string) => void;
8
- export declare function streamChat(message: string, voiceEnabled: boolean): AsyncGenerator<StreamResponse>;
10
+ export declare function streamChat(message: string, voiceEnabled: boolean, stream?: boolean): AsyncGenerator<StreamResponse>;
package/dist/api.cjs CHANGED
@@ -1,3 +1,3 @@
1
- "use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});let l=null,d=null,y=0,A=0;const m=async()=>{if(typeof window>"u")try{let e,r;try{e=await Promise.resolve().then(()=>require("./__vite-browser-external-BcPniuRQ.cjs")),r=await Promise.resolve().then(()=>require("./__vite-browser-external-BcPniuRQ.cjs"))}catch(t){console.warn("[Connection Pool] Failed to import Node.js modules:",t);return}l=new e.Agent({keepAlive:!0,keepAliveMsecs:3e4,maxSockets:5});const i=l.createConnection;l.createConnection=function(t,o){y++;const a=i.call(this,t,o);return a.on("reuse",()=>{A++}),a},d=new r.Agent({keepAlive:!0,keepAliveMsecs:3e4,maxSockets:5})}catch(e){console.warn("[Connection Pool] Failed to initialize HTTP agents:",e)}};(async()=>{try{await m()}catch(e){console.warn("Failed to initialize connection pool:",e)}})();let f=null;const P=e=>{if(!e||typeof e!="string")throw new Error("API URL must be a valid string");f=e},k=()=>{if(!f)throw new Error("API URL not configured. Please call configureApi() with your server URL before using any API functions.");return f},C=(e,r={})=>{const i=e.startsWith("https:");if(typeof window>"u"){if(i&&d)return{...r,agent:d};if(l)return{...r,agent:l}}const t=Date.now().toString(36)+Math.random().toString(36).substring(2);return{...r,headers:{...r.headers,Connection:"keep-alive","X-Request-ID":t}}};async function*v(e,r){var i;try{const t=k(),o=await fetch(`${t}/chat`,C(t,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({message:e,voiceEnabled:r})}));if(!o.ok){const n=await o.text();throw console.error(`API request failed: ${o.status} ${n}`),new Error(`Network response was not ok: ${o.status} ${n}`)}const a=(i=o.body)==null?void 0:i.getReader();if(!a)throw new Error("No reader available");const w=new TextDecoder;let s="";for(;;){const{done:n,value:g}=await a.read();if(n)break;const p=w.decode(g,{stream:!0});s+=p;const h=s.split(`
2
- `);s=h.pop()||"";for(const u of h)if(u.trim())try{const c=JSON.parse(u);c.content&&!c.text&&(c.text=c.content),yield c}catch{console.warn("Error parsing JSON chunk:",u)}}if(s)try{const n=JSON.parse(s);n.content&&!n.text&&(n.text=n.content),yield n}catch{console.warn("Error parsing final JSON buffer:",s)}}catch(t){console.error("Chat API error:",t.message),yield{text:`Error connecting to chat server: ${t.message}`,done:!0}}}exports.configureApi=P;exports.streamChat=v;
1
+ "use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});let c=null,w=null,v=0,C=0;const S=async()=>{if(typeof window>"u")try{let e,t;try{e=await Promise.resolve().then(()=>require("./__vite-browser-external-BcPniuRQ.cjs")),t=await Promise.resolve().then(()=>require("./__vite-browser-external-BcPniuRQ.cjs"))}catch(o){console.warn("[Connection Pool] Failed to import Node.js modules:",o);return}c=new e.Agent({keepAlive:!0,keepAliveMsecs:3e4,maxSockets:5});const n=c.createConnection;c.createConnection=function(o,i){v++;const s=n.call(this,o,i);return s.on("reuse",()=>{C++}),s},w=new t.Agent({keepAlive:!0,keepAliveMsecs:3e4,maxSockets:5})}catch(e){console.warn("[Connection Pool] Failed to initialize HTTP agents:",e)}};(async()=>{try{await S()}catch(e){console.warn("Failed to initialize connection pool:",e)}})();let p=null;const b=e=>{if(!e||typeof e!="string")throw new Error("API URL must be a valid string");p=e},m=()=>{if(!p)throw new Error("API URL not configured. Please call configureApi() with your server URL before using any API functions.");return p},x=(e,t={})=>{const n=e.startsWith("https:");if(typeof window>"u"){if(n&&w)return{...t,agent:w};if(c)return{...t,agent:c}}const o=Date.now().toString(36)+Math.random().toString(36).substring(2);return{...t,headers:{...t.headers,Connection:"keep-alive","X-Request-ID":o}}};async function*N(e,t,n=!0){var o;try{const i=m(),s=await fetch(`${i}/chat`,x(i,{method:"POST",headers:{"Content-Type":"application/json",Accept:n?"text/event-stream":"application/json"},body:JSON.stringify({message:e,voiceEnabled:t,stream:n})}));if(!s.ok){const r=await s.text();throw console.error(`API request failed: ${s.status} ${r}`),new Error(`Network response was not ok: ${s.status} ${r}`)}if(!n){yield{text:(await s.json()).response,done:!0};return}const g=(o=s.body)==null?void 0:o.getReader();if(!g)throw new Error("No reader available");const P=new TextDecoder;let a="",f="";for(;;){const{done:r,value:d}=await g.read();if(r)break;const k=P.decode(d,{stream:!0});a+=k;const y=a.split(`
2
+ `);a=y.pop()||"";for(const u of y)if(u.trim()&&u.startsWith("data: "))try{const l=JSON.parse(u.slice(6));if(l.text){const h=A(l.text,f);h?(f+=h,yield{text:h,done:l.done||!1}):l.done&&(yield{text:"",done:!0})}else yield{text:l.text||"",done:l.done||!1}}catch{console.warn("Error parsing JSON chunk:",u)}}if(a&&a.startsWith("data: "))try{const r=JSON.parse(a.slice(6));if(r.text){const d=A(r.text,f);(d||r.done)&&(yield{text:d||"",done:r.done||!1})}else yield{text:r.text||"",done:r.done||!1}}catch{console.warn("Error parsing final JSON buffer:",a)}}catch(i){console.error("Chat API error:",i.message),yield{text:`Error connecting to chat server: ${i.message}`,done:!0}}}function A(e,t){if(!t)return e;if(t.endsWith(e))return"";if(e.length>t.length){if(e.startsWith(t))return e.slice(t.length);let n=0;const o=Math.min(t.length,e.length);for(;n<o&&t[n]===e[n];)n++;if(n>t.length/2)return e.slice(n)}return e}exports.configureApi=b;exports.streamChat=N;
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// Track connections for debugging\nlet connectionCounter = 0;\nlet connectionReuseCounter = 0;\n\n// Define the StreamResponse interface\nexport interface StreamResponse {\n text?: string;\n content?: string; // Adding alternative property name\n done?: boolean;\n type?: string; // Type of response (e.g., 'text', 'audio')\n}\n\n// Initialize the HTTP agents for connection pooling\nconst initConnectionPool = async () => {\n // Only run in Node.js environment\n if (typeof window === 'undefined') {\n try {\n // Use dynamic imports for Node.js modules in ESM context\n let http, https;\n \n try {\n http = await import('node:http');\n https = await import('node:https');\n } catch (e) {\n console.warn('[Connection Pool] Failed to import Node.js modules:', e);\n return;\n }\n \n // Create agents with keepAlive enabled and add tracking\n httpAgent = new http.Agent({ \n keepAlive: true,\n keepAliveMsecs: 30000, // 30 seconds\n maxSockets: 5 // Limit parallel connections\n });\n \n // Add tracking for connection reuse\n const originalCreateConnection = httpAgent.createConnection;\n httpAgent.createConnection = function(options: any, callback: any) {\n connectionCounter++;\n \n \n const socket = originalCreateConnection.call(this, options, callback);\n \n socket.on('reuse', () => {\n connectionReuseCounter++;\n });\n \n return socket;\n };\n \n httpsAgent = new https.Agent({ \n keepAlive: true,\n keepAliveMsecs: 30000,\n maxSockets: 5\n });\n \n } catch (error) {\n console.warn('[Connection Pool] Failed to initialize HTTP agents:', error);\n }\n }\n};\n\n// Try to initialize connection pool (as an async function)\n(async () => {\n try {\n await initConnectionPool();\n } catch (error) {\n console.warn('Failed to initialize connection pool:', error);\n }\n})();\n\n// Store the configured API URL\nlet configuredApiUrl: string | null = null;\n\n// Configure the API with a custom URL\nexport const configureApi = (apiUrl: string): void => {\n if (!apiUrl || typeof apiUrl !== 'string') {\n throw new Error('API URL must be a valid string');\n }\n configuredApiUrl = apiUrl;\n}\n\n// Get the configured API URL or throw an error if not configured\nconst getApiUrl = (): string => {\n if (!configuredApiUrl) {\n throw new Error('API URL not configured. Please call configureApi() with your server URL before using any API functions.');\n }\n return configuredApiUrl;\n};\n\n// Helper to get fetch options with connection pooling if available\nconst getFetchOptions = (apiUrl: string, options: RequestInit = {}): RequestInit | any => {\n const isHttps = apiUrl.startsWith('https:');\n \n // Only use agents in Node.js environment\n if (typeof window === 'undefined') {\n if (isHttps && httpsAgent) {\n // Using 'any' type to bypass TypeScript limitations with Node.js http.Agent\n return { ...options, agent: httpsAgent } as any;\n } else if (httpAgent) {\n return { ...options, agent: httpAgent } as any;\n }\n }\n \n // Browser environment\n const requestId = Date.now().toString(36) + Math.random().toString(36).substring(2);\n \n // Use keep-alive header in browser environments\n return {\n ...options,\n headers: {\n ...options.headers,\n 'Connection': 'keep-alive',\n 'X-Request-ID': requestId // Add unique ID to track requests\n }\n };\n};\n\nexport async function* streamChat(\n message: string,\n voiceEnabled: boolean\n): AsyncGenerator<StreamResponse> {\n try {\n // Get the API URL at the time of the request (allows for dynamic configuration)\n const API_URL = getApiUrl();\n \n // Skip the OPTIONS preflight check that was causing CORS issues\n const response = await fetch(`${API_URL}/chat`, getFetchOptions(API_URL, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n },\n body: JSON.stringify({ message, voiceEnabled }),\n }));\n\n if (!response.ok) {\n const errorText = await response.text();\n console.error(`API request failed: ${response.status} ${errorText}`);\n throw new Error(`Network response was not ok: ${response.status} ${errorText}`);\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\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 const lines = buffer.split('\\n');\n buffer = lines.pop() || '';\n\n for (const line of lines) {\n if (line.trim()) {\n try {\n const data = JSON.parse(line);\n \n // Normalize the response to use 'text' property\n if (data.content && !data.text) {\n data.text = data.content;\n }\n \n yield data;\n } catch (error: any) {\n // Silent error handling for JSON parse errors\n console.warn('Error parsing JSON chunk:', line);\n }\n }\n }\n }\n\n if (buffer) {\n try {\n const data = JSON.parse(buffer);\n \n // Normalize the response to use 'text' property\n if (data.content && !data.text) {\n data.text = data.content;\n }\n \n yield data;\n } catch (error: any) {\n // Silent error handling for JSON parse errors\n console.warn('Error parsing final JSON buffer:', buffer);\n }\n }\n } catch (error: any) {\n console.error('Chat API error:', error.message);\n yield { \n text: `Error connecting to chat server: ${error.message}`, \n done: true \n };\n }\n}"],"names":["httpAgent","httpsAgent","connectionCounter","connectionReuseCounter","initConnectionPool","http","https","e","originalCreateConnection","options","callback","socket","error","configuredApiUrl","configureApi","apiUrl","getApiUrl","getFetchOptions","isHttps","requestId","streamChat","message","voiceEnabled","API_URL","response","errorText","reader","_a","decoder","buffer","done","value","chunk","lines","line","data"],"mappings":"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"}
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// Track connections for debugging\nlet connectionCounter = 0;\nlet connectionReuseCounter = 0;\n\n// Define the StreamResponse interface\nexport interface StreamResponse {\n text: string;\n done: boolean;\n}\n\nexport interface ChatResponse {\n response: string;\n audio: string | null;\n}\n\n// Initialize the HTTP agents for connection pooling\nconst initConnectionPool = async () => {\n // Only run in Node.js environment\n if (typeof window === 'undefined') {\n try {\n // Use dynamic imports for Node.js modules in ESM context\n let http, https;\n \n try {\n http = await import('node:http');\n https = await import('node:https');\n } catch (e) {\n console.warn('[Connection Pool] Failed to import Node.js modules:', e);\n return;\n }\n \n // Create agents with keepAlive enabled and add tracking\n httpAgent = new http.Agent({ \n keepAlive: true,\n keepAliveMsecs: 30000, // 30 seconds\n maxSockets: 5 // Limit parallel connections\n });\n \n // Add tracking for connection reuse\n const originalCreateConnection = httpAgent.createConnection;\n httpAgent.createConnection = function(options: any, callback: any) {\n connectionCounter++;\n \n \n const socket = originalCreateConnection.call(this, options, callback);\n \n socket.on('reuse', () => {\n connectionReuseCounter++;\n });\n \n return socket;\n };\n \n httpsAgent = new https.Agent({ \n keepAlive: true,\n keepAliveMsecs: 30000,\n maxSockets: 5\n });\n \n } catch (error) {\n console.warn('[Connection Pool] Failed to initialize HTTP agents:', error);\n }\n }\n};\n\n// Try to initialize connection pool (as an async function)\n(async () => {\n try {\n await initConnectionPool();\n } catch (error) {\n console.warn('Failed to initialize connection pool:', error);\n }\n})();\n\n// Store the configured API URL\nlet configuredApiUrl: string | null = null;\n\n// Configure the API with a custom URL\nexport const configureApi = (apiUrl: string): void => {\n if (!apiUrl || typeof apiUrl !== 'string') {\n throw new Error('API URL must be a valid string');\n }\n configuredApiUrl = apiUrl;\n}\n\n// Get the configured API URL or throw an error if not configured\nconst getApiUrl = (): string => {\n if (!configuredApiUrl) {\n throw new Error('API URL not configured. Please call configureApi() with your server URL before using any API functions.');\n }\n return configuredApiUrl;\n};\n\n// Helper to get fetch options with connection pooling if available\nconst getFetchOptions = (apiUrl: string, options: RequestInit = {}): RequestInit | any => {\n const isHttps = apiUrl.startsWith('https:');\n \n // Only use agents in Node.js environment\n if (typeof window === 'undefined') {\n if (isHttps && httpsAgent) {\n // Using 'any' type to bypass TypeScript limitations with Node.js http.Agent\n return { ...options, agent: httpsAgent } as any;\n } else if (httpAgent) {\n return { ...options, agent: httpAgent } as any;\n }\n }\n \n // Browser environment\n const requestId = Date.now().toString(36) + Math.random().toString(36).substring(2);\n \n // Use keep-alive header in browser environments\n return {\n ...options,\n headers: {\n ...options.headers,\n 'Connection': 'keep-alive',\n 'X-Request-ID': requestId // Add unique ID to track requests\n }\n };\n};\n\nexport async function* streamChat(\n message: string,\n voiceEnabled: boolean,\n stream: boolean = true\n): AsyncGenerator<StreamResponse> {\n try {\n const API_URL = getApiUrl();\n \n const response = await fetch(`${API_URL}/chat`, getFetchOptions(API_URL, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n 'Accept': stream ? 'text/event-stream' : 'application/json'\n },\n body: JSON.stringify({ message, voiceEnabled, stream }),\n }));\n\n if (!response.ok) {\n const errorText = await response.text();\n console.error(`API request failed: ${response.status} ${errorText}`);\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 yield {\n text: data.response,\n done: true\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 currentFullText = ''; // Track full response to detect duplicates\n\n while (true) {\n const { done, value } = await reader.read();\n if (done) break;\n\n const chunk = decoder.decode(value, { stream: true });\n buffer += chunk;\n const lines = buffer.split('\\n');\n buffer = lines.pop() || '';\n\n for (const line of lines) {\n if (line.trim() && line.startsWith('data: ')) {\n try {\n const data = JSON.parse(line.slice(6)); // Remove 'data: ' prefix\n \n if (data.text) {\n // Check if this is a duplicate or overlapping chunk\n const newText = extractNewText(data.text, currentFullText);\n \n // Only yield if we have new text\n if (newText) {\n currentFullText += newText;\n yield {\n text: newText,\n done: data.done || false\n };\n } else if (data.done) {\n // Always send done signal even if no new text\n yield {\n text: '',\n done: true\n };\n }\n } else {\n // Pass through as-is if no text property\n yield {\n text: data.text || '',\n done: data.done || false\n };\n }\n } catch (error) {\n console.warn('Error parsing JSON chunk:', line);\n }\n }\n }\n }\n\n if (buffer && buffer.startsWith('data: ')) {\n try {\n const data = JSON.parse(buffer.slice(6));\n \n if (data.text) {\n // Check for duplicates in final chunk\n const newText = extractNewText(data.text, currentFullText);\n if (newText || data.done) {\n yield {\n text: newText || '',\n done: data.done || false\n };\n }\n } else {\n yield {\n text: data.text || '',\n done: data.done || false\n };\n }\n } catch (error) {\n console.warn('Error parsing final JSON buffer:', buffer);\n }\n }\n } catch (error: any) {\n console.error('Chat API error:', error.message);\n yield { \n text: `Error connecting to chat server: ${error.message}`, \n done: true \n };\n }\n}\n\n// Helper function to extract only new text from incoming chunks\nfunction extractNewText(incomingText: string, currentText: string): string {\n // If we have no current text, all text is new\n if (!currentText) return incomingText;\n \n // Handle exact duplicates\n if (currentText.endsWith(incomingText)) return '';\n\n // If incoming text is larger, check if it's an expanded version\n if (incomingText.length > currentText.length) {\n // If incoming text contains all of current text at the beginning,\n // only return the new part\n if (incomingText.startsWith(currentText)) {\n return incomingText.slice(currentText.length);\n }\n \n // Sometimes the FastAPI server might send growing chunks like \"Hel\" -> \"Hello\" -> \"Hello wo\" -> \"Hello world\"\n // Find the longest common prefix\n let i = 0;\n const minLength = Math.min(currentText.length, incomingText.length);\n while (i < minLength && currentText[i] === incomingText[i]) {\n i++;\n }\n \n // If there's significant overlap, extract only the new part\n if (i > currentText.length / 2) {\n return incomingText.slice(i);\n }\n }\n \n // Default: return the full text (this handles non-overlapping chunks)\n return incomingText;\n}"],"names":["httpAgent","httpsAgent","connectionCounter","connectionReuseCounter","initConnectionPool","http","https","e","originalCreateConnection","options","callback","socket","error","configuredApiUrl","configureApi","apiUrl","getApiUrl","getFetchOptions","isHttps","requestId","streamChat","message","voiceEnabled","stream","API_URL","response","errorText","reader","_a","decoder","buffer","currentFullText","done","value","chunk","lines","line","data","newText","extractNewText","incomingText","currentText","i","minLength"],"mappings":"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"}
package/dist/api.mjs CHANGED
@@ -1,29 +1,29 @@
1
- let l = null, d = null, y = 0, A = 0;
2
- const k = async () => {
1
+ let c = null, w = null, P = 0, v = 0;
2
+ const x = async () => {
3
3
  if (typeof window > "u")
4
4
  try {
5
- let e, o;
5
+ let e, t;
6
6
  try {
7
- e = await import("./__vite-browser-external-DYxpcVy9.js"), o = await import("./__vite-browser-external-DYxpcVy9.js");
8
- } catch (t) {
9
- console.warn("[Connection Pool] Failed to import Node.js modules:", t);
7
+ e = await import("./__vite-browser-external-DYxpcVy9.js"), t = await import("./__vite-browser-external-DYxpcVy9.js");
8
+ } catch (r) {
9
+ console.warn("[Connection Pool] Failed to import Node.js modules:", r);
10
10
  return;
11
11
  }
12
- l = new e.Agent({
12
+ c = new e.Agent({
13
13
  keepAlive: !0,
14
14
  keepAliveMsecs: 3e4,
15
15
  // 30 seconds
16
16
  maxSockets: 5
17
17
  // Limit parallel connections
18
18
  });
19
- const i = l.createConnection;
20
- l.createConnection = function(t, r) {
21
- y++;
22
- const a = i.call(this, t, r);
23
- return a.on("reuse", () => {
24
- A++;
25
- }), a;
26
- }, d = new o.Agent({
19
+ const n = c.createConnection;
20
+ c.createConnection = function(r, i) {
21
+ P++;
22
+ const s = n.call(this, r, i);
23
+ return s.on("reuse", () => {
24
+ v++;
25
+ }), s;
26
+ }, w = new t.Agent({
27
27
  keepAlive: !0,
28
28
  keepAliveMsecs: 3e4,
29
29
  maxSockets: 5
@@ -34,91 +34,136 @@ const k = async () => {
34
34
  };
35
35
  (async () => {
36
36
  try {
37
- await k();
37
+ await x();
38
38
  } catch (e) {
39
39
  console.warn("Failed to initialize connection pool:", e);
40
40
  }
41
41
  })();
42
- let f = null;
43
- const P = (e) => {
42
+ let p = null;
43
+ const N = (e) => {
44
44
  if (!e || typeof e != "string")
45
45
  throw new Error("API URL must be a valid string");
46
- f = e;
47
- }, m = () => {
48
- if (!f)
46
+ p = e;
47
+ }, S = () => {
48
+ if (!p)
49
49
  throw new Error("API URL not configured. Please call configureApi() with your server URL before using any API functions.");
50
- return f;
51
- }, C = (e, o = {}) => {
52
- const i = e.startsWith("https:");
50
+ return p;
51
+ }, b = (e, t = {}) => {
52
+ const n = e.startsWith("https:");
53
53
  if (typeof window > "u") {
54
- if (i && d)
55
- return { ...o, agent: d };
56
- if (l)
57
- return { ...o, agent: l };
54
+ if (n && w)
55
+ return { ...t, agent: w };
56
+ if (c)
57
+ return { ...t, agent: c };
58
58
  }
59
- const t = Date.now().toString(36) + Math.random().toString(36).substring(2);
59
+ const r = Date.now().toString(36) + Math.random().toString(36).substring(2);
60
60
  return {
61
- ...o,
61
+ ...t,
62
62
  headers: {
63
- ...o.headers,
63
+ ...t.headers,
64
64
  Connection: "keep-alive",
65
- "X-Request-ID": t
65
+ "X-Request-ID": r
66
66
  // Add unique ID to track requests
67
67
  }
68
68
  };
69
69
  };
70
- async function* x(e, o) {
71
- var i;
70
+ async function* I(e, t, n = !0) {
71
+ var r;
72
72
  try {
73
- const t = m(), r = await fetch(`${t}/chat`, C(t, {
73
+ const i = S(), s = await fetch(`${i}/chat`, b(i, {
74
74
  method: "POST",
75
75
  headers: {
76
- "Content-Type": "application/json"
76
+ "Content-Type": "application/json",
77
+ Accept: n ? "text/event-stream" : "application/json"
77
78
  },
78
- body: JSON.stringify({ message: e, voiceEnabled: o })
79
+ body: JSON.stringify({ message: e, voiceEnabled: t, stream: n })
79
80
  }));
80
- if (!r.ok) {
81
- const n = await r.text();
82
- throw console.error(`API request failed: ${r.status} ${n}`), new Error(`Network response was not ok: ${r.status} ${n}`);
81
+ if (!s.ok) {
82
+ const o = await s.text();
83
+ throw console.error(`API request failed: ${s.status} ${o}`), new Error(`Network response was not ok: ${s.status} ${o}`);
83
84
  }
84
- const a = (i = r.body) == null ? void 0 : i.getReader();
85
- if (!a) throw new Error("No reader available");
86
- const p = new TextDecoder();
87
- let s = "";
85
+ if (!n) {
86
+ yield {
87
+ text: (await s.json()).response,
88
+ done: !0
89
+ };
90
+ return;
91
+ }
92
+ const g = (r = s.body) == null ? void 0 : r.getReader();
93
+ if (!g) throw new Error("No reader available");
94
+ const k = new TextDecoder();
95
+ let a = "", u = "";
88
96
  for (; ; ) {
89
- const { done: n, value: w } = await a.read();
90
- if (n)
91
- break;
92
- const g = p.decode(w, { stream: !0 });
93
- s += g;
94
- const h = s.split(`
97
+ const { done: o, value: d } = await g.read();
98
+ if (o) break;
99
+ const C = k.decode(d, { stream: !0 });
100
+ a += C;
101
+ const y = a.split(`
95
102
  `);
96
- s = h.pop() || "";
97
- for (const u of h)
98
- if (u.trim())
103
+ a = y.pop() || "";
104
+ for (const f of y)
105
+ if (f.trim() && f.startsWith("data: "))
99
106
  try {
100
- const c = JSON.parse(u);
101
- c.content && !c.text && (c.text = c.content), yield c;
107
+ const l = JSON.parse(f.slice(6));
108
+ if (l.text) {
109
+ const h = A(l.text, u);
110
+ h ? (u += h, yield {
111
+ text: h,
112
+ done: l.done || !1
113
+ }) : l.done && (yield {
114
+ text: "",
115
+ done: !0
116
+ });
117
+ } else
118
+ yield {
119
+ text: l.text || "",
120
+ done: l.done || !1
121
+ };
102
122
  } catch {
103
- console.warn("Error parsing JSON chunk:", u);
123
+ console.warn("Error parsing JSON chunk:", f);
104
124
  }
105
125
  }
106
- if (s)
126
+ if (a && a.startsWith("data: "))
107
127
  try {
108
- const n = JSON.parse(s);
109
- n.content && !n.text && (n.text = n.content), yield n;
128
+ const o = JSON.parse(a.slice(6));
129
+ if (o.text) {
130
+ const d = A(o.text, u);
131
+ (d || o.done) && (yield {
132
+ text: d || "",
133
+ done: o.done || !1
134
+ });
135
+ } else
136
+ yield {
137
+ text: o.text || "",
138
+ done: o.done || !1
139
+ };
110
140
  } catch {
111
- console.warn("Error parsing final JSON buffer:", s);
141
+ console.warn("Error parsing final JSON buffer:", a);
112
142
  }
113
- } catch (t) {
114
- console.error("Chat API error:", t.message), yield {
115
- text: `Error connecting to chat server: ${t.message}`,
143
+ } catch (i) {
144
+ console.error("Chat API error:", i.message), yield {
145
+ text: `Error connecting to chat server: ${i.message}`,
116
146
  done: !0
117
147
  };
118
148
  }
119
149
  }
150
+ function A(e, t) {
151
+ if (!t) return e;
152
+ if (t.endsWith(e)) return "";
153
+ if (e.length > t.length) {
154
+ if (e.startsWith(t))
155
+ return e.slice(t.length);
156
+ let n = 0;
157
+ const r = Math.min(t.length, e.length);
158
+ for (; n < r && t[n] === e[n]; )
159
+ n++;
160
+ if (n > t.length / 2)
161
+ return e.slice(n);
162
+ }
163
+ return e;
164
+ }
120
165
  export {
121
- P as configureApi,
122
- x as streamChat
166
+ N as configureApi,
167
+ I as streamChat
123
168
  };
124
169
  //# 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// Track connections for debugging\nlet connectionCounter = 0;\nlet connectionReuseCounter = 0;\n\n// Define the StreamResponse interface\nexport interface StreamResponse {\n text?: string;\n content?: string; // Adding alternative property name\n done?: boolean;\n type?: string; // Type of response (e.g., 'text', 'audio')\n}\n\n// Initialize the HTTP agents for connection pooling\nconst initConnectionPool = async () => {\n // Only run in Node.js environment\n if (typeof window === 'undefined') {\n try {\n // Use dynamic imports for Node.js modules in ESM context\n let http, https;\n \n try {\n http = await import('node:http');\n https = await import('node:https');\n } catch (e) {\n console.warn('[Connection Pool] Failed to import Node.js modules:', e);\n return;\n }\n \n // Create agents with keepAlive enabled and add tracking\n httpAgent = new http.Agent({ \n keepAlive: true,\n keepAliveMsecs: 30000, // 30 seconds\n maxSockets: 5 // Limit parallel connections\n });\n \n // Add tracking for connection reuse\n const originalCreateConnection = httpAgent.createConnection;\n httpAgent.createConnection = function(options: any, callback: any) {\n connectionCounter++;\n \n \n const socket = originalCreateConnection.call(this, options, callback);\n \n socket.on('reuse', () => {\n connectionReuseCounter++;\n });\n \n return socket;\n };\n \n httpsAgent = new https.Agent({ \n keepAlive: true,\n keepAliveMsecs: 30000,\n maxSockets: 5\n });\n \n } catch (error) {\n console.warn('[Connection Pool] Failed to initialize HTTP agents:', error);\n }\n }\n};\n\n// Try to initialize connection pool (as an async function)\n(async () => {\n try {\n await initConnectionPool();\n } catch (error) {\n console.warn('Failed to initialize connection pool:', error);\n }\n})();\n\n// Store the configured API URL\nlet configuredApiUrl: string | null = null;\n\n// Configure the API with a custom URL\nexport const configureApi = (apiUrl: string): void => {\n if (!apiUrl || typeof apiUrl !== 'string') {\n throw new Error('API URL must be a valid string');\n }\n configuredApiUrl = apiUrl;\n}\n\n// Get the configured API URL or throw an error if not configured\nconst getApiUrl = (): string => {\n if (!configuredApiUrl) {\n throw new Error('API URL not configured. Please call configureApi() with your server URL before using any API functions.');\n }\n return configuredApiUrl;\n};\n\n// Helper to get fetch options with connection pooling if available\nconst getFetchOptions = (apiUrl: string, options: RequestInit = {}): RequestInit | any => {\n const isHttps = apiUrl.startsWith('https:');\n \n // Only use agents in Node.js environment\n if (typeof window === 'undefined') {\n if (isHttps && httpsAgent) {\n // Using 'any' type to bypass TypeScript limitations with Node.js http.Agent\n return { ...options, agent: httpsAgent } as any;\n } else if (httpAgent) {\n return { ...options, agent: httpAgent } as any;\n }\n }\n \n // Browser environment\n const requestId = Date.now().toString(36) + Math.random().toString(36).substring(2);\n \n // Use keep-alive header in browser environments\n return {\n ...options,\n headers: {\n ...options.headers,\n 'Connection': 'keep-alive',\n 'X-Request-ID': requestId // Add unique ID to track requests\n }\n };\n};\n\nexport async function* streamChat(\n message: string,\n voiceEnabled: boolean\n): AsyncGenerator<StreamResponse> {\n try {\n // Get the API URL at the time of the request (allows for dynamic configuration)\n const API_URL = getApiUrl();\n \n // Skip the OPTIONS preflight check that was causing CORS issues\n const response = await fetch(`${API_URL}/chat`, getFetchOptions(API_URL, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n },\n body: JSON.stringify({ message, voiceEnabled }),\n }));\n\n if (!response.ok) {\n const errorText = await response.text();\n console.error(`API request failed: ${response.status} ${errorText}`);\n throw new Error(`Network response was not ok: ${response.status} ${errorText}`);\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\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 const lines = buffer.split('\\n');\n buffer = lines.pop() || '';\n\n for (const line of lines) {\n if (line.trim()) {\n try {\n const data = JSON.parse(line);\n \n // Normalize the response to use 'text' property\n if (data.content && !data.text) {\n data.text = data.content;\n }\n \n yield data;\n } catch (error: any) {\n // Silent error handling for JSON parse errors\n console.warn('Error parsing JSON chunk:', line);\n }\n }\n }\n }\n\n if (buffer) {\n try {\n const data = JSON.parse(buffer);\n \n // Normalize the response to use 'text' property\n if (data.content && !data.text) {\n data.text = data.content;\n }\n \n yield data;\n } catch (error: any) {\n // Silent error handling for JSON parse errors\n console.warn('Error parsing final JSON buffer:', buffer);\n }\n }\n } catch (error: any) {\n console.error('Chat API error:', error.message);\n yield { \n text: `Error connecting to chat server: ${error.message}`, \n done: true \n };\n }\n}"],"names":["httpAgent","httpsAgent","connectionCounter","connectionReuseCounter","initConnectionPool","http","https","e","originalCreateConnection","options","callback","socket","error","configuredApiUrl","configureApi","apiUrl","getApiUrl","getFetchOptions","isHttps","requestId","streamChat","message","voiceEnabled","_a","API_URL","response","errorText","reader","decoder","buffer","done","value","chunk","lines","line","data"],"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// Track connections for debugging\nlet connectionCounter = 0;\nlet connectionReuseCounter = 0;\n\n// Define the StreamResponse interface\nexport interface StreamResponse {\n text: string;\n done: boolean;\n}\n\nexport interface ChatResponse {\n response: string;\n audio: string | null;\n}\n\n// Initialize the HTTP agents for connection pooling\nconst initConnectionPool = async () => {\n // Only run in Node.js environment\n if (typeof window === 'undefined') {\n try {\n // Use dynamic imports for Node.js modules in ESM context\n let http, https;\n \n try {\n http = await import('node:http');\n https = await import('node:https');\n } catch (e) {\n console.warn('[Connection Pool] Failed to import Node.js modules:', e);\n return;\n }\n \n // Create agents with keepAlive enabled and add tracking\n httpAgent = new http.Agent({ \n keepAlive: true,\n keepAliveMsecs: 30000, // 30 seconds\n maxSockets: 5 // Limit parallel connections\n });\n \n // Add tracking for connection reuse\n const originalCreateConnection = httpAgent.createConnection;\n httpAgent.createConnection = function(options: any, callback: any) {\n connectionCounter++;\n \n \n const socket = originalCreateConnection.call(this, options, callback);\n \n socket.on('reuse', () => {\n connectionReuseCounter++;\n });\n \n return socket;\n };\n \n httpsAgent = new https.Agent({ \n keepAlive: true,\n keepAliveMsecs: 30000,\n maxSockets: 5\n });\n \n } catch (error) {\n console.warn('[Connection Pool] Failed to initialize HTTP agents:', error);\n }\n }\n};\n\n// Try to initialize connection pool (as an async function)\n(async () => {\n try {\n await initConnectionPool();\n } catch (error) {\n console.warn('Failed to initialize connection pool:', error);\n }\n})();\n\n// Store the configured API URL\nlet configuredApiUrl: string | null = null;\n\n// Configure the API with a custom URL\nexport const configureApi = (apiUrl: string): void => {\n if (!apiUrl || typeof apiUrl !== 'string') {\n throw new Error('API URL must be a valid string');\n }\n configuredApiUrl = apiUrl;\n}\n\n// Get the configured API URL or throw an error if not configured\nconst getApiUrl = (): string => {\n if (!configuredApiUrl) {\n throw new Error('API URL not configured. Please call configureApi() with your server URL before using any API functions.');\n }\n return configuredApiUrl;\n};\n\n// Helper to get fetch options with connection pooling if available\nconst getFetchOptions = (apiUrl: string, options: RequestInit = {}): RequestInit | any => {\n const isHttps = apiUrl.startsWith('https:');\n \n // Only use agents in Node.js environment\n if (typeof window === 'undefined') {\n if (isHttps && httpsAgent) {\n // Using 'any' type to bypass TypeScript limitations with Node.js http.Agent\n return { ...options, agent: httpsAgent } as any;\n } else if (httpAgent) {\n return { ...options, agent: httpAgent } as any;\n }\n }\n \n // Browser environment\n const requestId = Date.now().toString(36) + Math.random().toString(36).substring(2);\n \n // Use keep-alive header in browser environments\n return {\n ...options,\n headers: {\n ...options.headers,\n 'Connection': 'keep-alive',\n 'X-Request-ID': requestId // Add unique ID to track requests\n }\n };\n};\n\nexport async function* streamChat(\n message: string,\n voiceEnabled: boolean,\n stream: boolean = true\n): AsyncGenerator<StreamResponse> {\n try {\n const API_URL = getApiUrl();\n \n const response = await fetch(`${API_URL}/chat`, getFetchOptions(API_URL, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n 'Accept': stream ? 'text/event-stream' : 'application/json'\n },\n body: JSON.stringify({ message, voiceEnabled, stream }),\n }));\n\n if (!response.ok) {\n const errorText = await response.text();\n console.error(`API request failed: ${response.status} ${errorText}`);\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 yield {\n text: data.response,\n done: true\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 currentFullText = ''; // Track full response to detect duplicates\n\n while (true) {\n const { done, value } = await reader.read();\n if (done) break;\n\n const chunk = decoder.decode(value, { stream: true });\n buffer += chunk;\n const lines = buffer.split('\\n');\n buffer = lines.pop() || '';\n\n for (const line of lines) {\n if (line.trim() && line.startsWith('data: ')) {\n try {\n const data = JSON.parse(line.slice(6)); // Remove 'data: ' prefix\n \n if (data.text) {\n // Check if this is a duplicate or overlapping chunk\n const newText = extractNewText(data.text, currentFullText);\n \n // Only yield if we have new text\n if (newText) {\n currentFullText += newText;\n yield {\n text: newText,\n done: data.done || false\n };\n } else if (data.done) {\n // Always send done signal even if no new text\n yield {\n text: '',\n done: true\n };\n }\n } else {\n // Pass through as-is if no text property\n yield {\n text: data.text || '',\n done: data.done || false\n };\n }\n } catch (error) {\n console.warn('Error parsing JSON chunk:', line);\n }\n }\n }\n }\n\n if (buffer && buffer.startsWith('data: ')) {\n try {\n const data = JSON.parse(buffer.slice(6));\n \n if (data.text) {\n // Check for duplicates in final chunk\n const newText = extractNewText(data.text, currentFullText);\n if (newText || data.done) {\n yield {\n text: newText || '',\n done: data.done || false\n };\n }\n } else {\n yield {\n text: data.text || '',\n done: data.done || false\n };\n }\n } catch (error) {\n console.warn('Error parsing final JSON buffer:', buffer);\n }\n }\n } catch (error: any) {\n console.error('Chat API error:', error.message);\n yield { \n text: `Error connecting to chat server: ${error.message}`, \n done: true \n };\n }\n}\n\n// Helper function to extract only new text from incoming chunks\nfunction extractNewText(incomingText: string, currentText: string): string {\n // If we have no current text, all text is new\n if (!currentText) return incomingText;\n \n // Handle exact duplicates\n if (currentText.endsWith(incomingText)) return '';\n\n // If incoming text is larger, check if it's an expanded version\n if (incomingText.length > currentText.length) {\n // If incoming text contains all of current text at the beginning,\n // only return the new part\n if (incomingText.startsWith(currentText)) {\n return incomingText.slice(currentText.length);\n }\n \n // Sometimes the FastAPI server might send growing chunks like \"Hel\" -> \"Hello\" -> \"Hello wo\" -> \"Hello world\"\n // Find the longest common prefix\n let i = 0;\n const minLength = Math.min(currentText.length, incomingText.length);\n while (i < minLength && currentText[i] === incomingText[i]) {\n i++;\n }\n \n // If there's significant overlap, extract only the new part\n if (i > currentText.length / 2) {\n return incomingText.slice(i);\n }\n }\n \n // Default: return the full text (this handles non-overlapping chunks)\n return incomingText;\n}"],"names":["httpAgent","httpsAgent","connectionCounter","connectionReuseCounter","initConnectionPool","http","https","e","originalCreateConnection","options","callback","socket","error","configuredApiUrl","configureApi","apiUrl","getApiUrl","getFetchOptions","isHttps","requestId","streamChat","message","voiceEnabled","stream","_a","API_URL","response","errorText","reader","decoder","buffer","currentFullText","done","value","chunk","lines","line","data","newText","extractNewText","incomingText","currentText","i","minLength"],"mappings":"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"}
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@schmitech/chatbot-api",
3
3
  "private": false,
4
- "version": "0.1.1",
4
+ "version": "0.3.0",
5
5
  "description": "API client for the Chatbot server",
6
6
  "type": "module",
7
7
  "main": "./dist/api.cjs",
@@ -25,7 +25,8 @@
25
25
  "preview": "vite preview",
26
26
  "test": "vitest",
27
27
  "test:watch": "vitest",
28
- "test-query": "node --import 'data:text/javascript,import { register } from \"node:module\"; import { pathToFileURL } from \"node:url\"; register(\"ts-node/esm\", pathToFileURL(\"./\"));' ./test/run-query.js"
28
+ "test-query": "node --import 'data:text/javascript,import { register } from \"node:module\"; import { pathToFileURL } from \"node:url\"; register(\"ts-node/esm\", pathToFileURL(\"./\"));' ./test/run-query.js",
29
+ "test-query-from-pairs": "node --import 'data:text/javascript,import { register } from \"node:module\"; import { pathToFileURL } from \"node:url\"; register(\"ts-node/esm\", pathToFileURL(\"./\"));' ./test/run-query-from-pairs.js"
29
30
  },
30
31
  "dependencies": {
31
32
  "cors": "^2.8.5",