mirror of
https://github.com/badlogic/pi-mono.git
synced 2026-11-05 23:37:25 +00:00
feat: expose provider stream events to extensions (#9901)
Add onProviderStreamEvent through pi-ai and agent core, and surface provider_stream_event to coding-agent extensions. Closes #9784
This commit is contained in:
parent
8d897edaa6
commit
002fc83852
36 changed files with 729 additions and 36 deletions
|
|
@ -113,6 +113,7 @@ cp permission-gate.ts ~/.pi/agent/extensions/
|
|||
|-----------|-------------|
|
||||
| `message-renderer.ts` | Custom message rendering with colors and expandable details via `registerMessageRenderer` |
|
||||
| `entry-renderer.ts` | TUI-only session entry rendering via `appendEntry` and `registerEntryRenderer` |
|
||||
| `debug-provider.ts` | Toggle raw provider stream capture with `/debug-provider` and inspect each assistant message in a TUI-only session entry |
|
||||
| `event-bus.ts` | Inter-extension communication via `pi.events` |
|
||||
|
||||
### Session Metadata
|
||||
|
|
|
|||
90
packages/coding-agent/examples/extensions/debug-provider.ts
Normal file
90
packages/coding-agent/examples/extensions/debug-provider.ts
Normal file
|
|
@ -0,0 +1,90 @@
|
|||
/**
|
||||
* Raw provider event viewer.
|
||||
*
|
||||
* Usage: /debug-provider [on|off]
|
||||
* With no argument, the command toggles capture. Captured events are persisted
|
||||
* as expandable custom entries.
|
||||
*/
|
||||
|
||||
import { type ExtensionAPI, keyHint } from "@earendil-works/pi-coding-agent";
|
||||
import { Box, Text } from "@earendil-works/pi-tui";
|
||||
|
||||
const ENTRY_TYPE = "debug-provider-events";
|
||||
const STATUS_KEY = "debug-provider";
|
||||
|
||||
interface ProviderDebugEntry {
|
||||
provider: string;
|
||||
api: string;
|
||||
model: string;
|
||||
events: unknown[];
|
||||
}
|
||||
|
||||
export default function (pi: ExtensionAPI) {
|
||||
let enabled = false;
|
||||
let activeEvents: unknown[] | undefined;
|
||||
let completedEntry: ProviderDebugEntry | undefined;
|
||||
|
||||
pi.registerEntryRenderer<ProviderDebugEntry>(ENTRY_TYPE, (entry, { expanded }, theme) => {
|
||||
const data = entry.data;
|
||||
if (!data) return new Text(theme.fg("warning", "[provider debug] Missing event data"), 0, 0);
|
||||
|
||||
const box = new Box(1, 1, (text) => theme.bg("customMessageBg", text));
|
||||
const count = `${data.events.length} event${data.events.length === 1 ? "" : "s"}`;
|
||||
const expandHint = expanded ? "" : ` (${keyHint("app.tools.expand", "to view events")})`;
|
||||
box.addChild(
|
||||
new Text(
|
||||
`${theme.fg("accent", "[provider debug]")} ${data.provider}/${data.model} (${data.api}) · ${count}${expandHint}`,
|
||||
0,
|
||||
0,
|
||||
),
|
||||
);
|
||||
if (expanded) {
|
||||
box.addChild(new Text(JSON.stringify(data.events, null, 2), 0, 0));
|
||||
}
|
||||
return box;
|
||||
});
|
||||
|
||||
pi.registerCommand("debug-provider", {
|
||||
description: "Toggle capture of raw provider stream events",
|
||||
handler: async (args, ctx) => {
|
||||
const requestedState = args.trim().toLowerCase();
|
||||
if (requestedState !== "" && requestedState !== "on" && requestedState !== "off") {
|
||||
ctx.ui.notify("Usage: /debug-provider [on|off]", "warning");
|
||||
return;
|
||||
}
|
||||
|
||||
enabled = requestedState === "" ? !enabled : requestedState === "on";
|
||||
if (!enabled) {
|
||||
activeEvents = undefined;
|
||||
completedEntry = undefined;
|
||||
}
|
||||
ctx.ui.setStatus(STATUS_KEY, enabled ? "provider debug" : undefined);
|
||||
ctx.ui.notify(`Provider event capture ${enabled ? "enabled" : "disabled"}`, "info");
|
||||
},
|
||||
});
|
||||
|
||||
pi.on("turn_start", () => {
|
||||
activeEvents = enabled ? [] : undefined;
|
||||
});
|
||||
|
||||
pi.on("provider_stream_event", (event) => {
|
||||
activeEvents?.push(structuredClone(event.data));
|
||||
});
|
||||
|
||||
pi.on("message_end", (event) => {
|
||||
if (event.message.role !== "assistant" || !activeEvents) return;
|
||||
completedEntry = {
|
||||
provider: event.message.provider,
|
||||
api: event.message.api,
|
||||
model: event.message.model,
|
||||
events: activeEvents,
|
||||
};
|
||||
activeEvents = undefined;
|
||||
});
|
||||
|
||||
pi.on("turn_end", () => {
|
||||
if (!completedEntry) return;
|
||||
pi.appendEntry<ProviderDebugEntry>(ENTRY_TYPE, completedEntry);
|
||||
completedEntry = undefined;
|
||||
});
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue