chore(vendor): import pi 1.3.0
Imported as a linear vendor commit so forge rebases do not replay the upstream root tree. git-subtree-dir: third_party/pi git-subtree-split: 71023aec1546d6d677a65669c5665c0927ce0dd0
This commit is contained in:
1 parent
719370c8f7
commit
6081e3e325
1743 files changed
+205459
-131704
No files matched your search
+62
-41
@@ -1,11 +1,15 @@
|
||||
import type {
|
||||
ImageContent,
|
||||
Message,
|
||||
Model,
|
||||
SimpleStreamOptions,
|
||||
TextContent,
|
||||
ThinkingBudgets,
|
||||
Transport,
|
||||
import {
|
||||
createInitialSystemMessage,
|
||||
getCurrentSystemMessage,
|
||||
getCurrentSystemPrompt,
|
||||
type ImageContent,
|
||||
type Message,
|
||||
type Model,
|
||||
type SimpleStreamOptions,
|
||||
type TextContent,
|
||||
type ThinkingBudgets,
|
||||
type Transport,
|
||||
toToolDeclaration,
|
||||
} from "@earendil-works/pi-ai";
|
||||
import { runAgentLoop, runAgentLoopContinue } from "./agent-loop.ts";
|
||||
import { getDefaultStreamFn } from "./stream-fn.ts";
|
||||
@@ -21,9 +25,10 @@ import type {
|
||||
AgentTool,
|
||||
BeforeToolCallContext,
|
||||
BeforeToolCallResult,
|
||||
FinishTurn,
|
||||
PrepareNextTurnContext,
|
||||
PrepareRequest,
|
||||
QueueMode,
|
||||
ShouldStopAfterTurnContext,
|
||||
StreamFn,
|
||||
ToolExecutionMode,
|
||||
} from "./types.ts";
|
||||
@@ -32,7 +37,11 @@ export type { QueueMode } from "./types.ts";
|
||||
|
||||
function defaultConvertToLlm(messages: AgentMessage[]): Message[] {
|
||||
return messages.filter(
|
||||
(message) => message.role === "user" || message.role === "assistant" || message.role === "toolResult",
|
||||
(message) =>
|
||||
message.role === "system" ||
|
||||
message.role === "user" ||
|
||||
message.role === "assistant" ||
|
||||
message.role === "toolResult",
|
||||
);
|
||||
}
|
||||
|
||||
@@ -65,14 +74,21 @@ type MutableAgentState = Omit<AgentState, "isStreaming" | "streamingMessage" | "
|
||||
errorMessage?: string;
|
||||
};
|
||||
|
||||
function createMutableAgentState(
|
||||
initialState?: Partial<Omit<AgentState, "pendingToolCalls" | "isStreaming" | "streamingMessage" | "errorMessage">>,
|
||||
): MutableAgentState {
|
||||
/** Initial state for {@link Agent}. `systemPrompt` and `tools` become the leading system message unless `messages` already starts with one. */
|
||||
export type AgentInitialState = Partial<
|
||||
Omit<AgentState, "pendingToolCalls" | "isStreaming" | "streamingMessage" | "errorMessage">
|
||||
>;
|
||||
|
||||
function createMutableAgentState(initialState?: AgentInitialState): MutableAgentState {
|
||||
let tools = initialState?.tools?.slice() ?? [];
|
||||
let messages = initialState?.messages?.slice() ?? [];
|
||||
const initialMessage = createInitialSystemMessage(initialState?.systemPrompt, tools.map(toToolDeclaration));
|
||||
if (messages[0]?.role !== "system" && initialMessage) messages.unshift(initialMessage);
|
||||
|
||||
return {
|
||||
systemPrompt: initialState?.systemPrompt ?? "",
|
||||
get systemPrompt() {
|
||||
return getCurrentSystemPrompt(messages);
|
||||
},
|
||||
model: initialState?.model ?? DEFAULT_MODEL,
|
||||
thinkingLevel: initialState?.thinkingLevel ?? "off",
|
||||
get tools() {
|
||||
@@ -96,16 +112,18 @@ function createMutableAgentState(
|
||||
|
||||
/** Options for constructing an {@link Agent}. */
|
||||
export interface AgentOptions {
|
||||
initialState?: Partial<Omit<AgentState, "pendingToolCalls" | "isStreaming" | "streamingMessage" | "errorMessage">>;
|
||||
initialState?: AgentInitialState;
|
||||
convertToLlm?: (messages: AgentMessage[]) => Message[] | Promise<Message[]>;
|
||||
transformContext?: (messages: AgentMessage[], signal?: AbortSignal) => Promise<AgentMessage[]>;
|
||||
streamFn: StreamFn;
|
||||
getApiKey?: (provider: string) => Promise<string | undefined> | string | undefined;
|
||||
onPayload?: SimpleStreamOptions["onPayload"];
|
||||
onResponse?: SimpleStreamOptions["onResponse"];
|
||||
onProviderStreamEvent?: SimpleStreamOptions["onProviderStreamEvent"];
|
||||
beforeToolCall?: (context: BeforeToolCallContext, signal?: AbortSignal) => Promise<BeforeToolCallResult | undefined>;
|
||||
afterToolCall?: (context: AfterToolCallContext, signal?: AbortSignal) => Promise<AfterToolCallResult | undefined>;
|
||||
shouldStopAfterTurn?: (context: ShouldStopAfterTurnContext, signal?: AbortSignal) => boolean | Promise<boolean>;
|
||||
finishTurn?: FinishTurn;
|
||||
prepareRequest?: PrepareRequest;
|
||||
prepareNextTurn?: (
|
||||
signal?: AbortSignal,
|
||||
) => Promise<AgentLoopTurnUpdate | undefined> | AgentLoopTurnUpdate | undefined;
|
||||
@@ -138,19 +156,16 @@ class PendingMessageQueue {
|
||||
return this.messages.length > 0;
|
||||
}
|
||||
|
||||
drain(): AgentMessage[] {
|
||||
if (this.mode === "all") {
|
||||
const drained = this.messages.slice();
|
||||
this.messages = [];
|
||||
return drained;
|
||||
}
|
||||
|
||||
peek(): AgentMessage[] {
|
||||
if (this.mode === "all") return this.messages.slice();
|
||||
const first = this.messages[0];
|
||||
if (!first) {
|
||||
return [];
|
||||
}
|
||||
this.messages = this.messages.slice(1);
|
||||
return [first];
|
||||
return first ? [first] : [];
|
||||
}
|
||||
|
||||
drain(): AgentMessage[] {
|
||||
const drained = this.peek();
|
||||
this.messages = this.messages.slice(drained.length);
|
||||
return drained;
|
||||
}
|
||||
|
||||
clear(): void {
|
||||
@@ -182,6 +197,7 @@ export class Agent {
|
||||
public getApiKey?: (provider: string) => Promise<string | undefined> | string | undefined;
|
||||
public onPayload?: SimpleStreamOptions["onPayload"];
|
||||
public onResponse?: SimpleStreamOptions["onResponse"];
|
||||
public onProviderStreamEvent?: SimpleStreamOptions["onProviderStreamEvent"];
|
||||
public beforeToolCall?: (
|
||||
context: BeforeToolCallContext,
|
||||
signal?: AbortSignal,
|
||||
@@ -190,10 +206,8 @@ export class Agent {
|
||||
context: AfterToolCallContext,
|
||||
signal?: AbortSignal,
|
||||
) => Promise<AfterToolCallResult | undefined>;
|
||||
public shouldStopAfterTurn?: (
|
||||
context: ShouldStopAfterTurnContext,
|
||||
signal?: AbortSignal,
|
||||
) => boolean | Promise<boolean>;
|
||||
public finishTurn?: FinishTurn;
|
||||
public prepareRequest?: PrepareRequest;
|
||||
public prepareNextTurn?: (
|
||||
signal?: AbortSignal,
|
||||
) => Promise<AgentLoopTurnUpdate | undefined> | AgentLoopTurnUpdate | undefined;
|
||||
@@ -223,9 +237,11 @@ export class Agent {
|
||||
this.getApiKey = runtimeOptions.getApiKey;
|
||||
this.onPayload = runtimeOptions.onPayload;
|
||||
this.onResponse = runtimeOptions.onResponse;
|
||||
this.onProviderStreamEvent = runtimeOptions.onProviderStreamEvent;
|
||||
this.beforeToolCall = runtimeOptions.beforeToolCall;
|
||||
this.afterToolCall = runtimeOptions.afterToolCall;
|
||||
this.shouldStopAfterTurn = runtimeOptions.shouldStopAfterTurn;
|
||||
this.finishTurn = runtimeOptions.finishTurn;
|
||||
this.prepareRequest = runtimeOptions.prepareRequest;
|
||||
this.prepareNextTurn = runtimeOptions.prepareNextTurn;
|
||||
this.prepareNextTurnWithContext = runtimeOptions.prepareNextTurnWithContext;
|
||||
this.steeringQueue = new PendingMessageQueue(runtimeOptions.steeringMode ?? "one-at-a-time");
|
||||
@@ -310,6 +326,12 @@ export class Agent {
|
||||
return this.steeringQueue.hasItems() || this.followUpQueue.hasItems();
|
||||
}
|
||||
|
||||
/** Preview the messages selected for the next turn without consuming them. */
|
||||
peekQueuedMessages(): AgentMessage[] {
|
||||
const steering = this.steeringQueue.peek();
|
||||
return steering.length > 0 ? steering : this.followUpQueue.peek();
|
||||
}
|
||||
|
||||
/** Active abort signal for the current run, if any. */
|
||||
get signal(): AbortSignal | undefined {
|
||||
return this.activeRun?.abortController.signal;
|
||||
@@ -329,13 +351,14 @@ export class Agent {
|
||||
return this.activeRun?.promise ?? Promise.resolve();
|
||||
}
|
||||
|
||||
/** Clear transcript state, runtime state, and queued messages. */
|
||||
/** Clear conversation state and queues while retaining the replayed prompt/tool baseline. */
|
||||
reset(): void {
|
||||
if (this.activeRun) {
|
||||
throw new Error("Agent is already processing. Wait for completion before resetting.");
|
||||
}
|
||||
|
||||
this._state.messages = [];
|
||||
const baseline = getCurrentSystemMessage(this._state.messages);
|
||||
this._state.messages = baseline ? [baseline] : [];
|
||||
this._state.isStreaming = false;
|
||||
this._state.streamingMessage = undefined;
|
||||
this._state.pendingToolCalls = new Set<string>();
|
||||
@@ -364,7 +387,7 @@ export class Agent {
|
||||
}
|
||||
|
||||
const lastMessage = this._state.messages[this._state.messages.length - 1];
|
||||
if (!lastMessage) {
|
||||
if (!lastMessage || this._state.messages.every((message) => message.role === "system")) {
|
||||
throw new Error("No messages to continue from");
|
||||
}
|
||||
|
||||
@@ -436,7 +459,6 @@ export class Agent {
|
||||
|
||||
private createContextSnapshot(): AgentContext {
|
||||
return {
|
||||
systemPrompt: this._state.systemPrompt,
|
||||
messages: this._state.messages.slice(),
|
||||
tools: this._state.tools.slice(),
|
||||
};
|
||||
@@ -444,22 +466,21 @@ export class Agent {
|
||||
|
||||
private createLoopConfig(options: { skipInitialSteeringPoll?: boolean } = {}): AgentLoopConfig {
|
||||
let skipInitialSteeringPoll = options.skipInitialSteeringPoll === true;
|
||||
const shouldStopAfterTurn = this.shouldStopAfterTurn;
|
||||
return {
|
||||
model: this._state.model,
|
||||
reasoning: this._state.thinkingLevel === "off" ? undefined : this._state.thinkingLevel,
|
||||
sessionId: this.sessionId,
|
||||
onPayload: this.onPayload,
|
||||
onResponse: this.onResponse,
|
||||
onProviderStreamEvent: this.onProviderStreamEvent,
|
||||
transport: this.transport,
|
||||
thinkingBudgets: this.thinkingBudgets,
|
||||
maxRetryDelayMs: this.maxRetryDelayMs,
|
||||
toolExecution: this.toolExecution,
|
||||
beforeToolCall: this.beforeToolCall,
|
||||
afterToolCall: this.afterToolCall,
|
||||
shouldStopAfterTurn: shouldStopAfterTurn
|
||||
? async (context) => await shouldStopAfterTurn(context, this.signal)
|
||||
: undefined,
|
||||
finishTurn: this.finishTurn,
|
||||
prepareRequest: this.prepareRequest,
|
||||
prepareNextTurn:
|
||||
this.prepareNextTurnWithContext || this.prepareNextTurn
|
||||
? async (context) => {
|
||||
|
||||
Reference in new issue
Block a user