@schmitech/chatbot-api 0.2.1 → 0.3.1

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/api.d.ts CHANGED
@@ -1,8 +1,9 @@
1
1
  export interface StreamResponse {
2
- text?: string;
3
- content?: string;
4
- done?: boolean;
5
- type?: string;
2
+ text: string;
3
+ done: boolean;
6
4
  }
7
- export declare const configureApi: (apiUrl: string) => void;
8
- export declare function streamChat(message: string, voiceEnabled: boolean): AsyncGenerator<StreamResponse>;
5
+ export interface ChatResponse {
6
+ response: string;
7
+ }
8
+ export declare const configureApi: (apiUrl: string, apiKey: string) => void;
9
+ export declare function streamChat(message: string, 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 w=null,g=null;const b=(t,e)=>{if(!t||typeof t!="string")throw new Error("API URL must be a valid string");if(!e||typeof e!="string")throw new Error("API key must be a valid string");w=t,g=e},I=()=>{if(!w)throw new Error("API URL not configured. Please call configureApi() with your server URL before using any API functions.");return w},E=()=>{if(!g)throw new Error("API key not configured. Please call configureApi() with your API key before using any API functions.");return g},k=(t,e={})=>{t.startsWith("https:");const r=Date.now().toString(36)+Math.random().toString(36).substring(2);return{...e,headers:{...e.headers,Connection:"keep-alive","X-Request-ID":r,"X-API-Key":E()}}};async function*S(t,e=!0){var r;try{const s=I(),i=await fetch(`${s}/chat`,k(s,{method:"POST",headers:{"Content-Type":"application/json",Accept:e?"text/event-stream":"application/json"},body:JSON.stringify({message:t,stream:e})}));if(!i.ok){const o=await i.text();throw console.error(`API request failed: ${i.status} ${o}`),new Error(`Network response was not ok: ${i.status} ${o}`)}if(!e){yield{text:(await i.json()).response,done:!0};return}const y=(r=i.body)==null?void 0:r.getReader();if(!y)throw new Error("No reader available");const P=new TextDecoder;let a="",f="";for(;;){const{done:o,value:n}=await y.read();if(o)break;const c=P.decode(n,{stream:!0});a+=c;const p=a.split(`
2
+ `);a=p.pop()||"";for(const d of p)if(d.trim()&&d.startsWith("data: "))try{const u=d.slice(6).trim(),l=JSON.parse(u);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(u){console.warn("Error parsing JSON chunk:",d,"Error:",u)}}if(a&&a.startsWith("data: "))try{const o=a.slice(6).trim(),n=JSON.parse(o);if(n.text){const c=A(n.text,f);(c||n.done)&&(yield{text:c||"",done:n.done||!1})}else yield{text:n.text||"",done:n.done||!1}}catch(o){console.warn("Error parsing final JSON buffer:",a,"Error:",o)}}catch(s){console.error("Chat API error:",s.message),yield{text:`Error connecting to chat server: ${s.message}`,done:!0}}}function A(t,e){if(!e)return t;if(e.endsWith(t))return"";if(t.length>e.length){if(t.startsWith(e))return t.slice(e.length);let r=0;const s=Math.min(e.length,t.length);for(;r<s&&e[r]===t[r];)r++;if(r>e.length/2)return t.slice(r)}return t}exports.configureApi=b;exports.streamChat=S;
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// Define the StreamResponse interface\nexport interface StreamResponse {\n text: string;\n done: boolean;\n}\n\nexport interface ChatResponse {\n response: string;\n}\n\n// Store the configured API URL and key\nlet configuredApiUrl: string | null = null;\nlet configuredApiKey: string | null = null;\n\n// Configure the API with a custom URL and API key\nexport const configureApi = (apiUrl: string, apiKey: string): void => {\n if (!apiUrl || typeof apiUrl !== 'string') {\n throw new Error('API URL must be a valid string');\n }\n if (!apiKey || typeof apiKey !== 'string') {\n throw new Error('API key must be a valid string');\n }\n configuredApiUrl = apiUrl;\n configuredApiKey = apiKey;\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// Get the configured API key or throw an error if not configured\nconst getApiKey = (): string => {\n if (!configuredApiKey) {\n throw new Error('API key not configured. Please call configureApi() with your API key before using any API functions.');\n }\n return configuredApiKey;\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 'X-API-Key': getApiKey() // Add API key to headers\n }\n };\n};\n\nexport async function* streamChat(\n message: string,\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, 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 // Properly extract the JSON part by trimming whitespace after 'data:'\n const jsonText = line.slice(6).trim();\n const data = JSON.parse(jsonText);\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, 'Error:', error);\n }\n }\n }\n }\n\n if (buffer && buffer.startsWith('data: ')) {\n try {\n // Properly extract the JSON part by trimming whitespace after 'data:'\n const jsonText = buffer.slice(6).trim();\n const data = JSON.parse(jsonText);\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, 'Error:', error);\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":["configuredApiUrl","configuredApiKey","configureApi","apiUrl","apiKey","getApiUrl","getApiKey","getFetchOptions","options","requestId","streamChat","message","stream","API_URL","response","errorText","reader","_a","decoder","buffer","currentFullText","done","value","chunk","lines","line","jsonText","data","newText","extractNewText","error","incomingText","currentText","i","minLength"],"mappings":"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"}
package/dist/api.mjs CHANGED
@@ -1,124 +1,130 @@
1
- let l = null, d = null, y = 0, A = 0;
2
- const k = async () => {
3
- if (typeof window > "u")
4
- try {
5
- let e, o;
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);
10
- return;
11
- }
12
- l = new e.Agent({
13
- keepAlive: !0,
14
- keepAliveMsecs: 3e4,
15
- // 30 seconds
16
- maxSockets: 5
17
- // Limit parallel connections
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({
27
- keepAlive: !0,
28
- keepAliveMsecs: 3e4,
29
- maxSockets: 5
30
- });
31
- } catch (e) {
32
- console.warn("[Connection Pool] Failed to initialize HTTP agents:", e);
33
- }
34
- };
35
- (async () => {
36
- try {
37
- await k();
38
- } catch (e) {
39
- console.warn("Failed to initialize connection pool:", e);
40
- }
41
- })();
42
- let f = null;
43
- const P = (e) => {
44
- if (!e || typeof e != "string")
1
+ let w = null, g = null;
2
+ const k = (t, e) => {
3
+ if (!t || typeof t != "string")
45
4
  throw new Error("API URL must be a valid string");
46
- f = e;
47
- }, m = () => {
48
- if (!f)
5
+ if (!e || typeof e != "string")
6
+ throw new Error("API key must be a valid string");
7
+ w = t, g = e;
8
+ }, I = () => {
9
+ if (!w)
49
10
  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:");
53
- if (typeof window > "u") {
54
- if (i && d)
55
- return { ...o, agent: d };
56
- if (l)
57
- return { ...o, agent: l };
58
- }
59
- const t = Date.now().toString(36) + Math.random().toString(36).substring(2);
11
+ return w;
12
+ }, b = () => {
13
+ if (!g)
14
+ throw new Error("API key not configured. Please call configureApi() with your API key before using any API functions.");
15
+ return g;
16
+ }, E = (t, e = {}) => {
17
+ t.startsWith("https:");
18
+ const r = Date.now().toString(36) + Math.random().toString(36).substring(2);
60
19
  return {
61
- ...o,
20
+ ...e,
62
21
  headers: {
63
- ...o.headers,
22
+ ...e.headers,
64
23
  Connection: "keep-alive",
65
- "X-Request-ID": t
24
+ "X-Request-ID": r,
66
25
  // Add unique ID to track requests
26
+ "X-API-Key": b()
27
+ // Add API key to headers
67
28
  }
68
29
  };
69
30
  };
70
- async function* x(e, o) {
71
- var i;
31
+ async function* x(t, e = !0) {
32
+ var r;
72
33
  try {
73
- const t = m(), r = await fetch(`${t}/chat`, C(t, {
34
+ const s = I(), i = await fetch(`${s}/chat`, E(s, {
74
35
  method: "POST",
75
36
  headers: {
76
- "Content-Type": "application/json"
37
+ "Content-Type": "application/json",
38
+ Accept: e ? "text/event-stream" : "application/json"
77
39
  },
78
- body: JSON.stringify({ message: e, voiceEnabled: o })
40
+ body: JSON.stringify({ message: t, stream: e })
79
41
  }));
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}`);
42
+ if (!i.ok) {
43
+ const o = await i.text();
44
+ throw console.error(`API request failed: ${i.status} ${o}`), new Error(`Network response was not ok: ${i.status} ${o}`);
83
45
  }
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 = "";
46
+ if (!e) {
47
+ yield {
48
+ text: (await i.json()).response,
49
+ done: !0
50
+ };
51
+ return;
52
+ }
53
+ const y = (r = i.body) == null ? void 0 : r.getReader();
54
+ if (!y) throw new Error("No reader available");
55
+ const P = new TextDecoder();
56
+ let a = "", f = "";
88
57
  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(`
58
+ const { done: o, value: n } = await y.read();
59
+ if (o) break;
60
+ const c = P.decode(n, { stream: !0 });
61
+ a += c;
62
+ const p = a.split(`
95
63
  `);
96
- s = h.pop() || "";
97
- for (const u of h)
98
- if (u.trim())
64
+ a = p.pop() || "";
65
+ for (const d of p)
66
+ if (d.trim() && d.startsWith("data: "))
99
67
  try {
100
- const c = JSON.parse(u);
101
- c.content && !c.text && (c.text = c.content), yield c;
102
- } catch {
103
- console.warn("Error parsing JSON chunk:", u);
68
+ const h = d.slice(6).trim(), l = JSON.parse(h);
69
+ if (l.text) {
70
+ const u = A(l.text, f);
71
+ u ? (f += u, yield {
72
+ text: u,
73
+ done: l.done || !1
74
+ }) : l.done && (yield {
75
+ text: "",
76
+ done: !0
77
+ });
78
+ } else
79
+ yield {
80
+ text: l.text || "",
81
+ done: l.done || !1
82
+ };
83
+ } catch (h) {
84
+ console.warn("Error parsing JSON chunk:", d, "Error:", h);
104
85
  }
105
86
  }
106
- if (s)
87
+ if (a && a.startsWith("data: "))
107
88
  try {
108
- const n = JSON.parse(s);
109
- n.content && !n.text && (n.text = n.content), yield n;
110
- } catch {
111
- console.warn("Error parsing final JSON buffer:", s);
89
+ const o = a.slice(6).trim(), n = JSON.parse(o);
90
+ if (n.text) {
91
+ const c = A(n.text, f);
92
+ (c || n.done) && (yield {
93
+ text: c || "",
94
+ done: n.done || !1
95
+ });
96
+ } else
97
+ yield {
98
+ text: n.text || "",
99
+ done: n.done || !1
100
+ };
101
+ } catch (o) {
102
+ console.warn("Error parsing final JSON buffer:", a, "Error:", o);
112
103
  }
113
- } catch (t) {
114
- console.error("Chat API error:", t.message), yield {
115
- text: `Error connecting to chat server: ${t.message}`,
104
+ } catch (s) {
105
+ console.error("Chat API error:", s.message), yield {
106
+ text: `Error connecting to chat server: ${s.message}`,
116
107
  done: !0
117
108
  };
118
109
  }
119
110
  }
111
+ function A(t, e) {
112
+ if (!e) return t;
113
+ if (e.endsWith(t)) return "";
114
+ if (t.length > e.length) {
115
+ if (t.startsWith(e))
116
+ return t.slice(e.length);
117
+ let r = 0;
118
+ const s = Math.min(e.length, t.length);
119
+ for (; r < s && e[r] === t[r]; )
120
+ r++;
121
+ if (r > e.length / 2)
122
+ return t.slice(r);
123
+ }
124
+ return t;
125
+ }
120
126
  export {
121
- P as configureApi,
127
+ k as configureApi,
122
128
  x as streamChat
123
129
  };
124
130
  //# sourceMappingURL=api.mjs.map
@@ -0,0 +1,9 @@
1
+ export interface StreamResponse {
2
+ text?: string;
3
+ content?: string;
4
+ done?: boolean;
5
+ type?: string;
6
+ }
7
+
8
+ export function configureApi(apiUrl: string, apiKey?: string): void;
9
+ export function streamChat(message: string, voiceEnabled: boolean): AsyncGenerator<StreamResponse>;
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// Define the StreamResponse interface\nexport interface StreamResponse {\n text: string;\n done: boolean;\n}\n\nexport interface ChatResponse {\n response: string;\n}\n\n// Store the configured API URL and key\nlet configuredApiUrl: string | null = null;\nlet configuredApiKey: string | null = null;\n\n// Configure the API with a custom URL and API key\nexport const configureApi = (apiUrl: string, apiKey: string): void => {\n if (!apiUrl || typeof apiUrl !== 'string') {\n throw new Error('API URL must be a valid string');\n }\n if (!apiKey || typeof apiKey !== 'string') {\n throw new Error('API key must be a valid string');\n }\n configuredApiUrl = apiUrl;\n configuredApiKey = apiKey;\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// Get the configured API key or throw an error if not configured\nconst getApiKey = (): string => {\n if (!configuredApiKey) {\n throw new Error('API key not configured. Please call configureApi() with your API key before using any API functions.');\n }\n return configuredApiKey;\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 'X-API-Key': getApiKey() // Add API key to headers\n }\n };\n};\n\nexport async function* streamChat(\n message: string,\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, 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 // Properly extract the JSON part by trimming whitespace after 'data:'\n const jsonText = line.slice(6).trim();\n const data = JSON.parse(jsonText);\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, 'Error:', error);\n }\n }\n }\n }\n\n if (buffer && buffer.startsWith('data: ')) {\n try {\n // Properly extract the JSON part by trimming whitespace after 'data:'\n const jsonText = buffer.slice(6).trim();\n const data = JSON.parse(jsonText);\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, 'Error:', error);\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":["configuredApiUrl","configuredApiKey","configureApi","apiUrl","apiKey","getApiUrl","getApiKey","getFetchOptions","options","requestId","streamChat","message","stream","_a","API_URL","response","errorText","reader","decoder","buffer","currentFullText","done","value","chunk","lines","line","jsonText","data","newText","extractNewText","error","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.2.1",
4
+ "version": "0.3.1",
5
5
  "description": "API client for the Chatbot server",
6
6
  "type": "module",
7
7
  "main": "./dist/api.cjs",
@@ -1,2 +0,0 @@
1
- "use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const e={};exports.default=e;
2
- //# sourceMappingURL=__vite-browser-external-BcPniuRQ.cjs.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"__vite-browser-external-BcPniuRQ.cjs","sources":["../__vite-browser-external"],"sourcesContent":["export default {}"],"names":["__viteBrowserExternal"],"mappings":"gFAAA,MAAeA,EAAA,CAAA"}
@@ -1,5 +0,0 @@
1
- const e = {};
2
- export {
3
- e as default
4
- };
5
- //# sourceMappingURL=__vite-browser-external-DYxpcVy9.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"__vite-browser-external-DYxpcVy9.js","sources":["../__vite-browser-external"],"sourcesContent":["export default {}"],"names":["__viteBrowserExternal"],"mappings":"AAAA,MAAeA,IAAA,CAAA;"}