@oh-my-pi/pi-agent-core 14.7.2 → 14.7.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/package.json +4 -4
- package/src/agent-loop.ts +80 -95
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"type": "module",
|
|
3
3
|
"name": "@oh-my-pi/pi-agent-core",
|
|
4
|
-
"version": "14.7.
|
|
4
|
+
"version": "14.7.3",
|
|
5
5
|
"description": "General-purpose agent with transport abstraction, state management, and attachment support",
|
|
6
6
|
"homepage": "https://github.com/can1357/oh-my-pi",
|
|
7
7
|
"author": "Can Boluk",
|
|
@@ -35,9 +35,9 @@
|
|
|
35
35
|
"fmt": "biome format --write ."
|
|
36
36
|
},
|
|
37
37
|
"dependencies": {
|
|
38
|
-
"@oh-my-pi/pi-ai": "14.7.
|
|
39
|
-
"@oh-my-pi/pi-natives": "14.7.
|
|
40
|
-
"@oh-my-pi/pi-utils": "14.7.
|
|
38
|
+
"@oh-my-pi/pi-ai": "14.7.3",
|
|
39
|
+
"@oh-my-pi/pi-natives": "14.7.3",
|
|
40
|
+
"@oh-my-pi/pi-utils": "14.7.3"
|
|
41
41
|
},
|
|
42
42
|
"devDependencies": {
|
|
43
43
|
"@sinclair/typebox": "^0.34.49",
|
package/src/agent-loop.ts
CHANGED
|
@@ -22,6 +22,9 @@ import type {
|
|
|
22
22
|
StreamFn,
|
|
23
23
|
} from "./types";
|
|
24
24
|
|
|
25
|
+
/** Sentinel returned by the abort race in `streamAssistantResponse`. */
|
|
26
|
+
const ABORTED: unique symbol = Symbol("agent-loop-aborted");
|
|
27
|
+
|
|
25
28
|
/**
|
|
26
29
|
* Start an agent loop with a new prompt message.
|
|
27
30
|
* The prompt is added to the context and events are emitted for it.
|
|
@@ -360,115 +363,97 @@ async function streamAssistantResponse(
|
|
|
360
363
|
let addedPartial = false;
|
|
361
364
|
|
|
362
365
|
const responseIterator = response[Symbol.asyncIterator]();
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
+
// Set up a single abort race: register the abort listener once for the whole
|
|
367
|
+
// stream and reuse the same race promise for every iterator.next() instead of
|
|
368
|
+
// allocating Promise.withResolvers and add/removeEventListener per event.
|
|
369
|
+
let abortRacePromise: Promise<typeof ABORTED> | undefined;
|
|
370
|
+
let detachAbortListener: (() => void) | undefined;
|
|
371
|
+
if (signal) {
|
|
372
|
+
if (signal.aborted) {
|
|
366
373
|
return emitAbortedAssistantMessage(partialMessage, addedPartial, context, config, stream);
|
|
367
374
|
}
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
}
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
375
|
+
const { promise, resolve } = Promise.withResolvers<typeof ABORTED>();
|
|
376
|
+
const onAbort = () => resolve(ABORTED);
|
|
377
|
+
signal.addEventListener("abort", onAbort, { once: true });
|
|
378
|
+
abortRacePromise = promise;
|
|
379
|
+
detachAbortListener = () => signal.removeEventListener("abort", onAbort);
|
|
380
|
+
}
|
|
374
381
|
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
382
|
+
try {
|
|
383
|
+
while (true) {
|
|
384
|
+
let next: IteratorResult<AssistantMessageEvent>;
|
|
385
|
+
if (abortRacePromise) {
|
|
386
|
+
const result = await Promise.race([responseIterator.next(), abortRacePromise]);
|
|
387
|
+
if (result === ABORTED) {
|
|
388
|
+
responseIterator.return?.()?.catch(() => {});
|
|
389
|
+
return emitAbortedAssistantMessage(partialMessage, addedPartial, context, config, stream);
|
|
390
|
+
}
|
|
391
|
+
next = result;
|
|
392
|
+
} else {
|
|
393
|
+
next = await responseIterator.next();
|
|
394
|
+
}
|
|
395
|
+
if (signal?.aborted) {
|
|
396
|
+
return emitAbortedAssistantMessage(partialMessage, addedPartial, context, config, stream);
|
|
397
|
+
}
|
|
398
|
+
if (next.done) break;
|
|
380
399
|
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
addedPartial = true;
|
|
386
|
-
stream.push({ type: "message_start", message: { ...partialMessage } });
|
|
387
|
-
break;
|
|
388
|
-
|
|
389
|
-
case "text_start":
|
|
390
|
-
case "text_delta":
|
|
391
|
-
case "text_end":
|
|
392
|
-
case "thinking_start":
|
|
393
|
-
case "thinking_delta":
|
|
394
|
-
case "thinking_end":
|
|
395
|
-
case "toolcall_start":
|
|
396
|
-
case "toolcall_delta":
|
|
397
|
-
case "toolcall_end":
|
|
398
|
-
if (partialMessage) {
|
|
400
|
+
const event = next.value;
|
|
401
|
+
|
|
402
|
+
switch (event.type) {
|
|
403
|
+
case "start":
|
|
399
404
|
partialMessage = event.partial;
|
|
400
|
-
context.messages
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
405
|
+
context.messages.push(partialMessage);
|
|
406
|
+
addedPartial = true;
|
|
407
|
+
stream.push({ type: "message_start", message: { ...partialMessage } });
|
|
408
|
+
break;
|
|
409
|
+
|
|
410
|
+
case "text_start":
|
|
411
|
+
case "text_delta":
|
|
412
|
+
case "text_end":
|
|
413
|
+
case "thinking_start":
|
|
414
|
+
case "thinking_delta":
|
|
415
|
+
case "thinking_end":
|
|
416
|
+
case "toolcall_start":
|
|
417
|
+
case "toolcall_delta":
|
|
418
|
+
case "toolcall_end":
|
|
419
|
+
if (partialMessage) {
|
|
420
|
+
partialMessage = event.partial;
|
|
421
|
+
context.messages[context.messages.length - 1] = partialMessage;
|
|
422
|
+
config.onAssistantMessageEvent?.(partialMessage, event);
|
|
423
|
+
if (signal?.aborted) {
|
|
424
|
+
continue;
|
|
425
|
+
}
|
|
426
|
+
stream.push({
|
|
427
|
+
type: "message_update",
|
|
428
|
+
assistantMessageEvent: event,
|
|
429
|
+
message: { ...partialMessage },
|
|
430
|
+
});
|
|
404
431
|
}
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
}
|
|
421
|
-
if (!addedPartial) {
|
|
422
|
-
stream.push({ type: "message_start", message: { ...finalMessage } });
|
|
432
|
+
break;
|
|
433
|
+
|
|
434
|
+
case "done":
|
|
435
|
+
case "error": {
|
|
436
|
+
const finalMessage = await response.result();
|
|
437
|
+
if (addedPartial) {
|
|
438
|
+
context.messages[context.messages.length - 1] = finalMessage;
|
|
439
|
+
} else {
|
|
440
|
+
context.messages.push(finalMessage);
|
|
441
|
+
}
|
|
442
|
+
if (!addedPartial) {
|
|
443
|
+
stream.push({ type: "message_start", message: { ...finalMessage } });
|
|
444
|
+
}
|
|
445
|
+
stream.push({ type: "message_end", message: finalMessage });
|
|
446
|
+
return finalMessage;
|
|
423
447
|
}
|
|
424
|
-
stream.push({ type: "message_end", message: finalMessage });
|
|
425
|
-
return finalMessage;
|
|
426
448
|
}
|
|
427
449
|
}
|
|
450
|
+
} finally {
|
|
451
|
+
detachAbortListener?.();
|
|
428
452
|
}
|
|
429
453
|
|
|
430
454
|
return await response.result();
|
|
431
455
|
}
|
|
432
456
|
|
|
433
|
-
type ResponseEventRead =
|
|
434
|
-
| { type: "event"; result: IteratorResult<AssistantMessageEvent> }
|
|
435
|
-
| { type: "error"; error: unknown }
|
|
436
|
-
| { type: "aborted" };
|
|
437
|
-
|
|
438
|
-
async function readResponseEvent(
|
|
439
|
-
iterator: AsyncIterator<AssistantMessageEvent>,
|
|
440
|
-
signal: AbortSignal | undefined,
|
|
441
|
-
): Promise<ResponseEventRead> {
|
|
442
|
-
if (!signal) {
|
|
443
|
-
return { type: "event", result: await iterator.next() };
|
|
444
|
-
}
|
|
445
|
-
if (signal.aborted) {
|
|
446
|
-
const returnPromise = iterator.return?.();
|
|
447
|
-
if (returnPromise) void returnPromise.catch(() => {});
|
|
448
|
-
return { type: "aborted" };
|
|
449
|
-
}
|
|
450
|
-
|
|
451
|
-
const { promise: abortPromise, resolve: resolveAbort } = Promise.withResolvers<ResponseEventRead>();
|
|
452
|
-
const onAbort = () => resolveAbort({ type: "aborted" });
|
|
453
|
-
signal.addEventListener("abort", onAbort, { once: true });
|
|
454
|
-
|
|
455
|
-
const eventPromise = iterator.next().then(
|
|
456
|
-
result => ({ type: "event" as const, result }),
|
|
457
|
-
error => ({ type: "error" as const, error }),
|
|
458
|
-
);
|
|
459
|
-
|
|
460
|
-
try {
|
|
461
|
-
const read = await Promise.race([eventPromise, abortPromise]);
|
|
462
|
-
if (read.type === "aborted") {
|
|
463
|
-
const returnPromise = iterator.return?.();
|
|
464
|
-
if (returnPromise) void returnPromise.catch(() => {});
|
|
465
|
-
}
|
|
466
|
-
return read;
|
|
467
|
-
} finally {
|
|
468
|
-
signal.removeEventListener("abort", onAbort);
|
|
469
|
-
}
|
|
470
|
-
}
|
|
471
|
-
|
|
472
457
|
function emitAbortedAssistantMessage(
|
|
473
458
|
partialMessage: AssistantMessage | null,
|
|
474
459
|
addedPartial: boolean,
|