@schmitech/chatbot-api 0.1.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 +262 -0
- package/api.d.ts +8 -0
- package/dist/__vite-browser-external-BcPniuRQ.cjs +2 -0
- package/dist/__vite-browser-external-BcPniuRQ.cjs.map +1 -0
- package/dist/__vite-browser-external-DYxpcVy9.js +5 -0
- package/dist/__vite-browser-external-DYxpcVy9.js.map +1 -0
- package/dist/api.cjs +3 -0
- package/dist/api.cjs.map +1 -0
- package/dist/api.mjs +124 -0
- package/dist/api.mjs.map +1 -0
- package/package.json +60 -0
package/README.md
ADDED
|
@@ -0,0 +1,262 @@
|
|
|
1
|
+
# Chatbot API Client
|
|
2
|
+
|
|
3
|
+
A JavaScript/TypeScript client library for interacting with the Chatbot server.
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
### Option 1: Using npm link (for local development)
|
|
8
|
+
|
|
9
|
+
To use this library in other projects on your local machine during development:
|
|
10
|
+
|
|
11
|
+
1. In this library directory, run:
|
|
12
|
+
```bash
|
|
13
|
+
npm run build
|
|
14
|
+
npm link
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
2. In your project directory, run:
|
|
18
|
+
```bash
|
|
19
|
+
npm link chatbot-api
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
3. Now you can import and use the library in your project:
|
|
23
|
+
```javascript
|
|
24
|
+
import { streamChat } from 'chatbot-api';
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
### Option 2: Installing from a local directory
|
|
28
|
+
|
|
29
|
+
You can also install the package directly from the local directory:
|
|
30
|
+
|
|
31
|
+
1. In this library directory, run:
|
|
32
|
+
```bash
|
|
33
|
+
npm run build
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
2. In your project directory, run:
|
|
37
|
+
```bash
|
|
38
|
+
npm install /path/to/qa-chatbot-server/api
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
3. Now you can import and use the library in your project:
|
|
42
|
+
```javascript
|
|
43
|
+
import { streamChat } from 'chatbot-api';
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
### Option 3: Publishing to npm (for production)
|
|
47
|
+
|
|
48
|
+
To make this library available to anyone via npm:
|
|
49
|
+
|
|
50
|
+
1. Create an account on npmjs.com if you don't have one
|
|
51
|
+
2. Login to npm from the command line:
|
|
52
|
+
```bash
|
|
53
|
+
npm login
|
|
54
|
+
```
|
|
55
|
+
3. Update the package name in package.json to ensure it's unique
|
|
56
|
+
4. Publish the package:
|
|
57
|
+
```bash
|
|
58
|
+
npm publish
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
## Usage
|
|
62
|
+
|
|
63
|
+
### Configuration (Required)
|
|
64
|
+
|
|
65
|
+
Before using any API functions, you **must** configure the client with your server URL:
|
|
66
|
+
|
|
67
|
+
```javascript
|
|
68
|
+
import { configureApi, streamChat } from 'chatbot-api';
|
|
69
|
+
|
|
70
|
+
// Configure the API with your server URL
|
|
71
|
+
configureApi('https://your-api-server.com');
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
If you don't configure the API before calling other functions, an error will be thrown.
|
|
75
|
+
|
|
76
|
+
### Basic Usage
|
|
77
|
+
|
|
78
|
+
```javascript
|
|
79
|
+
import { configureApi, streamChat } from 'chatbot-api';
|
|
80
|
+
|
|
81
|
+
async function chat() {
|
|
82
|
+
// Configure the API with your server URL (required first step)
|
|
83
|
+
configureApi('https://your-api-server.com');
|
|
84
|
+
|
|
85
|
+
// Stream chat responses
|
|
86
|
+
for await (const response of streamChat('Hello, how can I help you?', false)) {
|
|
87
|
+
// Access the text response
|
|
88
|
+
console.log(response.text);
|
|
89
|
+
|
|
90
|
+
// Check if this is the final response
|
|
91
|
+
if (response.done) {
|
|
92
|
+
console.log('Chat complete!');
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
chat();
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
### With Voice Enabled
|
|
101
|
+
|
|
102
|
+
```javascript
|
|
103
|
+
import { configureApi, streamChat } from 'chatbot-api';
|
|
104
|
+
|
|
105
|
+
async function chatWithVoice() {
|
|
106
|
+
// Configure the API with your server URL
|
|
107
|
+
configureApi('https://your-api-server.com');
|
|
108
|
+
|
|
109
|
+
// Stream chat responses with voice enabled (second parameter true)
|
|
110
|
+
for await (const response of streamChat('Tell me a joke', true)) {
|
|
111
|
+
// Process text responses
|
|
112
|
+
if (response.text) {
|
|
113
|
+
console.log(response.text);
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
// Handle audio content when available
|
|
117
|
+
if (response.type === 'audio' && response.content) {
|
|
118
|
+
// Process audio content (base64 encoded)
|
|
119
|
+
console.log('Received audio content');
|
|
120
|
+
// You can decode and play the audio here
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
if (response.done) {
|
|
124
|
+
console.log('Chat complete!');
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
chatWithVoice();
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
### React Example
|
|
133
|
+
|
|
134
|
+
```jsx
|
|
135
|
+
import React, { useState, useEffect } from 'react';
|
|
136
|
+
import { configureApi, streamChat } from 'chatbot-api';
|
|
137
|
+
|
|
138
|
+
// Configure the API once at the application startup
|
|
139
|
+
configureApi('https://your-api-server.com');
|
|
140
|
+
|
|
141
|
+
function ChatComponent() {
|
|
142
|
+
const [messages, setMessages] = useState([]);
|
|
143
|
+
const [input, setInput] = useState('');
|
|
144
|
+
const [isLoading, setIsLoading] = useState(false);
|
|
145
|
+
|
|
146
|
+
const handleSubmit = async (e) => {
|
|
147
|
+
e.preventDefault();
|
|
148
|
+
if (!input.trim()) return;
|
|
149
|
+
|
|
150
|
+
const userMessage = input;
|
|
151
|
+
setInput('');
|
|
152
|
+
setMessages(prev => [...prev, { text: userMessage, isUser: true }]);
|
|
153
|
+
setIsLoading(true);
|
|
154
|
+
|
|
155
|
+
let fullResponse = '';
|
|
156
|
+
|
|
157
|
+
try {
|
|
158
|
+
for await (const response of streamChat(userMessage, false)) {
|
|
159
|
+
if (response.text) {
|
|
160
|
+
fullResponse += response.text;
|
|
161
|
+
// Update the UI with each chunk of text as it arrives
|
|
162
|
+
setMessages(prev => [
|
|
163
|
+
...prev.slice(0, -1),
|
|
164
|
+
{ text: userMessage, isUser: true },
|
|
165
|
+
{ text: fullResponse, isUser: false, isComplete: response.done }
|
|
166
|
+
]);
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
if (response.done) {
|
|
170
|
+
setIsLoading(false);
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
} catch (error) {
|
|
174
|
+
console.error('Chat error:', error);
|
|
175
|
+
setMessages(prev => [...prev, {
|
|
176
|
+
text: `Error: ${error.message}`,
|
|
177
|
+
isUser: false,
|
|
178
|
+
isError: true
|
|
179
|
+
}]);
|
|
180
|
+
setIsLoading(false);
|
|
181
|
+
}
|
|
182
|
+
};
|
|
183
|
+
|
|
184
|
+
return (
|
|
185
|
+
<div className="chat-container">
|
|
186
|
+
<div className="messages">
|
|
187
|
+
{messages.map((msg, i) => (
|
|
188
|
+
<div key={i} className={`message ${msg.isUser ? 'user' : 'bot'}`}>
|
|
189
|
+
{msg.text}
|
|
190
|
+
</div>
|
|
191
|
+
))}
|
|
192
|
+
{isLoading && <div className="loading">...</div>}
|
|
193
|
+
</div>
|
|
194
|
+
|
|
195
|
+
<form onSubmit={handleSubmit}>
|
|
196
|
+
<input
|
|
197
|
+
value={input}
|
|
198
|
+
onChange={(e) => setInput(e.target.value)}
|
|
199
|
+
placeholder="Type your message..."
|
|
200
|
+
disabled={isLoading}
|
|
201
|
+
/>
|
|
202
|
+
<button type="submit" disabled={isLoading}>Send</button>
|
|
203
|
+
</form>
|
|
204
|
+
</div>
|
|
205
|
+
);
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
export default ChatComponent;
|
|
209
|
+
```
|
|
210
|
+
|
|
211
|
+
## API Reference
|
|
212
|
+
|
|
213
|
+
### configureApi(apiUrl)
|
|
214
|
+
|
|
215
|
+
Configures the API client with your server URL.
|
|
216
|
+
|
|
217
|
+
- **apiUrl** (string): The URL of your Chatbot server
|
|
218
|
+
- **Returns**: void
|
|
219
|
+
|
|
220
|
+
### streamChat(message, voiceEnabled)
|
|
221
|
+
|
|
222
|
+
Streams chat responses from the server.
|
|
223
|
+
|
|
224
|
+
- **message** (string): The message to send to the chatbot
|
|
225
|
+
- **voiceEnabled** (boolean): Whether to enable voice responses
|
|
226
|
+
- **Returns**: AsyncGenerator that yields StreamResponse objects
|
|
227
|
+
|
|
228
|
+
### StreamResponse Interface
|
|
229
|
+
|
|
230
|
+
```typescript
|
|
231
|
+
interface StreamResponse {
|
|
232
|
+
text?: string; // The text response
|
|
233
|
+
content?: string; // Alternative property for text content
|
|
234
|
+
done?: boolean; // Whether this is the final response
|
|
235
|
+
type?: string; // Type of response (e.g., 'text', 'audio')
|
|
236
|
+
}
|
|
237
|
+
```
|
|
238
|
+
|
|
239
|
+
## Development
|
|
240
|
+
|
|
241
|
+
### Building the Library
|
|
242
|
+
|
|
243
|
+
```bash
|
|
244
|
+
npm run build
|
|
245
|
+
```
|
|
246
|
+
|
|
247
|
+
### Running Tests
|
|
248
|
+
|
|
249
|
+
```bash
|
|
250
|
+
# Run tests once
|
|
251
|
+
npm test
|
|
252
|
+
|
|
253
|
+
# Run tests in watch mode
|
|
254
|
+
npm run test:watch
|
|
255
|
+
|
|
256
|
+
# Test a specific query
|
|
257
|
+
npm run test-query "how much is the fee?" "http://your-api-server.com"
|
|
258
|
+
```
|
|
259
|
+
|
|
260
|
+
## License
|
|
261
|
+
|
|
262
|
+
MIT
|
package/api.d.ts
ADDED
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
export interface StreamResponse {
|
|
2
|
+
text?: string;
|
|
3
|
+
content?: string;
|
|
4
|
+
done?: boolean;
|
|
5
|
+
type?: string;
|
|
6
|
+
}
|
|
7
|
+
export declare const configureApi: (apiUrl: string) => void;
|
|
8
|
+
export declare function streamChat(message: string, voiceEnabled: boolean): AsyncGenerator<StreamResponse>;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"__vite-browser-external-BcPniuRQ.cjs","sources":["../__vite-browser-external"],"sourcesContent":["export default {}"],"names":["__viteBrowserExternal"],"mappings":"gFAAA,MAAeA,EAAA,CAAA"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"__vite-browser-external-DYxpcVy9.js","sources":["../__vite-browser-external"],"sourcesContent":["export default {}"],"names":["__viteBrowserExternal"],"mappings":"AAAA,MAAeA,IAAA,CAAA;"}
|
package/dist/api.cjs
ADDED
|
@@ -0,0 +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;
|
|
3
|
+
//# sourceMappingURL=api.cjs.map
|
package/dist/api.cjs.map
ADDED
|
@@ -0,0 +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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"}
|
package/dist/api.mjs
ADDED
|
@@ -0,0 +1,124 @@
|
|
|
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")
|
|
45
|
+
throw new Error("API URL must be a valid string");
|
|
46
|
+
f = e;
|
|
47
|
+
}, m = () => {
|
|
48
|
+
if (!f)
|
|
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:");
|
|
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);
|
|
60
|
+
return {
|
|
61
|
+
...o,
|
|
62
|
+
headers: {
|
|
63
|
+
...o.headers,
|
|
64
|
+
Connection: "keep-alive",
|
|
65
|
+
"X-Request-ID": t
|
|
66
|
+
// Add unique ID to track requests
|
|
67
|
+
}
|
|
68
|
+
};
|
|
69
|
+
};
|
|
70
|
+
async function* x(e, o) {
|
|
71
|
+
var i;
|
|
72
|
+
try {
|
|
73
|
+
const t = m(), r = await fetch(`${t}/chat`, C(t, {
|
|
74
|
+
method: "POST",
|
|
75
|
+
headers: {
|
|
76
|
+
"Content-Type": "application/json"
|
|
77
|
+
},
|
|
78
|
+
body: JSON.stringify({ message: e, voiceEnabled: o })
|
|
79
|
+
}));
|
|
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}`);
|
|
83
|
+
}
|
|
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 = "";
|
|
88
|
+
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(`
|
|
95
|
+
`);
|
|
96
|
+
s = h.pop() || "";
|
|
97
|
+
for (const u of h)
|
|
98
|
+
if (u.trim())
|
|
99
|
+
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);
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
if (s)
|
|
107
|
+
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);
|
|
112
|
+
}
|
|
113
|
+
} catch (t) {
|
|
114
|
+
console.error("Chat API error:", t.message), yield {
|
|
115
|
+
text: `Error connecting to chat server: ${t.message}`,
|
|
116
|
+
done: !0
|
|
117
|
+
};
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
export {
|
|
121
|
+
P as configureApi,
|
|
122
|
+
x as streamChat
|
|
123
|
+
};
|
|
124
|
+
//# sourceMappingURL=api.mjs.map
|
package/dist/api.mjs.map
ADDED
|
@@ -0,0 +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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"}
|
package/package.json
ADDED
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@schmitech/chatbot-api",
|
|
3
|
+
"private": false,
|
|
4
|
+
"version": "0.1.0",
|
|
5
|
+
"description": "API client for the Chatbot server",
|
|
6
|
+
"type": "module",
|
|
7
|
+
"main": "./dist/api.cjs",
|
|
8
|
+
"module": "./dist/api.mjs",
|
|
9
|
+
"types": "./api.d.ts",
|
|
10
|
+
"exports": {
|
|
11
|
+
".": {
|
|
12
|
+
"import": "./dist/api.mjs",
|
|
13
|
+
"require": "./dist/api.cjs"
|
|
14
|
+
}
|
|
15
|
+
},
|
|
16
|
+
"files": [
|
|
17
|
+
"dist",
|
|
18
|
+
"api.d.ts"
|
|
19
|
+
],
|
|
20
|
+
"scripts": {
|
|
21
|
+
"dev": "vite",
|
|
22
|
+
"build": "tsc --emitDeclarationOnly && vite build",
|
|
23
|
+
"lint": "eslint .",
|
|
24
|
+
"preview": "vite preview",
|
|
25
|
+
"test": "vitest",
|
|
26
|
+
"test:watch": "vitest",
|
|
27
|
+
"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
|
+
},
|
|
29
|
+
"dependencies": {
|
|
30
|
+
"cors": "^2.8.5",
|
|
31
|
+
"express": "^4.18.3",
|
|
32
|
+
"node-fetch": "^3.3.2"
|
|
33
|
+
},
|
|
34
|
+
"devDependencies": {
|
|
35
|
+
"@eslint/js": "^9.9.1",
|
|
36
|
+
"@types/cors": "^2.8.17",
|
|
37
|
+
"@types/express": "^4.17.21",
|
|
38
|
+
"@types/node": "^20.11.24",
|
|
39
|
+
"eslint": "^9.9.1",
|
|
40
|
+
"globals": "^15.9.0",
|
|
41
|
+
"msw": "^2.0.11",
|
|
42
|
+
"ts-node": "^10.9.2",
|
|
43
|
+
"typescript": "^5.5.3",
|
|
44
|
+
"typescript-eslint": "^8.3.0",
|
|
45
|
+
"vite": "^5.4.2",
|
|
46
|
+
"vitest": "^0.34.6"
|
|
47
|
+
},
|
|
48
|
+
"peerDependencies": {
|
|
49
|
+
"react": ">=16.8.0",
|
|
50
|
+
"react-dom": ">=16.8.0"
|
|
51
|
+
},
|
|
52
|
+
"peerDependenciesMeta": {
|
|
53
|
+
"react": {
|
|
54
|
+
"optional": true
|
|
55
|
+
},
|
|
56
|
+
"react-dom": {
|
|
57
|
+
"optional": true
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
}
|