@ynhcj/xiaoyi 2.2.2 → 2.2.3
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/channel.js +162 -60
- package/package.json +1 -1
package/dist/channel.js
CHANGED
|
@@ -3,6 +3,21 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
exports.xiaoyiPlugin = void 0;
|
|
4
4
|
const runtime_1 = require("./runtime");
|
|
5
5
|
const file_handler_1 = require("./file-handler");
|
|
6
|
+
// Import agent event functions - will be resolved at runtime
|
|
7
|
+
// Use dynamic require for compatibility
|
|
8
|
+
let onAgentEvent = null;
|
|
9
|
+
let registerAgentRunContext = null;
|
|
10
|
+
try {
|
|
11
|
+
// Try to import from the built openclaw package
|
|
12
|
+
const agentEvents = require("openclaw/dist/infra/agent-events");
|
|
13
|
+
onAgentEvent = agentEvents.onAgentEvent;
|
|
14
|
+
registerAgentRunContext = agentEvents.registerAgentRunContext;
|
|
15
|
+
console.log("XiaoYi: [AGENT EVENT] Successfully imported agent event functions");
|
|
16
|
+
}
|
|
17
|
+
catch (error) {
|
|
18
|
+
console.warn("XiaoYi: [AGENT EVENT] Could not import agent event functions:", error);
|
|
19
|
+
console.warn("XiaoYi: [AGENT EVENT] Streaming will be disabled");
|
|
20
|
+
}
|
|
6
21
|
/**
|
|
7
22
|
* XiaoYi Channel Plugin
|
|
8
23
|
* Implements OpenClaw ChannelPlugin interface for XiaoYi A2A protocol
|
|
@@ -307,15 +322,18 @@ exports.xiaoyiPlugin = {
|
|
|
307
322
|
SenderId: senderId,
|
|
308
323
|
OriginatingChannel: "xiaoyi",
|
|
309
324
|
};
|
|
310
|
-
// Use the correct API to dispatch the message (streaming
|
|
325
|
+
// Use the correct API to dispatch the message (streaming via onAgentEvent)
|
|
311
326
|
try {
|
|
312
327
|
const streamingEnabled = resolvedAccount.config.enableStreaming === true;
|
|
328
|
+
const hasAgentEventSupport = typeof onAgentEvent === "function";
|
|
313
329
|
console.log("\n" + "=".repeat(60));
|
|
314
330
|
console.log(`XiaoYi: [STREAMING] Starting message processing`);
|
|
315
331
|
console.log(` Session: ${message.sessionId}`);
|
|
316
332
|
console.log(` Task ID: ${message.params.id}`);
|
|
317
333
|
console.log(` User input: ${bodyText.substring(0, 50)}${bodyText.length > 50 ? "..." : ""}`);
|
|
318
|
-
console.log(` Streaming: ${streamingEnabled ? "ENABLED" : "DISABLED
|
|
334
|
+
console.log(` Config Streaming: ${streamingEnabled ? "ENABLED" : "DISABLED"}`);
|
|
335
|
+
console.log(` Agent Event Support: ${hasAgentEventSupport ? "AVAILABLE" : "NOT AVAILABLE"}`);
|
|
336
|
+
console.log(` Effective Streaming: ${streamingEnabled && hasAgentEventSupport ? "ENABLED (via onAgentEvent)" : "DISABLED"}`);
|
|
319
337
|
console.log("=".repeat(60) + "\n");
|
|
320
338
|
// Track response state for streaming
|
|
321
339
|
let accumulatedText = "";
|
|
@@ -323,23 +341,147 @@ exports.xiaoyiPlugin = {
|
|
|
323
341
|
let frameCount = 0;
|
|
324
342
|
let partialCount = 0;
|
|
325
343
|
const startTime = Date.now();
|
|
344
|
+
// Register agent event listener for streaming (NEW METHOD)
|
|
345
|
+
let unsubscribeAgentEvents;
|
|
346
|
+
let runId;
|
|
347
|
+
let isCompleted = false;
|
|
348
|
+
if (streamingEnabled && hasAgentEventSupport) {
|
|
349
|
+
// Subscribe to agent events BEFORE dispatching the message
|
|
350
|
+
unsubscribeAgentEvents = onAgentEvent((evt) => {
|
|
351
|
+
if (isCompleted)
|
|
352
|
+
return;
|
|
353
|
+
// Get runId from first event
|
|
354
|
+
if (!runId && evt.runId) {
|
|
355
|
+
runId = evt.runId;
|
|
356
|
+
console.log(`XiaoYi: [AGENT EVENT] Registered runId: ${runId}`);
|
|
357
|
+
}
|
|
358
|
+
// Only process events for this run
|
|
359
|
+
if (runId && evt.runId !== runId)
|
|
360
|
+
return;
|
|
361
|
+
const elapsed = Date.now() - startTime;
|
|
362
|
+
// Handle streaming text events
|
|
363
|
+
if (evt.stream === "assistant" && typeof evt.data?.text === "string") {
|
|
364
|
+
const newText = evt.data.text;
|
|
365
|
+
if (!newText)
|
|
366
|
+
return;
|
|
367
|
+
// Calculate delta text
|
|
368
|
+
const previousLength = accumulatedText.length;
|
|
369
|
+
accumulatedText = newText;
|
|
370
|
+
const deltaText = newText.slice(previousLength);
|
|
371
|
+
console.log(`\n>>> XiaoYi: [AGENT EVENT] Stream frame #${++partialCount} at ${elapsed}ms`);
|
|
372
|
+
console.log(` Run ID: ${evt.runId}`);
|
|
373
|
+
console.log(` Delta length: ${deltaText.length} chars`);
|
|
374
|
+
console.log(` Total length: ${newText.length} chars`);
|
|
375
|
+
console.log(` Delta content: "${deltaText}"`);
|
|
376
|
+
// Send PARTIAL frame with delta content
|
|
377
|
+
const partialResponse = {
|
|
378
|
+
sessionId: message.sessionId,
|
|
379
|
+
messageId: `partial_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`,
|
|
380
|
+
timestamp: Date.now(),
|
|
381
|
+
agentId: resolvedAccount.config.agentId,
|
|
382
|
+
sender: {
|
|
383
|
+
id: resolvedAccount.config.agentId,
|
|
384
|
+
name: "OpenClaw Agent",
|
|
385
|
+
type: "agent",
|
|
386
|
+
},
|
|
387
|
+
content: {
|
|
388
|
+
type: "text",
|
|
389
|
+
text: deltaText || newText,
|
|
390
|
+
},
|
|
391
|
+
status: "processing",
|
|
392
|
+
};
|
|
393
|
+
const conn = runtime.getConnection();
|
|
394
|
+
if (conn) {
|
|
395
|
+
conn.sendResponse(partialResponse, taskId, message.sessionId, false, true)
|
|
396
|
+
.then(() => {
|
|
397
|
+
console.log(` ✓ SENT (isFinal=false, append=true)\n`);
|
|
398
|
+
})
|
|
399
|
+
.catch((err) => {
|
|
400
|
+
console.error(` ✗ Send failed: ${err}\n`);
|
|
401
|
+
});
|
|
402
|
+
}
|
|
403
|
+
}
|
|
404
|
+
// Handle lifecycle events (completion/error)
|
|
405
|
+
if (evt.stream === "lifecycle") {
|
|
406
|
+
const phase = evt.data?.phase;
|
|
407
|
+
if (phase === "end") {
|
|
408
|
+
isCompleted = true;
|
|
409
|
+
console.log("\n" + "-".repeat(60));
|
|
410
|
+
console.log(`XiaoYi: [AGENT EVENT] Lifecycle phase: end`);
|
|
411
|
+
console.log(` Run ID: ${evt.runId}`);
|
|
412
|
+
console.log(` Elapsed: ${elapsed}ms`);
|
|
413
|
+
console.log(` Total frames: ${partialCount}`);
|
|
414
|
+
console.log(` Final length: ${accumulatedText.length} chars`);
|
|
415
|
+
console.log("-".repeat(60) + "\n");
|
|
416
|
+
// Send FINAL frame
|
|
417
|
+
const response = {
|
|
418
|
+
sessionId: message.sessionId,
|
|
419
|
+
messageId: `msg_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`,
|
|
420
|
+
timestamp: Date.now(),
|
|
421
|
+
agentId: resolvedAccount.config.agentId,
|
|
422
|
+
sender: {
|
|
423
|
+
id: resolvedAccount.config.agentId,
|
|
424
|
+
name: "OpenClaw Agent",
|
|
425
|
+
type: "agent",
|
|
426
|
+
},
|
|
427
|
+
content: {
|
|
428
|
+
type: "text",
|
|
429
|
+
text: accumulatedText,
|
|
430
|
+
},
|
|
431
|
+
status: "success",
|
|
432
|
+
};
|
|
433
|
+
const conn = runtime.getConnection();
|
|
434
|
+
if (conn) {
|
|
435
|
+
conn.sendResponse(response, taskId, message.sessionId, true, false)
|
|
436
|
+
.then(() => {
|
|
437
|
+
console.log(`✓ XiaoYi: FINAL frame sent (isFinal=true, append=false)\n`);
|
|
438
|
+
})
|
|
439
|
+
.catch((err) => {
|
|
440
|
+
console.error(`✗ Final send failed: ${err}\n`);
|
|
441
|
+
});
|
|
442
|
+
}
|
|
443
|
+
// Unsubscribe from events
|
|
444
|
+
if (unsubscribeAgentEvents) {
|
|
445
|
+
unsubscribeAgentEvents();
|
|
446
|
+
unsubscribeAgentEvents = undefined;
|
|
447
|
+
}
|
|
448
|
+
}
|
|
449
|
+
else if (phase === "error") {
|
|
450
|
+
isCompleted = true;
|
|
451
|
+
console.error(`XiaoYi: [AGENT EVENT] Error phase:`, evt.data?.error);
|
|
452
|
+
// Unsubscribe from events
|
|
453
|
+
if (unsubscribeAgentEvents) {
|
|
454
|
+
unsubscribeAgentEvents();
|
|
455
|
+
unsubscribeAgentEvents = undefined;
|
|
456
|
+
}
|
|
457
|
+
}
|
|
458
|
+
}
|
|
459
|
+
});
|
|
460
|
+
console.log(`XiaoYi: [AGENT EVENT] Listener registered for streaming`);
|
|
461
|
+
}
|
|
462
|
+
else if (streamingEnabled && !hasAgentEventSupport) {
|
|
463
|
+
console.warn(`XiaoYi: [WARN] Streaming enabled but onAgentEvent not available, falling back to non-streaming mode`);
|
|
464
|
+
}
|
|
326
465
|
await pluginRuntime.channel.reply.dispatchReplyWithBufferedBlockDispatcher({
|
|
327
466
|
ctx: msgContext,
|
|
328
467
|
cfg: config,
|
|
329
468
|
dispatcherOptions: {
|
|
330
469
|
deliver: async (payload) => {
|
|
470
|
+
// This is the fallback for when streaming is disabled
|
|
471
|
+
if (streamingEnabled) {
|
|
472
|
+
// Streaming mode: deliver is handled by onAgentEvent
|
|
473
|
+
console.log(`XiaoYi: [DELIVER] Skipping (handled by onAgentEvent)`);
|
|
474
|
+
return;
|
|
475
|
+
}
|
|
331
476
|
const elapsed = Date.now() - startTime;
|
|
332
477
|
const completeText = payload.text || "";
|
|
333
478
|
accumulatedText = completeText;
|
|
334
479
|
console.log("\n" + "-".repeat(60));
|
|
335
|
-
console.log(`XiaoYi: [
|
|
480
|
+
console.log(`XiaoYi: [DELIVER] Non-streaming mode`);
|
|
336
481
|
console.log(` Elapsed: ${elapsed}ms`);
|
|
337
|
-
console.log(`
|
|
338
|
-
console.log(` Total length: ${completeText.length} chars`);
|
|
339
|
-
console.log(` Partial frames sent: ${partialCount}`);
|
|
340
|
-
console.log(` Content preview: ${completeText.substring(0, 100)}...`);
|
|
482
|
+
console.log(` Length: ${completeText.length} chars`);
|
|
341
483
|
console.log("-".repeat(60) + "\n");
|
|
342
|
-
// Send
|
|
484
|
+
// Send final frame
|
|
343
485
|
const response = {
|
|
344
486
|
sessionId: message.sessionId,
|
|
345
487
|
messageId: `msg_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`,
|
|
@@ -352,74 +494,34 @@ exports.xiaoyiPlugin = {
|
|
|
352
494
|
},
|
|
353
495
|
content: {
|
|
354
496
|
type: "text",
|
|
355
|
-
text: accumulatedText,
|
|
497
|
+
text: accumulatedText,
|
|
356
498
|
},
|
|
357
499
|
status: "success",
|
|
358
500
|
};
|
|
359
501
|
const conn = runtime.getConnection();
|
|
360
502
|
if (conn) {
|
|
361
|
-
// Use append=false for final frame (complete content)
|
|
362
503
|
await conn.sendResponse(response, taskId, message.sessionId, true, false);
|
|
363
|
-
console.log(`✓ XiaoYi: FINAL frame sent (
|
|
504
|
+
console.log(`✓ XiaoYi: FINAL frame sent (non-streaming)\n`);
|
|
364
505
|
}
|
|
365
506
|
},
|
|
366
507
|
onIdle: async () => {
|
|
367
508
|
const elapsed = Date.now() - startTime;
|
|
368
509
|
console.log("\n" + "=".repeat(60));
|
|
369
|
-
console.log(`XiaoYi: [
|
|
510
|
+
console.log(`XiaoYi: [IDLE] Processing complete`);
|
|
370
511
|
console.log(` Total time: ${elapsed}ms`);
|
|
371
|
-
console.log(`
|
|
372
|
-
console.log(` Partial frames: ${partialCount}`);
|
|
512
|
+
console.log(` Streaming frames: ${partialCount}`);
|
|
373
513
|
console.log(` Final length: ${accumulatedText.length} chars`);
|
|
374
514
|
console.log("=".repeat(60) + "\n");
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
onPartialReply: async (payload) => {
|
|
381
|
-
const elapsed = Date.now() - startTime;
|
|
382
|
-
const newText = payload.text || "";
|
|
383
|
-
if (!newText) {
|
|
384
|
-
console.log(`XiaoYi: [STREAMING] Skipping empty partial response at ${elapsed}ms`);
|
|
385
|
-
return;
|
|
386
|
-
}
|
|
387
|
-
// Calculate delta text (增量部分)
|
|
388
|
-
const previousLength = accumulatedText.length;
|
|
389
|
-
accumulatedText = newText;
|
|
390
|
-
const deltaText = newText.slice(previousLength);
|
|
391
|
-
console.log(`\n>>> XiaoYi: [PARTIAL] Frame #${++partialCount} at ${elapsed}ms`);
|
|
392
|
-
console.log(` Previous length: ${previousLength}`);
|
|
393
|
-
console.log(` New length: ${newText.length}`);
|
|
394
|
-
console.log(` Delta length: ${deltaText.length}`);
|
|
395
|
-
console.log(` Delta content: "${deltaText}"`);
|
|
396
|
-
console.log(` Accumulated so far: "${newText}"`);
|
|
397
|
-
// Send PARTIAL frame with delta content
|
|
398
|
-
const partialResponse = {
|
|
399
|
-
sessionId: message.sessionId,
|
|
400
|
-
messageId: `partial_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`,
|
|
401
|
-
timestamp: Date.now(),
|
|
402
|
-
agentId: resolvedAccount.config.agentId,
|
|
403
|
-
sender: {
|
|
404
|
-
id: resolvedAccount.config.agentId,
|
|
405
|
-
name: "OpenClaw Agent",
|
|
406
|
-
type: "agent",
|
|
407
|
-
},
|
|
408
|
-
content: {
|
|
409
|
-
type: "text",
|
|
410
|
-
text: deltaText || newText, // Send delta only (or full if delta is empty)
|
|
411
|
-
},
|
|
412
|
-
status: "processing", // Mark as processing
|
|
413
|
-
};
|
|
414
|
-
const conn = runtime.getConnection();
|
|
415
|
-
if (conn) {
|
|
416
|
-
// Use append=true for streaming frames (服务端会追加)
|
|
417
|
-
await conn.sendResponse(partialResponse, taskId, message.sessionId, false, true);
|
|
418
|
-
console.log(` ✓ SENT (isFinal=false, append=true)\n`);
|
|
515
|
+
// Cleanup event listener if still active
|
|
516
|
+
if (unsubscribeAgentEvents && !isCompleted) {
|
|
517
|
+
console.log(`XiaoYi: [IDLE] Cleaning up event listener`);
|
|
518
|
+
unsubscribeAgentEvents();
|
|
519
|
+
unsubscribeAgentEvents = undefined;
|
|
419
520
|
}
|
|
420
521
|
},
|
|
421
|
-
}
|
|
422
|
-
|
|
522
|
+
},
|
|
523
|
+
replyOptions: undefined, // NOT using onPartialReply anymore
|
|
524
|
+
images: images.length > 0 ? images : undefined,
|
|
423
525
|
});
|
|
424
526
|
}
|
|
425
527
|
catch (error) {
|