@toothfairyai/cli 1.1.1 → 1.1.2
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/bin/toothfairy.js +868 -856
- package/package.json +1 -1
- package/src/api.js +573 -380
package/src/api.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
const axios = require(
|
|
2
|
-
const fs = require(
|
|
3
|
-
const path = require(
|
|
1
|
+
const axios = require('axios');
|
|
2
|
+
const fs = require('fs');
|
|
3
|
+
const path = require('path');
|
|
4
4
|
|
|
5
5
|
class ToothFairyAPI {
|
|
6
6
|
constructor(
|
|
@@ -17,8 +17,8 @@ class ToothFairyAPI {
|
|
|
17
17
|
this.workspaceId = workspaceId;
|
|
18
18
|
this.verbose = verbose;
|
|
19
19
|
this.headers = {
|
|
20
|
-
|
|
21
|
-
|
|
20
|
+
'Content-Type': 'application/json',
|
|
21
|
+
'x-api-key': apiKey,
|
|
22
22
|
};
|
|
23
23
|
}
|
|
24
24
|
|
|
@@ -29,20 +29,20 @@ class ToothFairyAPI {
|
|
|
29
29
|
headers: this.headers,
|
|
30
30
|
};
|
|
31
31
|
|
|
32
|
-
if (method ===
|
|
32
|
+
if (method === 'POST' || method === 'PUT') {
|
|
33
33
|
if (data) {
|
|
34
34
|
data = { workspaceid: this.workspaceId, ...data };
|
|
35
35
|
}
|
|
36
36
|
config.data = data;
|
|
37
|
-
} else if (method ===
|
|
37
|
+
} else if (method === 'GET' && data) {
|
|
38
38
|
// For GET requests with data, add as query parameters
|
|
39
39
|
const params = new URLSearchParams(data);
|
|
40
40
|
config.url += `?${params.toString()}`;
|
|
41
41
|
}
|
|
42
42
|
|
|
43
43
|
if (this.verbose) {
|
|
44
|
-
const chalk = require(
|
|
45
|
-
console.error(chalk.dim(
|
|
44
|
+
const chalk = require('chalk');
|
|
45
|
+
console.error(chalk.dim('\n--- API Request Debug ---'));
|
|
46
46
|
console.error(chalk.dim(`Method: ${method}`));
|
|
47
47
|
console.error(chalk.dim(`URL: ${config.url}`));
|
|
48
48
|
console.error(
|
|
@@ -53,15 +53,15 @@ class ToothFairyAPI {
|
|
|
53
53
|
chalk.dim(`Data: ${JSON.stringify(config.data, null, 2)}`)
|
|
54
54
|
);
|
|
55
55
|
}
|
|
56
|
-
console.error(chalk.dim(
|
|
56
|
+
console.error(chalk.dim('----------------------\n'));
|
|
57
57
|
}
|
|
58
58
|
|
|
59
59
|
try {
|
|
60
60
|
const response = await axios(config);
|
|
61
61
|
|
|
62
62
|
if (this.verbose) {
|
|
63
|
-
const chalk = require(
|
|
64
|
-
console.error(chalk.dim(
|
|
63
|
+
const chalk = require('chalk');
|
|
64
|
+
console.error(chalk.dim('\n--- API Response Debug ---'));
|
|
65
65
|
console.error(
|
|
66
66
|
chalk.dim(`Status: ${response.status} ${response.statusText}`)
|
|
67
67
|
);
|
|
@@ -73,14 +73,14 @@ class ToothFairyAPI {
|
|
|
73
73
|
console.error(
|
|
74
74
|
chalk.dim(`Response Data: ${JSON.stringify(response.data, null, 2)}`)
|
|
75
75
|
);
|
|
76
|
-
console.error(chalk.dim(
|
|
76
|
+
console.error(chalk.dim('------------------------\n'));
|
|
77
77
|
}
|
|
78
78
|
|
|
79
79
|
return response.data;
|
|
80
80
|
} catch (error) {
|
|
81
81
|
if (this.verbose) {
|
|
82
|
-
const chalk = require(
|
|
83
|
-
console.error(chalk.red(
|
|
82
|
+
const chalk = require('chalk');
|
|
83
|
+
console.error(chalk.red('\n--- API Error Debug ---'));
|
|
84
84
|
console.error(chalk.red(`Error: ${error.message}`));
|
|
85
85
|
if (error.response) {
|
|
86
86
|
console.error(
|
|
@@ -110,13 +110,13 @@ class ToothFairyAPI {
|
|
|
110
110
|
)
|
|
111
111
|
);
|
|
112
112
|
}
|
|
113
|
-
console.error(chalk.red(
|
|
113
|
+
console.error(chalk.red('---------------------\n'));
|
|
114
114
|
}
|
|
115
115
|
|
|
116
116
|
if (error.response) {
|
|
117
117
|
throw new Error(
|
|
118
118
|
`HTTP ${error.response.status}: ${
|
|
119
|
-
error.response.data.message ||
|
|
119
|
+
error.response.data.message || 'API request failed'
|
|
120
120
|
}`
|
|
121
121
|
);
|
|
122
122
|
}
|
|
@@ -125,39 +125,39 @@ class ToothFairyAPI {
|
|
|
125
125
|
}
|
|
126
126
|
|
|
127
127
|
async createChat(chatData) {
|
|
128
|
-
return this._makeRequest(
|
|
128
|
+
return this._makeRequest('POST', 'chat/create', chatData);
|
|
129
129
|
}
|
|
130
130
|
|
|
131
131
|
async updateChat(chatData) {
|
|
132
|
-
return this._makeRequest(
|
|
132
|
+
return this._makeRequest('POST', 'chat/update', chatData);
|
|
133
133
|
}
|
|
134
134
|
|
|
135
135
|
async getChat(chatId) {
|
|
136
|
-
return this._makeRequest(
|
|
136
|
+
return this._makeRequest('GET', `chat/get/${chatId}`);
|
|
137
137
|
}
|
|
138
138
|
|
|
139
139
|
async createMessage(messageData) {
|
|
140
|
-
return this._makeRequest(
|
|
140
|
+
return this._makeRequest('POST', 'chat_message/create', messageData);
|
|
141
141
|
}
|
|
142
142
|
|
|
143
143
|
async getMessage(messageId) {
|
|
144
|
-
return this._makeRequest(
|
|
144
|
+
return this._makeRequest('GET', `chat_message/get/${messageId}`);
|
|
145
145
|
}
|
|
146
146
|
|
|
147
147
|
async getAllChats() {
|
|
148
|
-
return this._makeRequest(
|
|
148
|
+
return this._makeRequest('GET', 'chat/list', {
|
|
149
149
|
workspaceid: this.workspaceId,
|
|
150
150
|
});
|
|
151
151
|
}
|
|
152
152
|
async getAllMessagesByChat(chatid) {
|
|
153
|
-
return this._makeRequest(
|
|
153
|
+
return this._makeRequest('GET', 'chat_message/by_chat', {
|
|
154
154
|
chatid: chatid,
|
|
155
155
|
});
|
|
156
156
|
}
|
|
157
157
|
|
|
158
158
|
async getAgentResponse(agentData) {
|
|
159
159
|
const config = {
|
|
160
|
-
method:
|
|
160
|
+
method: 'POST',
|
|
161
161
|
url: `${this.aiUrl}/chatter`,
|
|
162
162
|
headers: this.headers,
|
|
163
163
|
data: { workspaceid: this.workspaceId, ...agentData },
|
|
@@ -170,7 +170,7 @@ class ToothFairyAPI {
|
|
|
170
170
|
if (error.response) {
|
|
171
171
|
throw new Error(
|
|
172
172
|
`HTTP ${error.response.status}: ${
|
|
173
|
-
error.response.data.message ||
|
|
173
|
+
error.response.data.message || 'Agent request failed'
|
|
174
174
|
}`
|
|
175
175
|
);
|
|
176
176
|
}
|
|
@@ -185,7 +185,8 @@ class ToothFairyAPI {
|
|
|
185
185
|
customerId = null,
|
|
186
186
|
providerId = null,
|
|
187
187
|
customerInfo = {},
|
|
188
|
-
attachments = {}
|
|
188
|
+
attachments = {},
|
|
189
|
+
chatId = null
|
|
189
190
|
) {
|
|
190
191
|
try {
|
|
191
192
|
// Use defaults for optional parameters
|
|
@@ -193,66 +194,92 @@ class ToothFairyAPI {
|
|
|
193
194
|
customerId ||
|
|
194
195
|
`cli-user-${
|
|
195
196
|
Math.abs(
|
|
196
|
-
message.split(
|
|
197
|
+
message.split('').reduce((a, b) => {
|
|
197
198
|
a = (a << 5) - a + b.charCodeAt(0);
|
|
198
199
|
return a & a;
|
|
199
200
|
}, 0)
|
|
200
201
|
) % 10000
|
|
201
202
|
}`;
|
|
202
|
-
phoneNumber = phoneNumber ||
|
|
203
|
-
providerId = providerId ||
|
|
203
|
+
phoneNumber = phoneNumber || '+1234567890';
|
|
204
|
+
providerId = providerId || 'default-sms-provider';
|
|
204
205
|
|
|
205
206
|
// Process file attachments if provided
|
|
206
207
|
const processedAttachments = await this._processAttachments(attachments);
|
|
207
208
|
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
}
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
209
|
+
if (chatId) {
|
|
210
|
+
// Use existing chat - create message in existing chat
|
|
211
|
+
const messageData = {
|
|
212
|
+
chatID: chatId,
|
|
213
|
+
text: message,
|
|
214
|
+
role: 'user',
|
|
215
|
+
userID: 'CLI',
|
|
216
|
+
...processedAttachments,
|
|
217
|
+
};
|
|
218
|
+
const createdMessage = await this.createMessage(messageData);
|
|
219
|
+
// console.log(`Message created: ${createdMessage.id}`);;
|
|
220
|
+
|
|
221
|
+
const agentData = {
|
|
222
|
+
chatid: chatId,
|
|
223
|
+
messages: [
|
|
224
|
+
{
|
|
225
|
+
text: createdMessage.text,
|
|
226
|
+
role: createdMessage.role,
|
|
227
|
+
userID: createdMessage.userID || 'System User',
|
|
228
|
+
},
|
|
229
|
+
],
|
|
230
|
+
agentid: agentId,
|
|
231
|
+
};
|
|
232
|
+
|
|
233
|
+
const agentResponse = await this.getAgentResponse(agentData);
|
|
234
|
+
|
|
235
|
+
return {
|
|
236
|
+
chatId: chatId,
|
|
237
|
+
messageId: createdMessage.id,
|
|
238
|
+
agentResponse: agentResponse,
|
|
239
|
+
};
|
|
240
|
+
} else {
|
|
241
|
+
// No chatId provided - let API create chat automatically
|
|
242
|
+
// Prepare message data for automatic chat creation
|
|
243
|
+
const messageData = {
|
|
244
|
+
text: message,
|
|
245
|
+
role: 'user',
|
|
246
|
+
userID: customerId,
|
|
247
|
+
};
|
|
236
248
|
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
249
|
+
// Only include attachment fields if they have content
|
|
250
|
+
if (processedAttachments.images && processedAttachments.images.length > 0) {
|
|
251
|
+
messageData.images = processedAttachments.images;
|
|
252
|
+
}
|
|
253
|
+
if (processedAttachments.audios && processedAttachments.audios.length > 0) {
|
|
254
|
+
messageData.audios = processedAttachments.audios;
|
|
255
|
+
}
|
|
256
|
+
if (processedAttachments.videos && processedAttachments.videos.length > 0) {
|
|
257
|
+
messageData.videos = processedAttachments.videos;
|
|
258
|
+
}
|
|
259
|
+
if (processedAttachments.files && processedAttachments.files.length > 0) {
|
|
260
|
+
messageData.files = processedAttachments.files;
|
|
261
|
+
}
|
|
250
262
|
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
263
|
+
// Send agent request without chatid - API will create chat and message automatically
|
|
264
|
+
const agentData = {
|
|
265
|
+
// No chatid - let API create the chat
|
|
266
|
+
messages: [messageData],
|
|
267
|
+
agentid: agentId,
|
|
268
|
+
// Include chat creation data since we're not pre-creating
|
|
269
|
+
phoneNumber: phoneNumber,
|
|
270
|
+
customerId: customerId,
|
|
271
|
+
providerId: providerId,
|
|
272
|
+
customerInfo: customerInfo,
|
|
273
|
+
};
|
|
274
|
+
|
|
275
|
+
const agentResponse = await this.getAgentResponse(agentData);
|
|
276
|
+
|
|
277
|
+
return {
|
|
278
|
+
chatId: agentResponse.chatId || 'auto-generated',
|
|
279
|
+
messageId: agentResponse.messageId || 'auto-generated',
|
|
280
|
+
agentResponse: agentResponse,
|
|
281
|
+
};
|
|
282
|
+
}
|
|
256
283
|
} catch (error) {
|
|
257
284
|
console.error(`Error in sendMessageToAgent: ${error.message}`);
|
|
258
285
|
throw error;
|
|
@@ -261,7 +288,7 @@ class ToothFairyAPI {
|
|
|
261
288
|
|
|
262
289
|
async searchDocuments(text, topK = 10, metadata = null) {
|
|
263
290
|
if (topK < 1 || topK > 50) {
|
|
264
|
-
throw new Error(
|
|
291
|
+
throw new Error('topK must be between 1 and 50');
|
|
265
292
|
}
|
|
266
293
|
|
|
267
294
|
const searchData = {
|
|
@@ -274,7 +301,7 @@ class ToothFairyAPI {
|
|
|
274
301
|
}
|
|
275
302
|
|
|
276
303
|
const config = {
|
|
277
|
-
method:
|
|
304
|
+
method: 'POST',
|
|
278
305
|
url: `${this.aiUrl}/searcher`,
|
|
279
306
|
headers: this.headers,
|
|
280
307
|
data: { workspaceid: this.workspaceId, ...searchData },
|
|
@@ -287,7 +314,7 @@ class ToothFairyAPI {
|
|
|
287
314
|
if (error.response) {
|
|
288
315
|
throw new Error(
|
|
289
316
|
`HTTP ${error.response.status}: ${
|
|
290
|
-
error.response.data.message ||
|
|
317
|
+
error.response.data.message || 'Search request failed'
|
|
291
318
|
}`
|
|
292
319
|
);
|
|
293
320
|
}
|
|
@@ -329,7 +356,7 @@ class ToothFairyAPI {
|
|
|
329
356
|
* - 'sse_event': All raw SSE events (only when showProgress=true)
|
|
330
357
|
*
|
|
331
358
|
* The onEvent callback receives: (eventType, eventData) => {}
|
|
332
|
-
*
|
|
359
|
+
*
|
|
333
360
|
* When showProgress is true, all SSE events will be emitted to the onEvent callback
|
|
334
361
|
* with the 'sse_event' type, providing access to all raw events from the streaming
|
|
335
362
|
* endpoint, similar to the CLI's --show-progress flag behavior.
|
|
@@ -343,7 +370,8 @@ class ToothFairyAPI {
|
|
|
343
370
|
customerInfo = {},
|
|
344
371
|
onEvent,
|
|
345
372
|
attachments = {},
|
|
346
|
-
showProgress = false
|
|
373
|
+
showProgress = false,
|
|
374
|
+
chatId = null
|
|
347
375
|
) {
|
|
348
376
|
try {
|
|
349
377
|
// Use defaults for optional parameters
|
|
@@ -351,207 +379,372 @@ class ToothFairyAPI {
|
|
|
351
379
|
customerId ||
|
|
352
380
|
`cli-user-${
|
|
353
381
|
Math.abs(
|
|
354
|
-
message.split(
|
|
382
|
+
message.split('').reduce((a, b) => {
|
|
355
383
|
a = (a << 5) - a + b.charCodeAt(0);
|
|
356
384
|
return a & a;
|
|
357
385
|
}, 0)
|
|
358
386
|
) % 10000
|
|
359
387
|
}`;
|
|
360
|
-
phoneNumber = phoneNumber ||
|
|
361
|
-
providerId = providerId ||
|
|
388
|
+
phoneNumber = phoneNumber || '+1234567890';
|
|
389
|
+
providerId = providerId || 'default-sms-provider';
|
|
362
390
|
|
|
363
391
|
// Process file attachments if provided
|
|
364
392
|
const processedAttachments = await this._processAttachments(attachments);
|
|
365
393
|
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
channelSettings: {
|
|
372
|
-
sms: {
|
|
373
|
-
isEnabled: true,
|
|
374
|
-
recipient: phoneNumber,
|
|
375
|
-
providerID: providerId,
|
|
376
|
-
},
|
|
377
|
-
},
|
|
378
|
-
customerId: customerId,
|
|
379
|
-
customerInfo: customerInfo,
|
|
380
|
-
isAIReplying: true,
|
|
381
|
-
};
|
|
382
|
-
|
|
383
|
-
const createdChat = await this.createChat(chatData);
|
|
384
|
-
if (this.verbose) {
|
|
385
|
-
console.debug(`Chat created: ${createdChat.id}`);
|
|
386
|
-
}
|
|
394
|
+
if (chatId) {
|
|
395
|
+
// Use existing chat - create message in existing chat
|
|
396
|
+
if (this.verbose) {
|
|
397
|
+
console.debug(`Using existing chat: ${chatId}`);
|
|
398
|
+
}
|
|
387
399
|
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
}
|
|
400
|
+
const messageData = {
|
|
401
|
+
chatID: chatId,
|
|
402
|
+
text: message,
|
|
403
|
+
role: 'user',
|
|
404
|
+
userID: 'CLI',
|
|
405
|
+
...processedAttachments,
|
|
406
|
+
};
|
|
407
|
+
const createdMessage = await this.createMessage(messageData);
|
|
408
|
+
if (this.verbose) {
|
|
409
|
+
console.debug(`Message created: ${createdMessage.id}`);
|
|
410
|
+
}
|
|
400
411
|
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
412
|
+
// Prepare agent data for streaming with existing chat
|
|
413
|
+
const agentData = {
|
|
414
|
+
workspaceid: this.workspaceId,
|
|
415
|
+
chatid: chatId,
|
|
416
|
+
messages: [
|
|
417
|
+
{
|
|
418
|
+
text: createdMessage.text,
|
|
419
|
+
role: createdMessage.role,
|
|
420
|
+
userID: createdMessage.userID || 'System User',
|
|
421
|
+
},
|
|
422
|
+
],
|
|
423
|
+
agentid: agentId,
|
|
424
|
+
};
|
|
413
425
|
|
|
414
|
-
|
|
415
|
-
|
|
426
|
+
// Stream the agent response using the dedicated streaming URL
|
|
427
|
+
const streamUrl = `${this.aiStreamUrl}/agent`;
|
|
428
|
+
|
|
429
|
+
return new Promise((resolve, reject) => {
|
|
430
|
+
// For POST requests with EventSource, we need to use a different approach
|
|
431
|
+
// EventSource doesn't support POST requests directly, so we'll use axios with streaming
|
|
432
|
+
const config = {
|
|
433
|
+
method: 'POST',
|
|
434
|
+
url: streamUrl,
|
|
435
|
+
headers: {
|
|
436
|
+
...this.headers,
|
|
437
|
+
Accept: 'text/event-stream',
|
|
438
|
+
'Cache-Control': 'no-cache',
|
|
439
|
+
},
|
|
440
|
+
data: agentData,
|
|
441
|
+
responseType: 'stream',
|
|
442
|
+
timeout: 300000, // 5 minute timeout
|
|
443
|
+
};
|
|
444
|
+
|
|
445
|
+
axios(config)
|
|
446
|
+
.then((response) => {
|
|
447
|
+
let buffer = '';
|
|
448
|
+
|
|
449
|
+
response.data.on('data', (chunk) => {
|
|
450
|
+
buffer += chunk.toString();
|
|
451
|
+
|
|
452
|
+
// Process complete lines
|
|
453
|
+
const lines = buffer.split('\n');
|
|
454
|
+
buffer = lines.pop() || ''; // Keep the incomplete line in buffer
|
|
455
|
+
|
|
456
|
+
for (const line of lines) {
|
|
457
|
+
if (line.trim() === '') continue;
|
|
458
|
+
|
|
459
|
+
if (line.startsWith('data: ')) {
|
|
460
|
+
const dataStr = line.slice(6); // Remove 'data: ' prefix
|
|
461
|
+
|
|
462
|
+
try {
|
|
463
|
+
const eventData = JSON.parse(dataStr);
|
|
464
|
+
|
|
465
|
+
// If showProgress is enabled, emit all events
|
|
466
|
+
if (showProgress) {
|
|
467
|
+
// Emit all events with their raw structure
|
|
468
|
+
onEvent('sse_event', eventData);
|
|
469
|
+
}
|
|
416
470
|
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
471
|
+
// Standard event processing (always executed for backward compatibility)
|
|
472
|
+
if (eventData.status) {
|
|
473
|
+
if (eventData.status === 'connected') {
|
|
474
|
+
onEvent('status', eventData);
|
|
475
|
+
} else if (eventData.status === 'complete') {
|
|
476
|
+
onEvent('status', eventData);
|
|
477
|
+
} else if (eventData.status === 'inProgress') {
|
|
478
|
+
// Parse metadata to understand what's happening
|
|
479
|
+
let metadata = {};
|
|
480
|
+
if (eventData.metadata) {
|
|
481
|
+
try {
|
|
482
|
+
metadata = JSON.parse(eventData.metadata);
|
|
483
|
+
} catch (e) {
|
|
484
|
+
metadata = { raw_metadata: eventData.metadata };
|
|
485
|
+
}
|
|
486
|
+
}
|
|
432
487
|
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
488
|
+
// Determine progress type
|
|
489
|
+
if (metadata.agent_processing_status) {
|
|
490
|
+
const processingStatus =
|
|
491
|
+
metadata.agent_processing_status;
|
|
492
|
+
onEvent('progress', {
|
|
493
|
+
...eventData,
|
|
494
|
+
processing_status: processingStatus,
|
|
495
|
+
metadata_parsed: metadata,
|
|
496
|
+
});
|
|
497
|
+
} else {
|
|
498
|
+
onEvent('progress', {
|
|
499
|
+
...eventData,
|
|
500
|
+
metadata_parsed: metadata,
|
|
501
|
+
});
|
|
502
|
+
}
|
|
503
|
+
} else if (eventData.status === 'fulfilled') {
|
|
504
|
+
// Final response with complete data
|
|
505
|
+
onEvent('complete', eventData);
|
|
506
|
+
}
|
|
507
|
+
} else if (eventData.text && eventData.type === 'message') {
|
|
508
|
+
// This is streaming text data
|
|
509
|
+
onEvent('data', eventData);
|
|
510
|
+
} else if (
|
|
511
|
+
eventData.type === 'message' &&
|
|
512
|
+
eventData.images !== undefined
|
|
513
|
+
) {
|
|
514
|
+
// Additional message metadata (images, files, etc.)
|
|
515
|
+
onEvent('metadata', eventData);
|
|
516
|
+
} else if (
|
|
517
|
+
eventData.type === 'message' &&
|
|
518
|
+
eventData.callbackMetadata
|
|
519
|
+
) {
|
|
520
|
+
// Callback metadata with function details and execution plan
|
|
521
|
+
onEvent('callback', eventData);
|
|
522
|
+
} else if (eventData.type === 'chat_created' || eventData.event === 'chat_created') {
|
|
523
|
+
// Chat creation event
|
|
524
|
+
onEvent('chat_created', eventData);
|
|
525
|
+
} else {
|
|
526
|
+
// Generic event data
|
|
527
|
+
onEvent('unknown', eventData);
|
|
528
|
+
}
|
|
529
|
+
} catch (e) {
|
|
530
|
+
console.error(
|
|
531
|
+
`Failed to parse SSE data: ${dataStr}, error: ${e.message}`
|
|
532
|
+
);
|
|
533
|
+
onEvent('error', {
|
|
534
|
+
error: 'json_decode_error',
|
|
535
|
+
raw_data: dataStr,
|
|
536
|
+
message: e.message,
|
|
537
|
+
});
|
|
538
|
+
}
|
|
539
|
+
}
|
|
540
|
+
}
|
|
541
|
+
});
|
|
436
542
|
|
|
437
|
-
|
|
438
|
-
|
|
543
|
+
response.data.on('end', () => {
|
|
544
|
+
resolve();
|
|
545
|
+
});
|
|
439
546
|
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
547
|
+
response.data.on('error', (error) => {
|
|
548
|
+
onEvent('error', {
|
|
549
|
+
error: 'stream_error',
|
|
550
|
+
message: error.message,
|
|
551
|
+
});
|
|
552
|
+
reject(error);
|
|
553
|
+
});
|
|
554
|
+
})
|
|
555
|
+
.catch((error) => {
|
|
556
|
+
onEvent('error', {
|
|
557
|
+
error: 'request_error',
|
|
558
|
+
message: error.message,
|
|
559
|
+
});
|
|
560
|
+
reject(error);
|
|
561
|
+
});
|
|
562
|
+
});
|
|
563
|
+
} else {
|
|
564
|
+
// No chatId provided - let streaming API create chat automatically
|
|
565
|
+
if (this.verbose) {
|
|
566
|
+
console.debug('No chatId provided - letting API create chat automatically');
|
|
567
|
+
}
|
|
443
568
|
|
|
444
|
-
|
|
445
|
-
|
|
569
|
+
// Prepare message data for automatic chat creation
|
|
570
|
+
const messageData = {
|
|
571
|
+
text: message,
|
|
572
|
+
role: 'user',
|
|
573
|
+
userID: customerId,
|
|
574
|
+
};
|
|
446
575
|
|
|
447
|
-
|
|
448
|
-
|
|
576
|
+
// Only include attachment fields if they have content
|
|
577
|
+
if (processedAttachments.images && processedAttachments.images.length > 0) {
|
|
578
|
+
messageData.images = processedAttachments.images;
|
|
579
|
+
}
|
|
580
|
+
if (processedAttachments.audios && processedAttachments.audios.length > 0) {
|
|
581
|
+
messageData.audios = processedAttachments.audios;
|
|
582
|
+
}
|
|
583
|
+
if (processedAttachments.videos && processedAttachments.videos.length > 0) {
|
|
584
|
+
messageData.videos = processedAttachments.videos;
|
|
585
|
+
}
|
|
586
|
+
if (processedAttachments.files && processedAttachments.files.length > 0) {
|
|
587
|
+
messageData.files = processedAttachments.files;
|
|
588
|
+
}
|
|
449
589
|
|
|
450
|
-
|
|
451
|
-
|
|
590
|
+
// Send agent request without chatid - API will create chat and message automatically
|
|
591
|
+
const agentData = {
|
|
592
|
+
workspaceid: this.workspaceId,
|
|
593
|
+
// No chatid - let API create the chat
|
|
594
|
+
messages: [messageData],
|
|
595
|
+
agentid: agentId,
|
|
596
|
+
// Include chat creation data since we're not pre-creating
|
|
597
|
+
phoneNumber: phoneNumber,
|
|
598
|
+
customerId: customerId,
|
|
599
|
+
providerId: providerId,
|
|
600
|
+
customerInfo: customerInfo,
|
|
601
|
+
};
|
|
452
602
|
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
603
|
+
// Stream the agent response using the dedicated streaming URL
|
|
604
|
+
const streamUrl = `${this.aiStreamUrl}/agent`;
|
|
605
|
+
|
|
606
|
+
return new Promise((resolve, reject) => {
|
|
607
|
+
// For POST requests with EventSource, we need to use a different approach
|
|
608
|
+
// EventSource doesn't support POST requests directly, so we'll use axios with streaming
|
|
609
|
+
const config = {
|
|
610
|
+
method: 'POST',
|
|
611
|
+
url: streamUrl,
|
|
612
|
+
headers: {
|
|
613
|
+
...this.headers,
|
|
614
|
+
Accept: 'text/event-stream',
|
|
615
|
+
'Cache-Control': 'no-cache',
|
|
616
|
+
},
|
|
617
|
+
data: agentData,
|
|
618
|
+
responseType: 'stream',
|
|
619
|
+
timeout: 300000, // 5 minute timeout
|
|
620
|
+
};
|
|
621
|
+
|
|
622
|
+
axios(config)
|
|
623
|
+
.then((response) => {
|
|
624
|
+
let buffer = '';
|
|
625
|
+
|
|
626
|
+
response.data.on('data', (chunk) => {
|
|
627
|
+
buffer += chunk.toString();
|
|
628
|
+
|
|
629
|
+
// Process complete lines
|
|
630
|
+
const lines = buffer.split('\n');
|
|
631
|
+
buffer = lines.pop() || ''; // Keep the incomplete line in buffer
|
|
632
|
+
|
|
633
|
+
for (const line of lines) {
|
|
634
|
+
if (line.trim() === '') continue;
|
|
635
|
+
|
|
636
|
+
if (line.startsWith('data: ')) {
|
|
637
|
+
const dataStr = line.slice(6); // Remove 'data: ' prefix
|
|
638
|
+
|
|
639
|
+
try {
|
|
640
|
+
const eventData = JSON.parse(dataStr);
|
|
641
|
+
|
|
642
|
+
// If showProgress is enabled, emit all events
|
|
643
|
+
if (showProgress) {
|
|
644
|
+
// Emit all events with their raw structure
|
|
645
|
+
onEvent('sse_event', eventData);
|
|
646
|
+
}
|
|
458
647
|
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
648
|
+
// Standard event processing (always executed for backward compatibility)
|
|
649
|
+
if (eventData.status) {
|
|
650
|
+
if (eventData.status === 'connected') {
|
|
651
|
+
onEvent('status', eventData);
|
|
652
|
+
} else if (eventData.status === 'complete') {
|
|
653
|
+
onEvent('status', eventData);
|
|
654
|
+
} else if (eventData.status === 'inProgress') {
|
|
655
|
+
// Parse metadata to understand what's happening
|
|
656
|
+
let metadata = {};
|
|
657
|
+
if (eventData.metadata) {
|
|
658
|
+
try {
|
|
659
|
+
metadata = JSON.parse(eventData.metadata);
|
|
660
|
+
} catch (e) {
|
|
661
|
+
metadata = { raw_metadata: eventData.metadata };
|
|
662
|
+
}
|
|
473
663
|
}
|
|
474
|
-
}
|
|
475
664
|
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
665
|
+
// Determine progress type
|
|
666
|
+
if (metadata.agent_processing_status) {
|
|
667
|
+
const processingStatus =
|
|
668
|
+
metadata.agent_processing_status;
|
|
669
|
+
onEvent('progress', {
|
|
670
|
+
...eventData,
|
|
671
|
+
processing_status: processingStatus,
|
|
672
|
+
metadata_parsed: metadata,
|
|
673
|
+
});
|
|
674
|
+
} else {
|
|
675
|
+
onEvent('progress', {
|
|
676
|
+
...eventData,
|
|
677
|
+
metadata_parsed: metadata,
|
|
678
|
+
});
|
|
679
|
+
}
|
|
680
|
+
} else if (eventData.status === 'fulfilled') {
|
|
681
|
+
// Final response with complete data
|
|
682
|
+
onEvent('complete', eventData);
|
|
490
683
|
}
|
|
491
|
-
} else if (eventData.
|
|
492
|
-
//
|
|
493
|
-
onEvent('
|
|
684
|
+
} else if (eventData.text && eventData.type === 'message') {
|
|
685
|
+
// This is streaming text data
|
|
686
|
+
onEvent('data', eventData);
|
|
687
|
+
} else if (
|
|
688
|
+
eventData.type === 'message' &&
|
|
689
|
+
eventData.images !== undefined
|
|
690
|
+
) {
|
|
691
|
+
// Additional message metadata (images, files, etc.)
|
|
692
|
+
onEvent('metadata', eventData);
|
|
693
|
+
} else if (
|
|
694
|
+
eventData.type === 'message' &&
|
|
695
|
+
eventData.callbackMetadata
|
|
696
|
+
) {
|
|
697
|
+
// Callback metadata with function details and execution plan
|
|
698
|
+
onEvent('callback', eventData);
|
|
699
|
+
} else if (
|
|
700
|
+
eventData.type === 'chat_created' ||
|
|
701
|
+
eventData.event === 'chat_created'
|
|
702
|
+
) {
|
|
703
|
+
// Chat creation event
|
|
704
|
+
onEvent('chat_created', eventData);
|
|
705
|
+
} else {
|
|
706
|
+
// Generic event data
|
|
707
|
+
onEvent('unknown', eventData);
|
|
494
708
|
}
|
|
495
|
-
}
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
} else if (
|
|
505
|
-
eventData.type === 'message' &&
|
|
506
|
-
eventData.callbackMetadata
|
|
507
|
-
) {
|
|
508
|
-
// Callback metadata with function details and execution plan
|
|
509
|
-
onEvent('callback', eventData);
|
|
510
|
-
} else if (eventData.type === 'chat_created' || eventData.event === 'chat_created') {
|
|
511
|
-
// Chat creation event
|
|
512
|
-
onEvent('chat_created', eventData);
|
|
513
|
-
} else {
|
|
514
|
-
// Generic event data
|
|
515
|
-
onEvent('unknown', eventData);
|
|
709
|
+
} catch (e) {
|
|
710
|
+
console.error(
|
|
711
|
+
`Failed to parse SSE data: ${dataStr}, error: ${e.message}`
|
|
712
|
+
);
|
|
713
|
+
onEvent('error', {
|
|
714
|
+
error: 'json_decode_error',
|
|
715
|
+
raw_data: dataStr,
|
|
716
|
+
message: e.message,
|
|
717
|
+
});
|
|
516
718
|
}
|
|
517
|
-
} catch (e) {
|
|
518
|
-
console.error(
|
|
519
|
-
`Failed to parse SSE data: ${dataStr}, error: ${e.message}`
|
|
520
|
-
);
|
|
521
|
-
onEvent("error", {
|
|
522
|
-
error: "json_decode_error",
|
|
523
|
-
raw_data: dataStr,
|
|
524
|
-
message: e.message,
|
|
525
|
-
});
|
|
526
719
|
}
|
|
527
720
|
}
|
|
528
|
-
}
|
|
529
|
-
});
|
|
721
|
+
});
|
|
530
722
|
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
723
|
+
response.data.on('end', () => {
|
|
724
|
+
resolve();
|
|
725
|
+
});
|
|
534
726
|
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
727
|
+
response.data.on('error', (error) => {
|
|
728
|
+
onEvent('error', {
|
|
729
|
+
error: 'stream_error',
|
|
730
|
+
message: error.message,
|
|
731
|
+
});
|
|
732
|
+
reject(error);
|
|
733
|
+
});
|
|
734
|
+
})
|
|
735
|
+
.catch((error) => {
|
|
736
|
+
onEvent('error', {
|
|
737
|
+
error: 'request_error',
|
|
538
738
|
message: error.message,
|
|
539
739
|
});
|
|
540
740
|
reject(error);
|
|
541
741
|
});
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
onEvent("error", {
|
|
545
|
-
error: "request_error",
|
|
546
|
-
message: error.message,
|
|
547
|
-
});
|
|
548
|
-
reject(error);
|
|
549
|
-
});
|
|
550
|
-
});
|
|
742
|
+
});
|
|
743
|
+
}
|
|
551
744
|
} catch (error) {
|
|
552
745
|
console.error(`Error in sendMessageToAgentStream: ${error.message}`);
|
|
553
|
-
onEvent(
|
|
554
|
-
error:
|
|
746
|
+
onEvent('error', {
|
|
747
|
+
error: 'setup_error',
|
|
555
748
|
message: error.message,
|
|
556
749
|
});
|
|
557
750
|
throw error;
|
|
@@ -572,24 +765,24 @@ class ToothFairyAPI {
|
|
|
572
765
|
});
|
|
573
766
|
|
|
574
767
|
if (importType) {
|
|
575
|
-
params.append(
|
|
768
|
+
params.append('importType', importType);
|
|
576
769
|
}
|
|
577
770
|
|
|
578
771
|
if (contentType) {
|
|
579
|
-
params.append(
|
|
772
|
+
params.append('contentType', contentType);
|
|
580
773
|
}
|
|
581
774
|
|
|
582
775
|
const config = {
|
|
583
|
-
method:
|
|
776
|
+
method: 'GET',
|
|
584
777
|
url: `${this.baseUrl}/documents/requestPreSignedURL?${params.toString()}`,
|
|
585
778
|
headers: this.headers,
|
|
586
779
|
};
|
|
587
780
|
|
|
588
781
|
if (this.verbose) {
|
|
589
|
-
const chalk = require(
|
|
590
|
-
console.error(chalk.dim(
|
|
782
|
+
const chalk = require('chalk');
|
|
783
|
+
console.error(chalk.dim('\n--- Upload URL Request Debug ---'));
|
|
591
784
|
console.error(chalk.dim(`URL: ${config.url}`));
|
|
592
|
-
console.error(chalk.dim(
|
|
785
|
+
console.error(chalk.dim('----------------------------\n'));
|
|
593
786
|
}
|
|
594
787
|
|
|
595
788
|
try {
|
|
@@ -599,7 +792,7 @@ class ToothFairyAPI {
|
|
|
599
792
|
if (error.response) {
|
|
600
793
|
throw new Error(
|
|
601
794
|
`HTTP ${error.response.status}: ${
|
|
602
|
-
error.response.data.message ||
|
|
795
|
+
error.response.data.message || 'Upload URL request failed'
|
|
603
796
|
}`
|
|
604
797
|
);
|
|
605
798
|
}
|
|
@@ -659,14 +852,14 @@ class ToothFairyAPI {
|
|
|
659
852
|
finalContentType
|
|
660
853
|
);
|
|
661
854
|
if (this.verbose) {
|
|
662
|
-
const chalk = require(
|
|
663
|
-
console.error(chalk.dim(
|
|
855
|
+
const chalk = require('chalk');
|
|
856
|
+
console.error(chalk.dim('\n--- File Upload URL response ---'));
|
|
664
857
|
console.error(chalk.dim(`Raw response: ${JSON.stringify(uploadData)}`));
|
|
665
858
|
}
|
|
666
859
|
|
|
667
860
|
// Parse the nested response structure
|
|
668
861
|
let parsedUploadData;
|
|
669
|
-
if (uploadData.body && typeof uploadData.body ===
|
|
862
|
+
if (uploadData.body && typeof uploadData.body === 'string') {
|
|
670
863
|
try {
|
|
671
864
|
parsedUploadData = JSON.parse(uploadData.body);
|
|
672
865
|
} catch (error) {
|
|
@@ -679,18 +872,18 @@ class ToothFairyAPI {
|
|
|
679
872
|
}
|
|
680
873
|
|
|
681
874
|
if (!parsedUploadData.uploadURL) {
|
|
682
|
-
throw new Error(
|
|
875
|
+
throw new Error('Failed to get upload URL from server');
|
|
683
876
|
}
|
|
684
877
|
|
|
685
878
|
// Upload file to S3
|
|
686
879
|
const fileData = fs.readFileSync(filePath);
|
|
687
880
|
|
|
688
881
|
const uploadConfig = {
|
|
689
|
-
method:
|
|
882
|
+
method: 'PUT',
|
|
690
883
|
url: parsedUploadData.uploadURL,
|
|
691
884
|
data: fileData,
|
|
692
885
|
headers: {
|
|
693
|
-
|
|
886
|
+
'Content-Type': finalContentType,
|
|
694
887
|
},
|
|
695
888
|
maxContentLength: Infinity,
|
|
696
889
|
maxBodyLength: Infinity,
|
|
@@ -709,8 +902,8 @@ class ToothFairyAPI {
|
|
|
709
902
|
};
|
|
710
903
|
|
|
711
904
|
if (this.verbose) {
|
|
712
|
-
const chalk = require(
|
|
713
|
-
console.error(chalk.dim(
|
|
905
|
+
const chalk = require('chalk');
|
|
906
|
+
console.error(chalk.dim('\n--- File Upload Debug ---'));
|
|
714
907
|
console.error(chalk.dim(`File: ${filePath}`));
|
|
715
908
|
console.error(chalk.dim(`Original filename: ${originalFilename}`));
|
|
716
909
|
console.error(chalk.dim(`Sanitized filename: ${sanitizedFilename}`));
|
|
@@ -719,7 +912,7 @@ class ToothFairyAPI {
|
|
|
719
912
|
console.error(chalk.dim(`Content type: ${finalContentType}`));
|
|
720
913
|
console.error(chalk.dim(`Size: ${fileSizeInMB.toFixed(2)}MB`));
|
|
721
914
|
console.error(chalk.dim(`Upload URL: ${parsedUploadData.uploadURL}`));
|
|
722
|
-
console.error(chalk.dim(
|
|
915
|
+
console.error(chalk.dim('------------------------\n'));
|
|
723
916
|
}
|
|
724
917
|
|
|
725
918
|
try {
|
|
@@ -731,7 +924,7 @@ class ToothFairyAPI {
|
|
|
731
924
|
// Remove S3 bucket prefix from filePath to get clean filename for download
|
|
732
925
|
downloadFilename = parsedUploadData.filePath.replace(
|
|
733
926
|
/^s3:\/\/[^/]+\//,
|
|
734
|
-
|
|
927
|
+
''
|
|
735
928
|
);
|
|
736
929
|
}
|
|
737
930
|
|
|
@@ -749,7 +942,7 @@ class ToothFairyAPI {
|
|
|
749
942
|
if (error.response) {
|
|
750
943
|
throw new Error(
|
|
751
944
|
`Upload failed: HTTP ${error.response.status}: ${
|
|
752
|
-
error.response.data?.message ||
|
|
945
|
+
error.response.data?.message || 'File upload failed'
|
|
753
946
|
}`
|
|
754
947
|
);
|
|
755
948
|
}
|
|
@@ -765,7 +958,7 @@ class ToothFairyAPI {
|
|
|
765
958
|
* @param {string} context - Context for the download (default: "pdf")
|
|
766
959
|
* @returns {Promise<Object>} - Download URL and metadata
|
|
767
960
|
*/
|
|
768
|
-
async getDownloadUrl(filename, workspaceId, context =
|
|
961
|
+
async getDownloadUrl(filename, workspaceId, context = 'pdf') {
|
|
769
962
|
const params = new URLSearchParams({
|
|
770
963
|
filename: filename,
|
|
771
964
|
context: context,
|
|
@@ -773,7 +966,7 @@ class ToothFairyAPI {
|
|
|
773
966
|
});
|
|
774
967
|
|
|
775
968
|
const config = {
|
|
776
|
-
method:
|
|
969
|
+
method: 'GET',
|
|
777
970
|
url: `${
|
|
778
971
|
this.baseUrl
|
|
779
972
|
}/documents/requestDownloadURLIncognito?${params.toString()}`,
|
|
@@ -781,10 +974,10 @@ class ToothFairyAPI {
|
|
|
781
974
|
};
|
|
782
975
|
|
|
783
976
|
if (this.verbose) {
|
|
784
|
-
const chalk = require(
|
|
785
|
-
console.error(chalk.dim(
|
|
977
|
+
const chalk = require('chalk');
|
|
978
|
+
console.error(chalk.dim('\n--- Download URL Request Debug ---'));
|
|
786
979
|
console.error(chalk.dim(`URL: ${config.url}`));
|
|
787
|
-
console.error(chalk.dim(
|
|
980
|
+
console.error(chalk.dim('-----------------------------\n'));
|
|
788
981
|
}
|
|
789
982
|
|
|
790
983
|
try {
|
|
@@ -794,7 +987,7 @@ class ToothFairyAPI {
|
|
|
794
987
|
if (error.response) {
|
|
795
988
|
throw new Error(
|
|
796
989
|
`HTTP ${error.response.status}: ${
|
|
797
|
-
error.response.data.message ||
|
|
990
|
+
error.response.data.message || 'Download URL request failed'
|
|
798
991
|
}`
|
|
799
992
|
);
|
|
800
993
|
}
|
|
@@ -816,7 +1009,7 @@ class ToothFairyAPI {
|
|
|
816
1009
|
filename,
|
|
817
1010
|
workspaceId,
|
|
818
1011
|
outputPath,
|
|
819
|
-
context =
|
|
1012
|
+
context = 'pdf',
|
|
820
1013
|
onProgress = null
|
|
821
1014
|
) {
|
|
822
1015
|
// Get download URL
|
|
@@ -827,7 +1020,7 @@ class ToothFairyAPI {
|
|
|
827
1020
|
);
|
|
828
1021
|
|
|
829
1022
|
if (!downloadData.url) {
|
|
830
|
-
throw new Error(
|
|
1023
|
+
throw new Error('Failed to get download URL from server');
|
|
831
1024
|
}
|
|
832
1025
|
|
|
833
1026
|
// Ensure output directory exists
|
|
@@ -838,9 +1031,9 @@ class ToothFairyAPI {
|
|
|
838
1031
|
|
|
839
1032
|
// Download file
|
|
840
1033
|
const downloadConfig = {
|
|
841
|
-
method:
|
|
1034
|
+
method: 'GET',
|
|
842
1035
|
url: downloadData.url,
|
|
843
|
-
responseType:
|
|
1036
|
+
responseType: 'stream',
|
|
844
1037
|
onDownloadProgress: onProgress
|
|
845
1038
|
? (progressEvent) => {
|
|
846
1039
|
if (progressEvent.total) {
|
|
@@ -858,11 +1051,11 @@ class ToothFairyAPI {
|
|
|
858
1051
|
};
|
|
859
1052
|
|
|
860
1053
|
if (this.verbose) {
|
|
861
|
-
const chalk = require(
|
|
862
|
-
console.error(chalk.dim(
|
|
1054
|
+
const chalk = require('chalk');
|
|
1055
|
+
console.error(chalk.dim('\n--- File Download Debug ---'));
|
|
863
1056
|
console.error(chalk.dim(`Download URL: ${downloadData.url}`));
|
|
864
1057
|
console.error(chalk.dim(`Output Path: ${outputPath}`));
|
|
865
|
-
console.error(chalk.dim(
|
|
1058
|
+
console.error(chalk.dim('---------------------------\n'));
|
|
866
1059
|
}
|
|
867
1060
|
|
|
868
1061
|
try {
|
|
@@ -873,7 +1066,7 @@ class ToothFairyAPI {
|
|
|
873
1066
|
response.data.pipe(writer);
|
|
874
1067
|
|
|
875
1068
|
return new Promise((resolve, reject) => {
|
|
876
|
-
writer.on(
|
|
1069
|
+
writer.on('finish', () => {
|
|
877
1070
|
const stats = fs.statSync(outputPath);
|
|
878
1071
|
resolve({
|
|
879
1072
|
success: true,
|
|
@@ -882,13 +1075,13 @@ class ToothFairyAPI {
|
|
|
882
1075
|
sizeInMB: (stats.size / (1024 * 1024)).toFixed(2),
|
|
883
1076
|
});
|
|
884
1077
|
});
|
|
885
|
-
writer.on(
|
|
1078
|
+
writer.on('error', reject);
|
|
886
1079
|
});
|
|
887
1080
|
} catch (error) {
|
|
888
1081
|
if (error.response) {
|
|
889
1082
|
throw new Error(
|
|
890
1083
|
`Download failed: HTTP ${error.response.status}: ${
|
|
891
|
-
error.response.data?.message ||
|
|
1084
|
+
error.response.data?.message || 'File download failed'
|
|
892
1085
|
}`
|
|
893
1086
|
);
|
|
894
1087
|
}
|
|
@@ -904,11 +1097,11 @@ class ToothFairyAPI {
|
|
|
904
1097
|
*/
|
|
905
1098
|
_sanitizeFilename(filename) {
|
|
906
1099
|
// Remove non-alphanumeric characters except dots
|
|
907
|
-
let sanitized = filename.replace(/[^a-zA-Z0-9.]/g,
|
|
1100
|
+
let sanitized = filename.replace(/[^a-zA-Z0-9.]/g, '');
|
|
908
1101
|
|
|
909
1102
|
// Check length limit
|
|
910
1103
|
if (sanitized.length > 100) {
|
|
911
|
-
throw new Error(
|
|
1104
|
+
throw new Error('File name cannot be more than 100 characters.');
|
|
912
1105
|
}
|
|
913
1106
|
|
|
914
1107
|
// Add timestamp prefix like frontend
|
|
@@ -927,20 +1120,20 @@ class ToothFairyAPI {
|
|
|
927
1120
|
const ext = path.extname(filePath).toLowerCase().slice(1);
|
|
928
1121
|
|
|
929
1122
|
// Image extensions
|
|
930
|
-
const imageExts = [
|
|
1123
|
+
const imageExts = ['png', 'jpg', 'jpeg', 'gif', 'bmp', 'svg'];
|
|
931
1124
|
// Video extensions
|
|
932
|
-
const videoExts = [
|
|
1125
|
+
const videoExts = ['mp4', 'avi', 'mov', 'wmv', 'flv', 'webm'];
|
|
933
1126
|
// Audio extensions
|
|
934
|
-
const audioExts = [
|
|
1127
|
+
const audioExts = ['wav', 'mp3', 'aac', 'ogg', 'flac'];
|
|
935
1128
|
|
|
936
1129
|
if (imageExts.includes(ext)) {
|
|
937
|
-
return
|
|
1130
|
+
return 'imported-image';
|
|
938
1131
|
} else if (videoExts.includes(ext)) {
|
|
939
|
-
return
|
|
1132
|
+
return 'imported_video_files';
|
|
940
1133
|
} else if (audioExts.includes(ext)) {
|
|
941
|
-
return
|
|
1134
|
+
return 'imported_audio_files';
|
|
942
1135
|
} else {
|
|
943
|
-
return
|
|
1136
|
+
return 'imported_doc_files';
|
|
944
1137
|
}
|
|
945
1138
|
}
|
|
946
1139
|
|
|
@@ -954,38 +1147,38 @@ class ToothFairyAPI {
|
|
|
954
1147
|
const ext = path.extname(filePath).toLowerCase().slice(1);
|
|
955
1148
|
|
|
956
1149
|
const contentTypes = {
|
|
957
|
-
txt:
|
|
958
|
-
md:
|
|
959
|
-
html:
|
|
960
|
-
pdf:
|
|
961
|
-
json:
|
|
962
|
-
jsonl:
|
|
963
|
-
wav:
|
|
964
|
-
mp4:
|
|
965
|
-
png:
|
|
966
|
-
jpg:
|
|
967
|
-
jpeg:
|
|
968
|
-
csv:
|
|
969
|
-
docx:
|
|
970
|
-
xlsx:
|
|
971
|
-
pptx:
|
|
972
|
-
ppt:
|
|
973
|
-
java:
|
|
974
|
-
py:
|
|
975
|
-
js:
|
|
976
|
-
ts:
|
|
977
|
-
tsx:
|
|
978
|
-
jsx:
|
|
979
|
-
yaml:
|
|
980
|
-
yml:
|
|
981
|
-
sql:
|
|
982
|
-
sh:
|
|
983
|
-
php:
|
|
984
|
-
csharp:
|
|
985
|
-
rb:
|
|
1150
|
+
txt: 'text/plain',
|
|
1151
|
+
md: 'text/markdown',
|
|
1152
|
+
html: 'text/html',
|
|
1153
|
+
pdf: 'application/pdf',
|
|
1154
|
+
json: 'application/json',
|
|
1155
|
+
jsonl: 'application/json',
|
|
1156
|
+
wav: 'audio/wav',
|
|
1157
|
+
mp4: 'video/mp4',
|
|
1158
|
+
png: 'image/png',
|
|
1159
|
+
jpg: 'image/jpeg',
|
|
1160
|
+
jpeg: 'image/jpeg',
|
|
1161
|
+
csv: 'text/csv',
|
|
1162
|
+
docx: 'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
|
|
1163
|
+
xlsx: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
|
|
1164
|
+
pptx: 'application/vnd.openxmlformats-officedocument.presentationml.presentation',
|
|
1165
|
+
ppt: 'application/vnd.ms-powerpoint',
|
|
1166
|
+
java: 'text/plain',
|
|
1167
|
+
py: 'text/plain',
|
|
1168
|
+
js: 'text/plain',
|
|
1169
|
+
ts: 'text/plain',
|
|
1170
|
+
tsx: 'text/plain',
|
|
1171
|
+
jsx: 'text/plain',
|
|
1172
|
+
yaml: 'text/plain',
|
|
1173
|
+
yml: 'text/plain',
|
|
1174
|
+
sql: 'text/plain',
|
|
1175
|
+
sh: 'text/plain',
|
|
1176
|
+
php: 'text/plain',
|
|
1177
|
+
csharp: 'text/plain',
|
|
1178
|
+
rb: 'text/plain',
|
|
986
1179
|
};
|
|
987
1180
|
|
|
988
|
-
return contentTypes[ext] ||
|
|
1181
|
+
return contentTypes[ext] || 'application/octet-stream';
|
|
989
1182
|
}
|
|
990
1183
|
|
|
991
1184
|
/**
|
|
@@ -996,18 +1189,18 @@ class ToothFairyAPI {
|
|
|
996
1189
|
*/
|
|
997
1190
|
_getDocumentType(filePath) {
|
|
998
1191
|
// Check if it's a URL
|
|
999
|
-
if (filePath.startsWith(
|
|
1000
|
-
return
|
|
1192
|
+
if (filePath.startsWith('http://') || filePath.startsWith('https://')) {
|
|
1193
|
+
return 'readComprehensionUrl';
|
|
1001
1194
|
}
|
|
1002
1195
|
|
|
1003
1196
|
// Check if it's a PDF file
|
|
1004
1197
|
const ext = path.extname(filePath).toLowerCase().slice(1);
|
|
1005
|
-
if (ext ===
|
|
1006
|
-
return
|
|
1198
|
+
if (ext === 'pdf') {
|
|
1199
|
+
return 'readComprehensionPdf';
|
|
1007
1200
|
}
|
|
1008
1201
|
|
|
1009
1202
|
// For all other file types (Excel, CSV, etc.)
|
|
1010
|
-
return
|
|
1203
|
+
return 'readComprehensionFile';
|
|
1011
1204
|
}
|
|
1012
1205
|
|
|
1013
1206
|
/**
|
|
@@ -1026,9 +1219,9 @@ class ToothFairyAPI {
|
|
|
1026
1219
|
filePath,
|
|
1027
1220
|
userId,
|
|
1028
1221
|
title = null,
|
|
1029
|
-
folderId =
|
|
1222
|
+
folderId = 'mrcRoot',
|
|
1030
1223
|
topics = [],
|
|
1031
|
-
status =
|
|
1224
|
+
status = 'published',
|
|
1032
1225
|
scope = null
|
|
1033
1226
|
) {
|
|
1034
1227
|
try {
|
|
@@ -1038,7 +1231,7 @@ class ToothFairyAPI {
|
|
|
1038
1231
|
// Use filename as title if not provided
|
|
1039
1232
|
let documentTitle = title;
|
|
1040
1233
|
if (!documentTitle) {
|
|
1041
|
-
if (filePath.startsWith(
|
|
1234
|
+
if (filePath.startsWith('http://') || filePath.startsWith('https://')) {
|
|
1042
1235
|
documentTitle = filePath;
|
|
1043
1236
|
} else {
|
|
1044
1237
|
documentTitle = path.basename(filePath);
|
|
@@ -1047,7 +1240,7 @@ class ToothFairyAPI {
|
|
|
1047
1240
|
|
|
1048
1241
|
// Determine external path based on file type
|
|
1049
1242
|
let externalPath;
|
|
1050
|
-
if (type ===
|
|
1243
|
+
if (type === 'readComprehensionUrl') {
|
|
1051
1244
|
externalPath = filePath; // Use URL directly
|
|
1052
1245
|
} else {
|
|
1053
1246
|
// For files, construct S3 path
|
|
@@ -1065,14 +1258,14 @@ class ToothFairyAPI {
|
|
|
1065
1258
|
topics: topics,
|
|
1066
1259
|
folderid: folderId,
|
|
1067
1260
|
external_path: externalPath,
|
|
1068
|
-
source:
|
|
1261
|
+
source: 'api',
|
|
1069
1262
|
status: status,
|
|
1070
1263
|
...(scope && { scope: scope }),
|
|
1071
1264
|
},
|
|
1072
1265
|
],
|
|
1073
1266
|
};
|
|
1074
1267
|
|
|
1075
|
-
return await this._makeRequest(
|
|
1268
|
+
return await this._makeRequest('POST', 'doc/create', documentData);
|
|
1076
1269
|
} catch (error) {
|
|
1077
1270
|
console.error(`Error creating document: ${error.message}`);
|
|
1078
1271
|
throw error;
|
|
@@ -1103,7 +1296,7 @@ class ToothFairyAPI {
|
|
|
1103
1296
|
},
|
|
1104
1297
|
};
|
|
1105
1298
|
|
|
1106
|
-
return await this._makeRequest(
|
|
1299
|
+
return await this._makeRequest('POST', 'doc/update', updateData);
|
|
1107
1300
|
} catch (error) {
|
|
1108
1301
|
console.error(`Error updating document: ${error.message}`);
|
|
1109
1302
|
throw error;
|
|
@@ -1118,7 +1311,7 @@ class ToothFairyAPI {
|
|
|
1118
1311
|
*/
|
|
1119
1312
|
async deleteDocument(documentId) {
|
|
1120
1313
|
try {
|
|
1121
|
-
return await this._makeRequest(
|
|
1314
|
+
return await this._makeRequest('DELETE', `doc/delete/${documentId}`);
|
|
1122
1315
|
} catch (error) {
|
|
1123
1316
|
console.error(`Error deleting document: ${error.message}`);
|
|
1124
1317
|
throw error;
|
|
@@ -1133,7 +1326,7 @@ class ToothFairyAPI {
|
|
|
1133
1326
|
*/
|
|
1134
1327
|
async getDocument(documentId) {
|
|
1135
1328
|
try {
|
|
1136
|
-
return await this._makeRequest(
|
|
1329
|
+
return await this._makeRequest('GET', `doc/get/${documentId}`);
|
|
1137
1330
|
} catch (error) {
|
|
1138
1331
|
console.error(`Error getting document: ${error.message}`);
|
|
1139
1332
|
throw error;
|
|
@@ -1155,7 +1348,7 @@ class ToothFairyAPI {
|
|
|
1155
1348
|
async createEntity(
|
|
1156
1349
|
userId,
|
|
1157
1350
|
label,
|
|
1158
|
-
type =
|
|
1351
|
+
type = 'topic',
|
|
1159
1352
|
description = null,
|
|
1160
1353
|
emoji = null,
|
|
1161
1354
|
parentEntity = null,
|
|
@@ -1173,7 +1366,7 @@ class ToothFairyAPI {
|
|
|
1173
1366
|
...(backgroundColor && { backgroundColor: backgroundColor }),
|
|
1174
1367
|
};
|
|
1175
1368
|
|
|
1176
|
-
return await this._makeRequest(
|
|
1369
|
+
return await this._makeRequest('POST', 'entity/create', entityData);
|
|
1177
1370
|
} catch (error) {
|
|
1178
1371
|
console.error(`Error creating entity: ${error.message}`);
|
|
1179
1372
|
throw error;
|
|
@@ -1200,7 +1393,7 @@ class ToothFairyAPI {
|
|
|
1200
1393
|
...fields,
|
|
1201
1394
|
};
|
|
1202
1395
|
|
|
1203
|
-
return await this._makeRequest(
|
|
1396
|
+
return await this._makeRequest('POST', 'entity/update', updateData);
|
|
1204
1397
|
} catch (error) {
|
|
1205
1398
|
console.error(`Error updating entity: ${error.message}`);
|
|
1206
1399
|
throw error;
|
|
@@ -1215,7 +1408,7 @@ class ToothFairyAPI {
|
|
|
1215
1408
|
*/
|
|
1216
1409
|
async deleteEntity(entityId) {
|
|
1217
1410
|
try {
|
|
1218
|
-
return await this._makeRequest(
|
|
1411
|
+
return await this._makeRequest('DELETE', `entity/delete/${entityId}`);
|
|
1219
1412
|
} catch (error) {
|
|
1220
1413
|
console.error(`Error deleting entity: ${error.message}`);
|
|
1221
1414
|
throw error;
|
|
@@ -1230,7 +1423,7 @@ class ToothFairyAPI {
|
|
|
1230
1423
|
*/
|
|
1231
1424
|
async getEntity(entityId) {
|
|
1232
1425
|
try {
|
|
1233
|
-
return await this._makeRequest(
|
|
1426
|
+
return await this._makeRequest('GET', `entity/get/${entityId}`);
|
|
1234
1427
|
} catch (error) {
|
|
1235
1428
|
console.error(`Error getting entity: ${error.message}`);
|
|
1236
1429
|
throw error;
|
|
@@ -1249,7 +1442,7 @@ class ToothFairyAPI {
|
|
|
1249
1442
|
if (limit) {
|
|
1250
1443
|
params.limit = limit;
|
|
1251
1444
|
}
|
|
1252
|
-
return await this._makeRequest(
|
|
1445
|
+
return await this._makeRequest('GET', 'entity/list', params);
|
|
1253
1446
|
} catch (error) {
|
|
1254
1447
|
console.error(`Error listing entities: ${error.message}`);
|
|
1255
1448
|
throw error;
|
|
@@ -1287,7 +1480,7 @@ class ToothFairyAPI {
|
|
|
1287
1480
|
...(status && { status: status }),
|
|
1288
1481
|
...(parent && { parent: parent }),
|
|
1289
1482
|
};
|
|
1290
|
-
return await this._makeRequest(
|
|
1483
|
+
return await this._makeRequest('POST', 'folder/create', folderData);
|
|
1291
1484
|
} catch (error) {
|
|
1292
1485
|
console.error(`Error creating folder: ${error.message}`);
|
|
1293
1486
|
throw error;
|
|
@@ -1322,7 +1515,7 @@ class ToothFairyAPI {
|
|
|
1322
1515
|
...(status && { status: status }),
|
|
1323
1516
|
...(parent !== null && { parent: parent }),
|
|
1324
1517
|
};
|
|
1325
|
-
return await this._makeRequest(
|
|
1518
|
+
return await this._makeRequest('POST', 'folder/update', updateData);
|
|
1326
1519
|
} catch (error) {
|
|
1327
1520
|
console.error(`Error updating folder: ${error.message}`);
|
|
1328
1521
|
throw error;
|
|
@@ -1337,7 +1530,7 @@ class ToothFairyAPI {
|
|
|
1337
1530
|
*/
|
|
1338
1531
|
async deleteFolder(folderId) {
|
|
1339
1532
|
try {
|
|
1340
|
-
return await this._makeRequest(
|
|
1533
|
+
return await this._makeRequest('DELETE', `folder/delete/${folderId}`);
|
|
1341
1534
|
} catch (error) {
|
|
1342
1535
|
console.error(`Error deleting folder: ${error.message}`);
|
|
1343
1536
|
throw error;
|
|
@@ -1352,7 +1545,7 @@ class ToothFairyAPI {
|
|
|
1352
1545
|
*/
|
|
1353
1546
|
async getFolder(folderId) {
|
|
1354
1547
|
try {
|
|
1355
|
-
return await this._makeRequest(
|
|
1548
|
+
return await this._makeRequest('GET', `folder/get/${folderId}`);
|
|
1356
1549
|
} catch (error) {
|
|
1357
1550
|
console.error(`Error getting folder: ${error.message}`);
|
|
1358
1551
|
throw error;
|
|
@@ -1379,7 +1572,7 @@ class ToothFairyAPI {
|
|
|
1379
1572
|
if (offset) {
|
|
1380
1573
|
params.offset = offset;
|
|
1381
1574
|
}
|
|
1382
|
-
return await this._makeRequest(
|
|
1575
|
+
return await this._makeRequest('GET', 'folder/list', params);
|
|
1383
1576
|
} catch (error) {
|
|
1384
1577
|
console.error(`Error listing folders: ${error.message}`);
|
|
1385
1578
|
throw error;
|
|
@@ -1429,7 +1622,7 @@ class ToothFairyAPI {
|
|
|
1429
1622
|
...(promptPlaceholder && { promptPlaceholder: promptPlaceholder }),
|
|
1430
1623
|
...(availableToAgents && { availableToAgents: availableToAgents }),
|
|
1431
1624
|
};
|
|
1432
|
-
return await this._makeRequest(
|
|
1625
|
+
return await this._makeRequest('POST', 'prompt/create', promptData);
|
|
1433
1626
|
} catch (error) {
|
|
1434
1627
|
console.error(`Error creating prompt: ${error.message}`);
|
|
1435
1628
|
throw error;
|
|
@@ -1482,7 +1675,7 @@ class ToothFairyAPI {
|
|
|
1482
1675
|
availableToAgents: availableToAgents,
|
|
1483
1676
|
}),
|
|
1484
1677
|
};
|
|
1485
|
-
return await this._makeRequest(
|
|
1678
|
+
return await this._makeRequest('POST', 'prompt/update', updateData);
|
|
1486
1679
|
} catch (error) {
|
|
1487
1680
|
console.error(`Error updating prompt: ${error.message}`);
|
|
1488
1681
|
throw error;
|
|
@@ -1497,7 +1690,7 @@ class ToothFairyAPI {
|
|
|
1497
1690
|
*/
|
|
1498
1691
|
async deletePrompt(promptId) {
|
|
1499
1692
|
try {
|
|
1500
|
-
return await this._makeRequest(
|
|
1693
|
+
return await this._makeRequest('DELETE', `prompt/delete/${promptId}`);
|
|
1501
1694
|
} catch (error) {
|
|
1502
1695
|
console.error(`Error deleting prompt: ${error.message}`);
|
|
1503
1696
|
throw error;
|
|
@@ -1512,7 +1705,7 @@ class ToothFairyAPI {
|
|
|
1512
1705
|
*/
|
|
1513
1706
|
async getPrompt(promptId) {
|
|
1514
1707
|
try {
|
|
1515
|
-
return await this._makeRequest(
|
|
1708
|
+
return await this._makeRequest('GET', `prompt/get/${promptId}`);
|
|
1516
1709
|
} catch (error) {
|
|
1517
1710
|
console.error(`Error getting prompt: ${error.message}`);
|
|
1518
1711
|
throw error;
|
|
@@ -1539,7 +1732,7 @@ class ToothFairyAPI {
|
|
|
1539
1732
|
if (offset) {
|
|
1540
1733
|
params.offset = offset;
|
|
1541
1734
|
}
|
|
1542
|
-
return await this._makeRequest(
|
|
1735
|
+
return await this._makeRequest('GET', 'prompt/list', params);
|
|
1543
1736
|
} catch (error) {
|
|
1544
1737
|
console.error(`Error listing prompts: ${error.message}`);
|
|
1545
1738
|
throw error;
|
|
@@ -1564,18 +1757,18 @@ class ToothFairyAPI {
|
|
|
1564
1757
|
};
|
|
1565
1758
|
|
|
1566
1759
|
const config = {
|
|
1567
|
-
method:
|
|
1760
|
+
method: 'POST',
|
|
1568
1761
|
url: `${this.baseUrl}/media/audio_generation`,
|
|
1569
1762
|
headers: {
|
|
1570
|
-
|
|
1571
|
-
|
|
1763
|
+
'Content-Type': 'application/json',
|
|
1764
|
+
'x-api-key': this.headers['x-api-key'],
|
|
1572
1765
|
},
|
|
1573
1766
|
data: speechData,
|
|
1574
1767
|
};
|
|
1575
1768
|
|
|
1576
1769
|
if (this.verbose) {
|
|
1577
|
-
const chalk = require(
|
|
1578
|
-
console.error(chalk.dim(
|
|
1770
|
+
const chalk = require('chalk');
|
|
1771
|
+
console.error(chalk.dim('\n--- Speech Generation Request Debug ---'));
|
|
1579
1772
|
console.error(chalk.dim(`Method: ${config.method}`));
|
|
1580
1773
|
console.error(chalk.dim(`URL: ${config.url}`));
|
|
1581
1774
|
console.error(
|
|
@@ -1584,28 +1777,28 @@ class ToothFairyAPI {
|
|
|
1584
1777
|
console.error(
|
|
1585
1778
|
chalk.dim(`Data: ${JSON.stringify(config.data, null, 2)}`)
|
|
1586
1779
|
);
|
|
1587
|
-
console.error(chalk.dim(
|
|
1780
|
+
console.error(chalk.dim('--------------------------------------\n'));
|
|
1588
1781
|
}
|
|
1589
1782
|
|
|
1590
1783
|
const response = await axios(config);
|
|
1591
1784
|
|
|
1592
1785
|
if (this.verbose) {
|
|
1593
|
-
const chalk = require(
|
|
1594
|
-
console.error(chalk.dim(
|
|
1786
|
+
const chalk = require('chalk');
|
|
1787
|
+
console.error(chalk.dim('\n--- Speech Generation Response Debug ---'));
|
|
1595
1788
|
console.error(
|
|
1596
1789
|
chalk.dim(`Status: ${response.status} ${response.statusText}`)
|
|
1597
1790
|
);
|
|
1598
1791
|
console.error(
|
|
1599
1792
|
chalk.dim(`Response Data: ${JSON.stringify(response.data, null, 2)}`)
|
|
1600
1793
|
);
|
|
1601
|
-
console.error(chalk.dim(
|
|
1794
|
+
console.error(chalk.dim('---------------------------------------\n'));
|
|
1602
1795
|
}
|
|
1603
1796
|
|
|
1604
1797
|
return response.data;
|
|
1605
1798
|
} catch (error) {
|
|
1606
1799
|
if (this.verbose) {
|
|
1607
|
-
const chalk = require(
|
|
1608
|
-
console.error(chalk.red(
|
|
1800
|
+
const chalk = require('chalk');
|
|
1801
|
+
console.error(chalk.red('\n--- Speech Generation Error Debug ---'));
|
|
1609
1802
|
console.error(chalk.red(`Error: ${error.message}`));
|
|
1610
1803
|
if (error.response) {
|
|
1611
1804
|
console.error(
|
|
@@ -1619,7 +1812,7 @@ class ToothFairyAPI {
|
|
|
1619
1812
|
)
|
|
1620
1813
|
);
|
|
1621
1814
|
}
|
|
1622
|
-
console.error(chalk.red(
|
|
1815
|
+
console.error(chalk.red('------------------------------------\n'));
|
|
1623
1816
|
}
|
|
1624
1817
|
console.error(`Error generating speech: ${error.message}`);
|
|
1625
1818
|
throw error;
|
|
@@ -1642,18 +1835,18 @@ class ToothFairyAPI {
|
|
|
1642
1835
|
};
|
|
1643
1836
|
|
|
1644
1837
|
const config = {
|
|
1645
|
-
method:
|
|
1838
|
+
method: 'POST',
|
|
1646
1839
|
url: `${this.baseUrl}/media/audio`,
|
|
1647
1840
|
headers: {
|
|
1648
|
-
|
|
1649
|
-
|
|
1841
|
+
'Content-Type': 'application/json',
|
|
1842
|
+
'x-api-key': this.headers['x-api-key'],
|
|
1650
1843
|
},
|
|
1651
1844
|
data: audioData,
|
|
1652
1845
|
};
|
|
1653
1846
|
|
|
1654
1847
|
if (this.verbose) {
|
|
1655
|
-
const chalk = require(
|
|
1656
|
-
console.error(chalk.dim(
|
|
1848
|
+
const chalk = require('chalk');
|
|
1849
|
+
console.error(chalk.dim('\n--- Audio Processing Request Debug ---'));
|
|
1657
1850
|
console.error(chalk.dim(`Method: ${config.method}`));
|
|
1658
1851
|
console.error(chalk.dim(`URL: ${config.url}`));
|
|
1659
1852
|
console.error(
|
|
@@ -1662,28 +1855,28 @@ class ToothFairyAPI {
|
|
|
1662
1855
|
console.error(
|
|
1663
1856
|
chalk.dim(`Data: ${JSON.stringify(config.data, null, 2)}`)
|
|
1664
1857
|
);
|
|
1665
|
-
console.error(chalk.dim(
|
|
1858
|
+
console.error(chalk.dim('------------------------------------\n'));
|
|
1666
1859
|
}
|
|
1667
1860
|
|
|
1668
1861
|
const response = await axios(config);
|
|
1669
1862
|
|
|
1670
1863
|
if (this.verbose) {
|
|
1671
|
-
const chalk = require(
|
|
1672
|
-
console.error(chalk.dim(
|
|
1864
|
+
const chalk = require('chalk');
|
|
1865
|
+
console.error(chalk.dim('\n--- Audio Processing Response Debug ---'));
|
|
1673
1866
|
console.error(
|
|
1674
1867
|
chalk.dim(`Status: ${response.status} ${response.statusText}`)
|
|
1675
1868
|
);
|
|
1676
1869
|
console.error(
|
|
1677
1870
|
chalk.dim(`Response Data: ${JSON.stringify(response.data, null, 2)}`)
|
|
1678
1871
|
);
|
|
1679
|
-
console.error(chalk.dim(
|
|
1872
|
+
console.error(chalk.dim('-------------------------------------\n'));
|
|
1680
1873
|
}
|
|
1681
1874
|
|
|
1682
1875
|
return response.data;
|
|
1683
1876
|
} catch (error) {
|
|
1684
1877
|
if (this.verbose) {
|
|
1685
|
-
const chalk = require(
|
|
1686
|
-
console.error(chalk.red(
|
|
1878
|
+
const chalk = require('chalk');
|
|
1879
|
+
console.error(chalk.red('\n--- Audio Processing Error Debug ---'));
|
|
1687
1880
|
console.error(chalk.red(`Error: ${error.message}`));
|
|
1688
1881
|
if (error.response) {
|
|
1689
1882
|
console.error(
|
|
@@ -1697,7 +1890,7 @@ class ToothFairyAPI {
|
|
|
1697
1890
|
)
|
|
1698
1891
|
);
|
|
1699
1892
|
}
|
|
1700
|
-
console.error(chalk.red(
|
|
1893
|
+
console.error(chalk.red('----------------------------------\n'));
|
|
1701
1894
|
}
|
|
1702
1895
|
console.error(`Error processing audio: ${error.message}`);
|
|
1703
1896
|
throw error;
|
|
@@ -1724,16 +1917,16 @@ class ToothFairyAPI {
|
|
|
1724
1917
|
try {
|
|
1725
1918
|
// Validate attachment limits
|
|
1726
1919
|
if (attachments.images && attachments.images.length > 1) {
|
|
1727
|
-
throw new Error(
|
|
1920
|
+
throw new Error('Maximum 1 image attachment allowed');
|
|
1728
1921
|
}
|
|
1729
1922
|
if (attachments.audios && attachments.audios.length > 1) {
|
|
1730
|
-
throw new Error(
|
|
1923
|
+
throw new Error('Maximum 1 audio attachment allowed');
|
|
1731
1924
|
}
|
|
1732
1925
|
if (attachments.videos && attachments.videos.length > 1) {
|
|
1733
|
-
throw new Error(
|
|
1926
|
+
throw new Error('Maximum 1 video attachment allowed');
|
|
1734
1927
|
}
|
|
1735
1928
|
if (attachments.files && attachments.files.length > 5) {
|
|
1736
|
-
throw new Error(
|
|
1929
|
+
throw new Error('Maximum 5 file attachments allowed');
|
|
1737
1930
|
}
|
|
1738
1931
|
|
|
1739
1932
|
// Process images
|
|
@@ -1789,10 +1982,10 @@ class ToothFairyAPI {
|
|
|
1789
1982
|
}
|
|
1790
1983
|
|
|
1791
1984
|
if (this.verbose) {
|
|
1792
|
-
const chalk = require(
|
|
1793
|
-
console.error(chalk.dim(
|
|
1985
|
+
const chalk = require('chalk');
|
|
1986
|
+
console.error(chalk.dim('\n--- Processed Attachments ---'));
|
|
1794
1987
|
console.error(chalk.dim(`Result: ${JSON.stringify(result, null, 2)}`));
|
|
1795
|
-
console.error(chalk.dim(
|
|
1988
|
+
console.error(chalk.dim('-----------------------------\n'));
|
|
1796
1989
|
}
|
|
1797
1990
|
|
|
1798
1991
|
return result;
|