mirror of
https://github.com/badlogic/pi-mono.git
synced 2026-11-05 23:37:25 +00:00
feat(coding-agent): expose output padding to custom renderers (#7045)
Co-authored-by: Mario Zechner <badlogicgames@gmail.com>
This commit is contained in:
parent
2a2b0a39f0
commit
518855dd50
7 changed files with 78 additions and 7 deletions
|
|
@ -17,6 +17,8 @@
|
|||
|
||||
### Added
|
||||
|
||||
- Exposed `PI_SESSION_ID`, `PI_SESSION_FILE`, `PI_PROVIDER`, `PI_MODEL`, and `PI_REASONING_LEVEL` to commands run by built-in and factory-created bash tools.
|
||||
- Exposed the `outputPad` setting to custom message renderers.
|
||||
- Added inherited `Tool.constrainedSampling` with strict JSON Schema (`prefer`/`require`) and OpenAI Lark/regex grammar variants across OpenAI, Anthropic, Amazon Bedrock, Google Gemini, and Mistral. See [Constrained Sampling for Tools](../ai/README.md#constrained-sampling-for-tools).
|
||||
- Added inherited `supportsGrammarTools` and `supportsStrictTools` compatibility flags, expanded `supportsStrictMode` coverage, and generated model capability metadata to gate constrained sampling.
|
||||
- Added inherited Kimi Code subscription OAuth login for the Kimi For Coding provider, including device authorization and automatic token refresh ([#6935](https://github.com/earendil-works/pi/pull/6935) by [@zaycruz](https://github.com/zaycruz)).
|
||||
|
|
|
|||
|
|
@ -2791,7 +2791,7 @@ Register a custom renderer for messages with your `customType`. Use message rend
|
|||
import { Text } from "@earendil-works/pi-tui";
|
||||
|
||||
pi.registerMessageRenderer("my-extension", (message, options, theme) => {
|
||||
const { expanded } = options;
|
||||
const { expanded, outputPad } = options;
|
||||
let text = theme.fg("accent", `[${message.customType}] `);
|
||||
text += message.content;
|
||||
|
||||
|
|
@ -2799,7 +2799,7 @@ pi.registerMessageRenderer("my-extension", (message, options, theme) => {
|
|||
text += "\n" + theme.fg("dim", JSON.stringify(message.details, null, 2));
|
||||
}
|
||||
|
||||
return new Text(text, 0, 0);
|
||||
return new Text(text, outputPad, 0);
|
||||
});
|
||||
```
|
||||
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@ import { Box, Text } from "@earendil-works/pi-tui";
|
|||
|
||||
export default function (pi: ExtensionAPI) {
|
||||
// Register custom renderer for "status-update" messages
|
||||
pi.registerMessageRenderer("status-update", (message, { expanded }, theme) => {
|
||||
pi.registerMessageRenderer("status-update", (message, { expanded, outputPad }, theme) => {
|
||||
const details = message.details as { level: string; timestamp: number } | undefined;
|
||||
const level = details?.level ?? "info";
|
||||
|
||||
|
|
@ -29,7 +29,7 @@ export default function (pi: ExtensionAPI) {
|
|||
}
|
||||
|
||||
// Use Box with customMessageBg for consistent styling
|
||||
const box = new Box(1, 1, (t) => theme.bg("customMessageBg", t));
|
||||
const box = new Box(outputPad, 1, (t) => theme.bg("customMessageBg", t));
|
||||
box.addChild(new Text(text, 0, 0));
|
||||
return box;
|
||||
});
|
||||
|
|
|
|||
|
|
@ -1129,6 +1129,8 @@ export interface SessionBeforeTreeResult {
|
|||
|
||||
export interface MessageRenderOptions {
|
||||
expanded: boolean;
|
||||
/** Horizontal padding configured by the outputPad setting. */
|
||||
outputPad: number;
|
||||
}
|
||||
|
||||
export interface EntryRenderOptions {
|
||||
|
|
|
|||
|
|
@ -16,16 +16,19 @@ export class CustomMessageComponent extends Container {
|
|||
private customComponent?: Component;
|
||||
private markdownTheme: MarkdownTheme;
|
||||
private _expanded = false;
|
||||
private outputPad: number;
|
||||
|
||||
constructor(
|
||||
message: CustomMessage<unknown>,
|
||||
customRenderer?: MessageRenderer,
|
||||
markdownTheme: MarkdownTheme = getMarkdownTheme(),
|
||||
outputPad = 1,
|
||||
) {
|
||||
super();
|
||||
this.message = message;
|
||||
this.customRenderer = customRenderer;
|
||||
this.markdownTheme = markdownTheme;
|
||||
this.outputPad = outputPad;
|
||||
|
||||
this.addChild(new Spacer(1));
|
||||
|
||||
|
|
@ -42,6 +45,13 @@ export class CustomMessageComponent extends Container {
|
|||
}
|
||||
}
|
||||
|
||||
setOutputPad(outputPad: number): void {
|
||||
if (this.outputPad !== outputPad) {
|
||||
this.outputPad = outputPad;
|
||||
this.rebuild();
|
||||
}
|
||||
}
|
||||
|
||||
override invalidate(): void {
|
||||
super.invalidate();
|
||||
this.rebuild();
|
||||
|
|
@ -58,7 +68,11 @@ export class CustomMessageComponent extends Container {
|
|||
// Try custom renderer first - it handles its own styling
|
||||
if (this.customRenderer) {
|
||||
try {
|
||||
const component = this.customRenderer(this.message, { expanded: this._expanded }, theme);
|
||||
const component = this.customRenderer(
|
||||
this.message,
|
||||
{ expanded: this._expanded, outputPad: this.outputPad },
|
||||
theme,
|
||||
);
|
||||
if (component) {
|
||||
// Custom renderer provides its own styled component
|
||||
this.customComponent = component;
|
||||
|
|
|
|||
|
|
@ -3242,7 +3242,12 @@ export class InteractiveMode {
|
|||
case "custom": {
|
||||
if (message.display) {
|
||||
const renderer = this.session.extensionRunner.getMessageRenderer(message.customType);
|
||||
const component = new CustomMessageComponent(message, renderer, this.getMarkdownThemeWithSettings());
|
||||
const component = new CustomMessageComponent(
|
||||
message,
|
||||
renderer,
|
||||
this.getMarkdownThemeWithSettings(),
|
||||
this.outputPad,
|
||||
);
|
||||
component.setExpanded(this.toolOutputExpanded);
|
||||
this.chatContainer.addChild(component);
|
||||
}
|
||||
|
|
@ -4257,7 +4262,11 @@ export class InteractiveMode {
|
|||
this.outputPad = padding;
|
||||
if (this.streamingComponent || this.session.isStreaming) {
|
||||
for (const child of this.chatContainer.children) {
|
||||
if (child instanceof AssistantMessageComponent || child instanceof UserMessageComponent) {
|
||||
if (
|
||||
child instanceof AssistantMessageComponent ||
|
||||
child instanceof CustomMessageComponent ||
|
||||
child instanceof UserMessageComponent
|
||||
) {
|
||||
child.setOutputPad(padding);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
44
packages/coding-agent/test/custom-message.test.ts
Normal file
44
packages/coding-agent/test/custom-message.test.ts
Normal file
|
|
@ -0,0 +1,44 @@
|
|||
import { Text } from "@earendil-works/pi-tui";
|
||||
import { describe, expect, test } from "vitest";
|
||||
import type { MessageRenderer, MessageRenderOptions } from "../src/core/extensions/types.ts";
|
||||
import type { CustomMessage } from "../src/core/messages.ts";
|
||||
import { CustomMessageComponent } from "../src/modes/interactive/components/custom-message.ts";
|
||||
import { initTheme } from "../src/modes/interactive/theme/theme.ts";
|
||||
import { stripAnsi } from "../src/utils/ansi.ts";
|
||||
|
||||
describe("CustomMessageComponent", () => {
|
||||
test("provides output padding to custom renderers and updates it", () => {
|
||||
initTheme("dark");
|
||||
const optionsSeen: MessageRenderOptions[] = [];
|
||||
const renderer: MessageRenderer = (_message, options) => {
|
||||
optionsSeen.push(options);
|
||||
return new Text("custom", options.outputPad, 0);
|
||||
};
|
||||
const message: CustomMessage = {
|
||||
role: "custom",
|
||||
customType: "test",
|
||||
content: "custom",
|
||||
display: true,
|
||||
timestamp: Date.now(),
|
||||
};
|
||||
const component = new CustomMessageComponent(message, renderer, undefined, 1);
|
||||
|
||||
expect(optionsSeen).toEqual([{ expanded: false, outputPad: 1 }]);
|
||||
expect(
|
||||
component
|
||||
.render(40)
|
||||
.map(stripAnsi)
|
||||
.some((line) => line.startsWith(" custom")),
|
||||
).toBe(true);
|
||||
|
||||
component.setOutputPad(0);
|
||||
|
||||
expect(optionsSeen.at(-1)).toEqual({ expanded: false, outputPad: 0 });
|
||||
expect(
|
||||
component
|
||||
.render(40)
|
||||
.map(stripAnsi)
|
||||
.some((line) => line.startsWith("custom")),
|
||||
).toBe(true);
|
||||
});
|
||||
});
|
||||
Loading…
Add table
Add a link
Reference in a new issue