mirror of
https://github.com/rowboatlabs/rowboat.git
synced 2026-06-06 19:35:44 +02:00
code block on chat
This commit is contained in:
parent
bc929b6c1b
commit
4fb153f5dc
7 changed files with 59 additions and 4 deletions
|
|
@ -1972,6 +1972,21 @@ function App() {
|
|||
break
|
||||
}
|
||||
|
||||
case 'tool-output-stream':
|
||||
{
|
||||
if (!isActiveRun) return
|
||||
setConversation(prev => prev.map(item => {
|
||||
if (
|
||||
isToolCall(item)
|
||||
&& item.id === event.toolCallId
|
||||
) {
|
||||
return { ...item, streamingOutput: (item.streamingOutput ?? '') + event.output }
|
||||
}
|
||||
return item
|
||||
}))
|
||||
break
|
||||
}
|
||||
|
||||
case 'tool-permission-request': {
|
||||
if (!isActiveRun) return
|
||||
const key = event.toolCall.toolCallId
|
||||
|
|
@ -3842,7 +3857,16 @@ function App() {
|
|||
/>
|
||||
<ToolContent>
|
||||
<ToolInput input={input} />
|
||||
{output !== null ? (
|
||||
{item.streamingOutput && item.status === 'running' ? (
|
||||
<div className="space-y-2 p-4">
|
||||
<h4 className="font-medium text-muted-foreground text-xs uppercase tracking-wide">
|
||||
Live Output
|
||||
</h4>
|
||||
<pre className="max-h-80 overflow-auto rounded-md border bg-zinc-950 p-4 font-mono text-xs text-green-400 whitespace-pre-wrap">
|
||||
{item.streamingOutput}
|
||||
</pre>
|
||||
</div>
|
||||
) : output !== null ? (
|
||||
<ToolOutput output={output} errorText={errorText} />
|
||||
) : null}
|
||||
</ToolContent>
|
||||
|
|
|
|||
|
|
@ -23,6 +23,7 @@ export interface ToolCall {
|
|||
name: string
|
||||
input: ToolUIPart['input']
|
||||
result?: ToolUIPart['output']
|
||||
streamingOutput?: string
|
||||
status: 'pending' | 'running' | 'completed' | 'error'
|
||||
timestamp: number
|
||||
}
|
||||
|
|
|
|||
|
|
@ -161,6 +161,7 @@ export class AgentRuntime implements IAgentRuntime {
|
|||
modelConfigRepo: this.modelConfigRepo,
|
||||
signal,
|
||||
abortRegistry: this.abortRegistry,
|
||||
bus: this.bus,
|
||||
})) {
|
||||
eventCount++;
|
||||
if (event.type !== "llm-stream-event") {
|
||||
|
|
@ -822,6 +823,7 @@ export async function* streamAgent({
|
|||
modelConfigRepo,
|
||||
signal,
|
||||
abortRegistry,
|
||||
bus,
|
||||
}: {
|
||||
state: AgentState,
|
||||
idGenerator: IMonotonicallyIncreasingIdGenerator;
|
||||
|
|
@ -830,6 +832,7 @@ export async function* streamAgent({
|
|||
modelConfigRepo: IModelConfigRepo;
|
||||
signal: AbortSignal;
|
||||
abortRegistry: IAbortRegistry;
|
||||
bus: IBus;
|
||||
}): AsyncGenerator<z.infer<typeof RunEvent>, void, unknown> {
|
||||
const logger = new PrefixLogger(`run-${runId}-${state.agentName}`);
|
||||
|
||||
|
|
@ -942,6 +945,7 @@ export async function* streamAgent({
|
|||
modelConfigRepo,
|
||||
signal,
|
||||
abortRegistry,
|
||||
bus,
|
||||
})) {
|
||||
yield* processEvent({
|
||||
...event,
|
||||
|
|
@ -952,7 +956,7 @@ export async function* streamAgent({
|
|||
result = subflowState.finalResponse();
|
||||
}
|
||||
} else {
|
||||
result = await execTool(agent.tools![toolCall.toolName], toolCall.arguments, { runId, signal, abortRegistry });
|
||||
result = await execTool(agent.tools![toolCall.toolName], toolCall.arguments, { runId, toolCallId, signal, abortRegistry, publish: (event) => bus.publish(event) });
|
||||
}
|
||||
const resultPayload = result === undefined ? null : result;
|
||||
const resultMsg: z.infer<typeof ToolMessage> = {
|
||||
|
|
|
|||
|
|
@ -847,6 +847,16 @@ export const BuiltinTools: z.infer<typeof BuiltinToolsSchema> = {
|
|||
const { promise, process: proc } = executeCommandAbortable(command, {
|
||||
cwd: workingDir,
|
||||
signal: ctx.signal,
|
||||
onData: ctx.publish ? (chunk: string) => {
|
||||
ctx.publish({
|
||||
runId: ctx.runId,
|
||||
type: "tool-output-stream",
|
||||
toolCallId: ctx.toolCallId,
|
||||
toolName: "executeCommand",
|
||||
output: chunk,
|
||||
subflow: [],
|
||||
});
|
||||
} : undefined,
|
||||
});
|
||||
|
||||
// Register process with abort registry for force-kill
|
||||
|
|
|
|||
|
|
@ -143,6 +143,7 @@ export function executeCommandAbortable(
|
|||
timeout?: number;
|
||||
maxBuffer?: number;
|
||||
signal?: AbortSignal;
|
||||
onData?: (chunk: string) => void;
|
||||
}
|
||||
): { promise: Promise<AbortableCommandResult>; process: ChildProcess } {
|
||||
// Check if already aborted before spawning
|
||||
|
|
@ -176,16 +177,20 @@ export function executeCommandAbortable(
|
|||
|
||||
// Collect output
|
||||
proc.stdout?.on('data', (chunk: Buffer) => {
|
||||
const text = chunk.toString();
|
||||
const maxBuffer = options?.maxBuffer || 1024 * 1024;
|
||||
if (stdout.length < maxBuffer) {
|
||||
stdout += chunk.toString();
|
||||
stdout += text;
|
||||
}
|
||||
options?.onData?.(text);
|
||||
});
|
||||
proc.stderr?.on('data', (chunk: Buffer) => {
|
||||
const text = chunk.toString();
|
||||
const maxBuffer = options?.maxBuffer || 1024 * 1024;
|
||||
if (stderr.length < maxBuffer) {
|
||||
stderr += chunk.toString();
|
||||
stderr += text;
|
||||
}
|
||||
options?.onData?.(text);
|
||||
});
|
||||
|
||||
// Abort handler
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
import { ToolAttachment } from "@x/shared/dist/agent.js";
|
||||
import { RunEvent } from "@x/shared/dist/runs.js";
|
||||
import { z } from "zod";
|
||||
import { BuiltinTools } from "./builtin-tools.js";
|
||||
import { executeTool } from "../../mcp/mcp.js";
|
||||
|
|
@ -9,8 +10,10 @@ import { IAbortRegistry } from "../../runs/abort-registry.js";
|
|||
*/
|
||||
export interface ToolContext {
|
||||
runId: string;
|
||||
toolCallId: string;
|
||||
signal: AbortSignal;
|
||||
abortRegistry: IAbortRegistry;
|
||||
publish: (event: z.infer<typeof RunEvent>) => Promise<void>;
|
||||
}
|
||||
|
||||
async function execMcpTool(agentTool: z.infer<typeof ToolAttachment> & { type: "mcp" }, input: Record<string, unknown>): Promise<unknown> {
|
||||
|
|
|
|||
|
|
@ -81,6 +81,13 @@ export const RunErrorEvent = BaseRunEvent.extend({
|
|||
error: z.string(),
|
||||
});
|
||||
|
||||
export const ToolOutputStreamEvent = BaseRunEvent.extend({
|
||||
type: z.literal("tool-output-stream"),
|
||||
toolCallId: z.string(),
|
||||
toolName: z.string(),
|
||||
output: z.string(),
|
||||
});
|
||||
|
||||
export const RunStoppedEvent = BaseRunEvent.extend({
|
||||
type: z.literal("run-stopped"),
|
||||
reason: z.enum(["user-requested", "force-stopped"]).optional(),
|
||||
|
|
@ -95,6 +102,7 @@ export const RunEvent = z.union([
|
|||
MessageEvent,
|
||||
ToolInvocationEvent,
|
||||
ToolResultEvent,
|
||||
ToolOutputStreamEvent,
|
||||
AskHumanRequestEvent,
|
||||
AskHumanResponseEvent,
|
||||
ToolPermissionRequestEvent,
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue