@cloudbase/agent-adapter-adp 0.0.16 → 0.0.19

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/dist/index.mjs CHANGED
@@ -93,18 +93,20 @@ var AdpAgent = class extends AbstractAgent {
93
93
  constructor(config) {
94
94
  super(config);
95
95
  this.finalAppKey = "";
96
+ this.finalCloudCredential = {};
96
97
  this.adpConfig = config.adpConfig;
97
98
  this.finalAppKey = this.adpConfig.appKey || this.adpConfig.request?.body?.botAppKey || process.env.ADP_APP_KEY || "";
98
99
  this.reqAppClient = axios.create({
99
100
  baseURL: this.adpConfig.request?.baseUrl || "https://wss.lke.cloud.tencent.com"
100
101
  });
101
102
  const LkeClient = lke.v20231130.Client;
103
+ this.finalCloudCredential = {
104
+ secretId: this.adpConfig.credential?.secretId || process.env.TENCENTCLOUD_SECRETID,
105
+ secretKey: this.adpConfig.credential?.secretKey || process.env.TENCENTCLOUD_SECRETKEY,
106
+ token: this.adpConfig.credential?.token || process.env.TENCENTCLOUD_SESSIONTOKEN
107
+ };
102
108
  this.reqLkeClient = new LkeClient({
103
- credential: {
104
- secretId: this.adpConfig.credential?.secretId || process.env.TENCENTCLOUD_SECRETID,
105
- secretKey: this.adpConfig.credential?.secretKey || process.env.TENCENTCLOUD_SECRETKEY,
106
- token: this.adpConfig.credential?.token || process.env.TENCENTCLOUD_SESSIONTOKEN
107
- }
109
+ credential: this.finalCloudCredential
108
110
  });
109
111
  }
110
112
  generateRequestBody({
@@ -133,9 +135,11 @@ var AdpAgent = class extends AbstractAgent {
133
135
  });
134
136
  }
135
137
  async _run(subscriber, input) {
138
+ let thinkingMessageSet = /* @__PURE__ */ new Set();
139
+ let thinkFinishedMessageSet = /* @__PURE__ */ new Set();
136
140
  try {
137
- const { runId, threadId: _threadId } = input;
138
- const threadId = _threadId || randomUUID();
141
+ const { runId } = input;
142
+ const threadId = input.threadId || randomUUID();
139
143
  subscriber.next({
140
144
  type: EventType.RUN_STARTED,
141
145
  runId,
@@ -147,19 +151,26 @@ var AdpAgent = class extends AbstractAgent {
147
151
  "MISSING_APP_KEY"
148
152
  );
149
153
  }
150
- const { messages: docExtractedMessages, fileInfos } = await this.extractDocuments(input, subscriber);
151
- const { message, trimmed } = await this.convertAGUIMessagesToAdpMessages(docExtractedMessages);
154
+ const latestUserMessage = input.messages.filter((m) => m.role === "user").pop();
155
+ if (!latestUserMessage) {
156
+ throw new AdpAgentError(
157
+ "Message content format error, or empty content",
158
+ "INVALID_MESSAGE_FORMAT"
159
+ );
160
+ }
161
+ const { message: docExtractedMessage, fileInfos } = await this.extractDocuments(latestUserMessage, threadId, subscriber);
162
+ const message = await this.convertAGUIMessagesToAdpMessages(docExtractedMessage);
152
163
  if (!message) {
153
164
  throw new AdpAgentError(
154
165
  "Message content format error, or empty content",
155
166
  "INVALID_MESSAGE_FORMAT"
156
167
  );
157
168
  }
158
- if (trimmed > 0) {
169
+ if (input.messages.length > 1) {
159
170
  subscriber.next({
160
171
  type: EventType.RAW,
161
172
  rawEvent: {
162
- message: `ADP handles message history itself, so that a total of ${trimmed} messages before and including last assistant message will be trimmed.`,
173
+ message: `ADP handles message history itself, so that a total of ${input.messages.length - 1} messages before and including last assistant message will be trimmed.`,
163
174
  type: "warn"
164
175
  }
165
176
  });
@@ -178,7 +189,6 @@ var AdpAgent = class extends AbstractAgent {
178
189
  let buffer = "";
179
190
  let interruptRequested = false;
180
191
  let thinkingStart = false;
181
- let thinkingMessageSet = /* @__PURE__ */ new Set();
182
192
  for await (const chunk of sseStream) {
183
193
  buffer += chunk.toString();
184
194
  const parts = buffer.split("\n\n");
@@ -205,6 +215,22 @@ var AdpAgent = class extends AbstractAgent {
205
215
  }
206
216
  switch (data.type) {
207
217
  case "reply": {
218
+ const messageId = data.payload.record_id;
219
+ const isFinal = data.payload.is_final;
220
+ if (thinkingStart) {
221
+ thinkingStart = false;
222
+ for (const index of thinkingMessageSet) {
223
+ subscriber.next({
224
+ type: EventType.THINKING_TEXT_MESSAGE_END,
225
+ messageId: `${messageId}-think-${index}`
226
+ });
227
+ }
228
+ thinkingMessageSet.clear();
229
+ subscriber.next({
230
+ type: EventType.THINKING_END,
231
+ messageId
232
+ });
233
+ }
208
234
  if (data.payload.is_from_self) {
209
235
  if (data.payload.is_evil) {
210
236
  throw new AdpAgentError(
@@ -215,8 +241,6 @@ var AdpAgent = class extends AbstractAgent {
215
241
  continue;
216
242
  }
217
243
  }
218
- const messageId = data.payload.record_id;
219
- const isFinal = data.payload.is_final;
220
244
  data.payload.content = data.payload.content.replace(
221
245
  /\\n/g,
222
246
  "\n\n"
@@ -296,51 +320,47 @@ var AdpAgent = class extends AbstractAgent {
296
320
  });
297
321
  }
298
322
  data.payload.procedures.forEach((procedure) => {
299
- if (!thinkingMessageSet.has(messageId)) {
300
- thinkingMessageSet.add(messageId);
323
+ const index = procedure.index.toString();
324
+ if (!thinkingMessageSet.has(index) && !thinkFinishedMessageSet.has(index)) {
325
+ thinkingMessageSet.add(index);
301
326
  subscriber.next({
302
327
  type: EventType.THINKING_TEXT_MESSAGE_START,
303
- messageId,
328
+ messageId: `${messageId}-think-${index}`,
304
329
  delta: procedure.debugging.content
305
330
  });
306
331
  } else {
307
332
  if (procedure.status === "processing") {
308
333
  subscriber.next({
309
334
  type: EventType.THINKING_TEXT_MESSAGE_CONTENT,
310
- messageId,
335
+ messageId: `${messageId}-think-${index}`,
311
336
  delta: procedure.debugging.content
312
337
  });
313
338
  } else {
314
- thinkingMessageSet.delete(messageId);
315
- subscriber.next({
316
- type: EventType.THINKING_TEXT_MESSAGE_END,
317
- messageId
318
- });
339
+ thinkingMessageSet.delete(index);
340
+ if (!thinkFinishedMessageSet.has(index)) {
341
+ thinkFinishedMessageSet.add(index);
342
+ subscriber.next({
343
+ type: EventType.THINKING_TEXT_MESSAGE_END,
344
+ messageId: `${messageId}-think-${index}`
345
+ });
346
+ }
319
347
  }
320
348
  }
321
349
  });
322
- const allFinished = data.payload.procedures.every(
323
- (procedure) => procedure.status !== "processing"
324
- );
325
- if (allFinished) {
326
- thinkingStart = false;
327
- thinkingMessageSet.clear();
328
- subscriber.next({
329
- type: EventType.THINKING_END,
330
- messageId
331
- });
332
- }
333
350
  break;
334
351
  }
335
352
  case "error": {
336
- console.error(JSON.stringify(data));
353
+ console.error(
354
+ "[ERROR] ADP throws error: ",
355
+ JSON.stringify(data)
356
+ );
337
357
  throw new AdpAgentError(
338
358
  data.error.message,
339
359
  data.error.code ? `ADP_ERROR_${data.error.code}` : "ADP_ERROR_-1"
340
360
  );
341
361
  }
342
362
  case "token_stat": {
343
- console.debug(JSON.stringify(data));
363
+ console.debug("[DEBUG] ADP token stat: ", JSON.stringify(data));
344
364
  break;
345
365
  }
346
366
  case "reference": {
@@ -370,7 +390,7 @@ var AdpAgent = class extends AbstractAgent {
370
390
  }
371
391
  subscriber.complete();
372
392
  } catch (e) {
373
- console.error(JSON.stringify(e));
393
+ console.error("[ERROR] Uncaught error: ", JSON.stringify(e));
374
394
  let code = "UNKNOWN_ERROR";
375
395
  let message = JSON.stringify(e);
376
396
  if (e instanceof AxiosError) {
@@ -386,21 +406,33 @@ var AdpAgent = class extends AbstractAgent {
386
406
  message: `Sorry, an error occurred while running the agent: Error code ${code}, ${message}`
387
407
  });
388
408
  subscriber.complete();
409
+ } finally {
410
+ thinkingMessageSet.clear();
411
+ thinkFinishedMessageSet.clear();
389
412
  }
390
413
  }
391
- async convertAGUIMessagesToAdpMessages(messages) {
414
+ async convertAGUIMessagesToAdpMessages(message) {
392
415
  let result = "";
393
- let trimmed = messages.length;
394
- for (const message of messages.reverse()) {
395
- if (message.role === "assistant") {
396
- break;
397
- }
398
- if (message.role === "user") {
399
- trimmed--;
400
- let content = "";
401
- if (typeof message.content === "string") {
402
- content = message.content;
403
- } else {
416
+ if (message.role === "user") {
417
+ let content = "";
418
+ if (typeof message.content === "string") {
419
+ content = message.content;
420
+ } else {
421
+ if (this.adpConfig.enableUpload) {
422
+ if (!this.finalCloudCredential.token) {
423
+ if (!this.finalCloudCredential.secretId) {
424
+ throw new AdpAgentError(
425
+ "TENCENTCLOUD_SECRETID is required, check your env variables or config passed with the adapter",
426
+ "MISSING_SECRET_ID"
427
+ );
428
+ }
429
+ if (!this.finalCloudCredential.secretKey) {
430
+ throw new AdpAgentError(
431
+ "TENCENTCLOUD_SECRETKEY is required, check your env variables or config passed with the adapter",
432
+ "MISSING_SECRET_KEY"
433
+ );
434
+ }
435
+ }
404
436
  const imageMap = /* @__PURE__ */ new Map();
405
437
  const imagesToUpload = [];
406
438
  message.content.forEach((item) => {
@@ -443,143 +475,158 @@ var AdpAgent = class extends AbstractAgent {
443
475
  }
444
476
  }, "").trim();
445
477
  }
446
- result = `${message.role}: ${content}
478
+ }
479
+ result = `${message.role}: ${content}
447
480
  ${result}`;
448
- } else {
449
- result = `${message.role}: ${message.content}
481
+ } else {
482
+ result = `${message.role}: ${message.content}
450
483
  ${result}`;
451
- }
452
484
  }
453
- return { message: result.trim(), trimmed };
485
+ return result.trim();
454
486
  }
455
- async extractDocuments(input, subscriber) {
456
- const { messages, runId, threadId } = input;
487
+ async extractDocuments(message, threadId, subscriber) {
457
488
  const documentFiles = [];
458
- const newMessages = messages.map((msg) => {
459
- if (msg.role === "user" && Array.isArray(msg.content)) {
460
- let newContent = [];
461
- msg.content.forEach((item) => {
462
- if (item.type === "text") {
489
+ let newMessage;
490
+ if (message.role === "user" && Array.isArray(message.content)) {
491
+ let newContent = [];
492
+ message.content.forEach((item) => {
493
+ if (item.type === "text") {
494
+ newContent.push(item);
495
+ } else if (item.type === "binary") {
496
+ if (Object.keys(DOCUMENT_MIME_TYPES).includes(item.mimeType)) {
497
+ documentFiles.push(item);
498
+ } else {
463
499
  newContent.push(item);
464
- } else if (item.type === "binary") {
465
- if (Object.keys(DOCUMENT_MIME_TYPES).includes(item.mimeType)) {
466
- documentFiles.push(item);
467
- } else {
468
- newContent.push(item);
469
- }
470
500
  }
471
- });
472
- return {
473
- ...msg,
474
- content: newContent
475
- };
476
- } else return msg;
477
- });
501
+ }
502
+ });
503
+ newMessage = {
504
+ ...message,
505
+ content: newContent
506
+ };
507
+ } else newMessage = message;
478
508
  const fileInfos = [];
479
509
  const successedFiles = [];
480
510
  const failedFiles = [];
481
- if (documentFiles.length) {
482
- try {
483
- await this.uploadToCos(
484
- documentFiles,
485
- (data, cosParams, file) => {
486
- successedFiles.push({ data, cosParams, file });
487
- },
488
- (fileName, error) => {
489
- failedFiles.push({ fileName, error });
490
- }
491
- );
492
- } catch (e) {
511
+ if (this.adpConfig.enableUpload) {
512
+ if (!this.finalCloudCredential.token) {
513
+ if (!this.finalCloudCredential.secretId) {
514
+ throw new AdpAgentError(
515
+ "TENCENTCLOUD_SECRETID is required, check your env variables or config passed with the adapter",
516
+ "MISSING_SECRET_ID"
517
+ );
518
+ }
519
+ if (!this.finalCloudCredential.secretKey) {
520
+ throw new AdpAgentError(
521
+ "TENCENTCLOUD_SECRETKEY is required, check your env variables or config passed with the adapter",
522
+ "MISSING_SECRET_KEY"
523
+ );
524
+ }
493
525
  }
494
- }
495
- if (successedFiles.length) {
496
- successedFiles.forEach(async ({ data: cosData, cosParams, file }) => {
497
- const extName = MIME_TYPES[file.mimeType];
498
- const requestBody = {
499
- sessionId: threadId,
500
- botAppKey: this.finalAppKey,
501
- requestId: randomUUID(),
502
- cosBucket: cosParams.Bucket,
503
- fileType: extName,
504
- fileName: file.filename,
505
- cosUrl: cosParams.UploadPath,
506
- cosHash: cosData.headers?.["x-cos-hash-crc64ecma"] || "",
507
- eTag: cosData.ETag,
508
- size: file.data?.length.toString() || "0"
509
- };
510
- const response = await this.reqAppClient.post(
511
- this.adpConfig.request?.endpoint || "/v1/qbot/chat/docParse",
512
- camelToSnakeKeys(requestBody),
513
- { responseType: "stream" }
514
- );
515
- const sseStream = response.data;
516
- let buffer = "";
517
- for await (const chunk of sseStream) {
518
- buffer += chunk.toString();
519
- const parts = buffer.split("\n\n");
520
- buffer = parts.pop() || "";
521
- for (const part of parts) {
522
- if (!part.trim()) continue;
523
- const event = { data: "", event: "" };
524
- for (const line of part.split("\n")) {
525
- if (line.startsWith("data:")) {
526
- event.data += line.slice(5);
527
- } else if (line.startsWith("event:")) {
528
- event.event = line.slice(6);
529
- }
526
+ if (documentFiles.length) {
527
+ try {
528
+ await this.uploadToCos(
529
+ documentFiles,
530
+ (data, cosParams, file) => {
531
+ successedFiles.push({ data, cosParams, file });
532
+ },
533
+ (fileName, error) => {
534
+ failedFiles.push({ fileName, error });
530
535
  }
531
- if (event.data) {
532
- let data;
533
- try {
534
- data = JSON.parse(event.data);
535
- } catch (e) {
536
- throw new AdpAgentError(
537
- `ADP returned invalid data: ${event.data}`,
538
- "INVALID_DATA"
539
- );
536
+ );
537
+ } catch (e) {
538
+ console.error("Document upload failed: ", JSON.stringify(e));
539
+ }
540
+ }
541
+ if (successedFiles.length) {
542
+ for (const { data: cosData, cosParams, file } of successedFiles) {
543
+ const extName = MIME_TYPES[file.mimeType];
544
+ const requestBody = {
545
+ sessionId: threadId,
546
+ botAppKey: this.finalAppKey,
547
+ requestId: randomUUID(),
548
+ cosBucket: cosParams.Bucket,
549
+ fileType: extName,
550
+ fileName: file.filename,
551
+ cosUrl: cosParams.UploadPath,
552
+ cosHash: cosData.headers?.["x-cos-hash-crc64ecma"] || "",
553
+ eTag: cosData.ETag,
554
+ size: file.data?.length.toString() || "0"
555
+ };
556
+ const response = await this.reqAppClient.post(
557
+ this.adpConfig.request?.docParseEndpoint || "/v1/qbot/chat/docParse",
558
+ camelToSnakeKeys(requestBody),
559
+ { responseType: "stream" }
560
+ );
561
+ const sseStream = response.data;
562
+ let buffer = "";
563
+ for await (const chunk of sseStream) {
564
+ buffer += chunk.toString();
565
+ const parts = buffer.split("\n\n");
566
+ buffer = parts.pop() || "";
567
+ for (const part of parts) {
568
+ if (!part.trim()) continue;
569
+ const event = { data: "", event: "" };
570
+ for (const line of part.split("\n")) {
571
+ if (line.startsWith("data:")) {
572
+ event.data += line.slice(5);
573
+ } else if (line.startsWith("event:")) {
574
+ event.event = line.slice(6);
575
+ }
540
576
  }
541
- switch (data.type) {
542
- case "parsing": {
543
- subscriber.next({
544
- type: EventType.RAW,
545
- rawEvent: {
546
- message: `Parsing document ${file.filename}: ${data.payload.process}%`,
547
- type: "info"
548
- }
549
- });
550
- if (data.payload.is_final) {
551
- if (data.payload.error_message) {
552
- subscriber.next({
553
- type: EventType.RAW,
554
- rawEvent: {
555
- message: `Parsing document ${file.filename} failed: ${data.payload.error_message}`,
556
- type: "error"
557
- }
558
- });
559
- } else {
560
- const fileNameNoExt = file.filename.split(".").splice(0, -1).join(".");
561
- fileInfos.push({
562
- docId: data.payload.doc_id,
563
- fileName: fileNameNoExt,
564
- fileType: extName,
565
- fileSize: file.data?.length.toString() || "0",
566
- fileUrl: `https://${cosData.Location}`
567
- });
577
+ if (event.data) {
578
+ let data;
579
+ try {
580
+ data = JSON.parse(event.data);
581
+ } catch (e) {
582
+ throw new AdpAgentError(
583
+ `ADP returned invalid data: ${event.data}`,
584
+ "INVALID_DATA"
585
+ );
586
+ }
587
+ switch (data.type) {
588
+ case "parsing": {
589
+ subscriber.next({
590
+ type: EventType.RAW,
591
+ rawEvent: {
592
+ message: `Parsing document ${file.filename}: ${data.payload.process}%`,
593
+ type: "info"
594
+ }
595
+ });
596
+ if (data.payload.is_final) {
597
+ if (data.payload.error_message) {
598
+ subscriber.next({
599
+ type: EventType.RAW,
600
+ rawEvent: {
601
+ message: `Parsing document ${file.filename} failed: ${data.payload.error_message}`,
602
+ type: "error"
603
+ }
604
+ });
605
+ } else {
606
+ const fileNameNoExt = file.filename.split(".").splice(0, -1).join(".");
607
+ fileInfos.push({
608
+ docId: data.payload.doc_id,
609
+ fileName: fileNameNoExt,
610
+ fileType: extName,
611
+ fileSize: file.data?.length.toString() || "0",
612
+ fileUrl: `https://${cosData.Location}`
613
+ });
614
+ }
568
615
  }
616
+ break;
617
+ }
618
+ default: {
619
+ break;
569
620
  }
570
- break;
571
- }
572
- default: {
573
- break;
574
621
  }
575
622
  }
576
623
  }
577
624
  }
578
625
  }
579
- });
626
+ }
580
627
  }
581
628
  return {
582
- messages: newMessages,
629
+ message: newMessage,
583
630
  fileInfos,
584
631
  failedFiles
585
632
  };