refactor(agent): split runner, tools, output, types
This commit is contained in:
parent
c0e3dabf25
commit
5fdae53687
7 changed files with 104 additions and 70 deletions
|
|
@ -1,5 +1,5 @@
|
|||
#!/usr/bin/env node
|
||||
import { Agent } from "./agent.js";
|
||||
import { Agent } from "./runner.js";
|
||||
|
||||
type CliOptions = {
|
||||
provider?: string;
|
||||
|
|
|
|||
|
|
@ -1 +1,2 @@
|
|||
export * from "./agent.js";
|
||||
export * from "./runner.js";
|
||||
export * from "./types.js";
|
||||
|
|
|
|||
|
|
@ -1,24 +1,14 @@
|
|||
import { Agent as PiAgentCore, type AgentEvent, type AgentMessage, type ThinkingLevel } from "@mariozechner/pi-agent-core";
|
||||
import { getModel } from "@mariozechner/pi-ai";
|
||||
import { createCodingTools } from "@mariozechner/pi-coding-agent";
|
||||
import type { AgentEvent, AgentMessage } from "@mariozechner/pi-agent-core";
|
||||
|
||||
export type AgentRunResult = {
|
||||
text: string;
|
||||
error?: string;
|
||||
export type AgentOutputState = {
|
||||
lastAssistantText: string;
|
||||
printedLen: number;
|
||||
streaming: boolean;
|
||||
};
|
||||
|
||||
export type AgentLogger = {
|
||||
stdout?: NodeJS.WritableStream;
|
||||
stderr?: NodeJS.WritableStream;
|
||||
};
|
||||
|
||||
export type AgentOptions = {
|
||||
provider?: string;
|
||||
model?: string;
|
||||
systemPrompt?: string;
|
||||
thinkingLevel?: ThinkingLevel;
|
||||
cwd?: string;
|
||||
logger?: AgentLogger;
|
||||
export type AgentOutput = {
|
||||
state: AgentOutputState;
|
||||
handleEvent: (event: AgentEvent) => void;
|
||||
};
|
||||
|
||||
function extractText(message: AgentMessage | undefined): string {
|
||||
|
|
@ -74,50 +64,27 @@ function formatToolLine(name: string, args: unknown): string {
|
|||
return argText ? `• Used ${title} (${argText})` : `• Used ${title}`;
|
||||
}
|
||||
|
||||
export class Agent {
|
||||
private readonly agent: PiAgentCore;
|
||||
private readonly stdout: NodeJS.WritableStream;
|
||||
private readonly stderr: NodeJS.WritableStream;
|
||||
private lastAssistantText = "";
|
||||
private printedLen = 0;
|
||||
private streaming = false;
|
||||
export function createAgentOutput(params: {
|
||||
stdout: NodeJS.WritableStream;
|
||||
stderr: NodeJS.WritableStream;
|
||||
}): AgentOutput {
|
||||
const state: AgentOutputState = {
|
||||
lastAssistantText: "",
|
||||
printedLen: 0,
|
||||
streaming: false,
|
||||
};
|
||||
|
||||
constructor(options: AgentOptions = {}) {
|
||||
this.stdout = options.logger?.stdout ?? process.stdout;
|
||||
this.stderr = options.logger?.stderr ?? process.stderr;
|
||||
|
||||
this.agent = new PiAgentCore();
|
||||
if (options.systemPrompt) this.agent.setSystemPrompt(options.systemPrompt);
|
||||
if (options.thinkingLevel) this.agent.setThinkingLevel(options.thinkingLevel);
|
||||
|
||||
if (options.provider && options.model) {
|
||||
this.agent.setModel(getModel(options.provider, options.model));
|
||||
} else {
|
||||
this.agent.setModel(getModel("kimi-coding", "kimi-k2-thinking"));
|
||||
}
|
||||
|
||||
const cwd = options.cwd ?? process.cwd();
|
||||
this.agent.setTools(createCodingTools(cwd));
|
||||
this.agent.subscribe((event) => this.handleEvent(event));
|
||||
}
|
||||
|
||||
async run(prompt: string): Promise<AgentRunResult> {
|
||||
this.lastAssistantText = "";
|
||||
await this.agent.prompt(prompt);
|
||||
return { text: this.lastAssistantText, error: this.agent.state.error };
|
||||
}
|
||||
|
||||
private handleEvent(event: AgentEvent) {
|
||||
const handleEvent = (event: AgentEvent) => {
|
||||
switch (event.type) {
|
||||
case "message_start": {
|
||||
const msg = event.message;
|
||||
if (msg.role === "assistant") {
|
||||
this.streaming = true;
|
||||
this.printedLen = 0;
|
||||
state.streaming = true;
|
||||
state.printedLen = 0;
|
||||
const text = extractText(msg);
|
||||
if (text.length > 0) {
|
||||
this.stdout.write(text);
|
||||
this.printedLen = text.length;
|
||||
params.stdout.write(text);
|
||||
state.printedLen = text.length;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
|
@ -126,9 +93,9 @@ export class Agent {
|
|||
const msg = event.message;
|
||||
if (msg.role === "assistant") {
|
||||
const text = extractText(msg);
|
||||
if (text.length > this.printedLen) {
|
||||
this.stdout.write(text.slice(this.printedLen));
|
||||
this.printedLen = text.length;
|
||||
if (text.length > state.printedLen) {
|
||||
params.stdout.write(text.slice(state.printedLen));
|
||||
state.printedLen = text.length;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
|
@ -137,27 +104,29 @@ export class Agent {
|
|||
const msg = event.message;
|
||||
if (msg.role === "assistant") {
|
||||
const text = extractText(msg);
|
||||
if (text.length > this.printedLen) {
|
||||
this.stdout.write(text.slice(this.printedLen));
|
||||
this.printedLen = text.length;
|
||||
if (text.length > state.printedLen) {
|
||||
params.stdout.write(text.slice(state.printedLen));
|
||||
state.printedLen = text.length;
|
||||
}
|
||||
if (this.streaming) this.stdout.write("\n");
|
||||
this.streaming = false;
|
||||
this.lastAssistantText = text;
|
||||
if (state.streaming) params.stdout.write("\n");
|
||||
state.streaming = false;
|
||||
state.lastAssistantText = text;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case "tool_execution_start":
|
||||
this.stderr.write(`${formatToolLine(event.toolName, event.args)}\n`);
|
||||
params.stderr.write(`${formatToolLine(event.toolName, event.args)}\n`);
|
||||
break;
|
||||
case "tool_execution_end":
|
||||
if (event.isError) {
|
||||
const errorText = extractText(event.result) || "Tool failed";
|
||||
this.stderr.write(`• Tool error (${toolDisplayName(event.toolName)}): ${errorText}\n`);
|
||||
params.stderr.write(`• Tool error (${toolDisplayName(event.toolName)}): ${errorText}\n`);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
return { state, handleEvent };
|
||||
}
|
||||
29
src/agent/runner.ts
Normal file
29
src/agent/runner.ts
Normal file
|
|
@ -0,0 +1,29 @@
|
|||
import { Agent as PiAgentCore, type AgentEvent } from "@mariozechner/pi-agent-core";
|
||||
import type { AgentOptions, AgentRunResult } from "./types.js";
|
||||
import { createAgentOutput } from "./output.js";
|
||||
import { resolveModel, resolveTools } from "./tools.js";
|
||||
|
||||
export class Agent {
|
||||
private readonly agent: PiAgentCore;
|
||||
private readonly output;
|
||||
|
||||
constructor(options: AgentOptions = {}) {
|
||||
const stdout = options.logger?.stdout ?? process.stdout;
|
||||
const stderr = options.logger?.stderr ?? process.stderr;
|
||||
this.output = createAgentOutput({ stdout, stderr });
|
||||
|
||||
this.agent = new PiAgentCore();
|
||||
if (options.systemPrompt) this.agent.setSystemPrompt(options.systemPrompt);
|
||||
if (options.thinkingLevel) this.agent.setThinkingLevel(options.thinkingLevel);
|
||||
|
||||
this.agent.setModel(resolveModel(options));
|
||||
this.agent.setTools(resolveTools(options));
|
||||
this.agent.subscribe((event: AgentEvent) => this.output.handleEvent(event));
|
||||
}
|
||||
|
||||
async run(prompt: string): Promise<AgentRunResult> {
|
||||
this.output.state.lastAssistantText = "";
|
||||
await this.agent.prompt(prompt);
|
||||
return { text: this.output.state.lastAssistantText, error: this.agent.state.error };
|
||||
}
|
||||
}
|
||||
15
src/agent/tools.ts
Normal file
15
src/agent/tools.ts
Normal file
|
|
@ -0,0 +1,15 @@
|
|||
import type { AgentOptions } from "./types.js";
|
||||
import { getModel } from "@mariozechner/pi-ai";
|
||||
import { createCodingTools } from "@mariozechner/pi-coding-agent";
|
||||
|
||||
export function resolveModel(options: AgentOptions) {
|
||||
if (options.provider && options.model) {
|
||||
return getModel(options.provider, options.model);
|
||||
}
|
||||
return getModel("kimi-coding", "kimi-k2-thinking");
|
||||
}
|
||||
|
||||
export function resolveTools(options: AgentOptions) {
|
||||
const cwd = options.cwd ?? process.cwd();
|
||||
return createCodingTools(cwd);
|
||||
}
|
||||
20
src/agent/types.ts
Normal file
20
src/agent/types.ts
Normal file
|
|
@ -0,0 +1,20 @@
|
|||
import type { ThinkingLevel } from "@mariozechner/pi-agent-core";
|
||||
|
||||
export type AgentRunResult = {
|
||||
text: string;
|
||||
error?: string;
|
||||
};
|
||||
|
||||
export type AgentLogger = {
|
||||
stdout?: NodeJS.WritableStream;
|
||||
stderr?: NodeJS.WritableStream;
|
||||
};
|
||||
|
||||
export type AgentOptions = {
|
||||
provider?: string;
|
||||
model?: string;
|
||||
systemPrompt?: string;
|
||||
thinkingLevel?: ThinkingLevel;
|
||||
cwd?: string;
|
||||
logger?: AgentLogger;
|
||||
};
|
||||
|
|
@ -1,5 +1,5 @@
|
|||
import { v7 as uuidv7 } from "uuid";
|
||||
import { Agent as CoreAgent } from "../agent/agent.js";
|
||||
import { Agent as CoreAgent } from "../agent/runner.js";
|
||||
import { Channel } from "./channel.js";
|
||||
import type { Message } from "./types.js";
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue