Files
KGStudio/src/agent/core/AgentState.ts
T
Xiaohan-Tian 2cd976ff96 refactor: migrate agent system from XML tool calling to OpenAI SDK with native function calling
- Replace custom XML-based tool parsing (XMLToolExecutor) with OpenAI SDK's
  native tool_calls via `openai` npm package (dangerouslyAllowBrowser)
- Consolidate 4 LLM providers (OpenAI, Claude, Gemini, ClaudeOpenRouter)
  into a single OpenAI SDK-based LLMProvider compatible with any
  OpenAI-style API (OpenAI, OpenRouter, Ollama, vLLM)
- Move agentic tool execution loop from ChatBox into AgentCore
- Update Message type to support tool roles, tool_calls, and tool_call_id
- Update system prompt to remove XML formatting instructions (~45% smaller)
- Remove AttemptCompletionTool (replaced by stop_reason detection),
  ThinkTool, and ThinkingTool
- Polish tool descriptions for OpenAI function calling schema compliance
- Normalize base URLs by stripping /chat/completions suffix
2026-04-05 18:59:25 -07:00

136 lines
3.2 KiB
TypeScript

/**
* Manages the state of an agent conversation
*/
/**
* Tool call info attached to assistant messages (OpenAI function calling format)
*/
export interface ToolCall {
id: string;
type: 'function';
function: {
name: string;
arguments: string; // JSON string of parameters
};
}
export interface Message {
id: string;
role: 'user' | 'assistant' | 'tool';
content: string | null;
timestamp: number;
tool_calls?: ToolCall[]; // present on assistant messages when LLM invokes tools
tool_call_id?: string; // present on tool-result messages, links back to ToolCall.id
}
export class AgentState {
private messages: Message[] = [];
private conversationId: string;
private isWorkingOnTask: boolean = false;
constructor(conversationId?: string, isWorkingOnTask: boolean = false) {
this.conversationId = conversationId || this.generateConversationId();
this.isWorkingOnTask = isWorkingOnTask;
}
/**
* Add a message to the conversation
*/
addMessage(
role: 'user' | 'assistant' | 'tool',
content: string | null,
options?: { tool_calls?: ToolCall[]; tool_call_id?: string }
): string {
const message: Message = {
id: this.generateMessageId(),
role,
content,
timestamp: Date.now(),
...(options?.tool_calls ? { tool_calls: options.tool_calls } : {}),
...(options?.tool_call_id ? { tool_call_id: options.tool_call_id } : {}),
};
this.messages.push(message);
return message.id;
}
/**
* Update the content of a message by ID
*/
updateMessage(messageId: string, content: string | null, options?: { tool_calls?: ToolCall[] }): boolean {
const messageIndex = this.messages.findIndex(msg => msg.id === messageId);
if (messageIndex !== -1) {
this.messages[messageIndex].content = content;
if (options?.tool_calls) {
this.messages[messageIndex].tool_calls = options.tool_calls;
}
return true;
}
return false;
}
/**
* Remove a message by ID
*/
removeMessage(messageId: string): boolean {
const messageIndex = this.messages.findIndex(msg => msg.id === messageId);
if (messageIndex !== -1) {
this.messages.splice(messageIndex, 1);
return true;
}
return false;
}
/**
* Remove the last N messages
*/
removeLastMessages(count: number): void {
this.messages.splice(-count, count);
}
/**
* Get all messages in the conversation
*/
getMessages(): Message[] {
return [...this.messages];
}
/**
* Get the conversation ID
*/
getConversationId(): string {
return this.conversationId;
}
/**
* Clear all messages
*/
clearMessages(): void {
this.messages = [];
}
/**
* Get the last N messages
*/
getRecentMessages(count: number): Message[] {
return this.messages.slice(-count);
}
private generateConversationId(): string {
return `conv_${Date.now()}_${Math.random().toString(36).substring(2, 15)}`;
}
private generateMessageId(): string {
return `msg_${Date.now()}_${Math.random().toString(36).substring(2, 15)}`;
}
// getters and setters
getIsWorkingOnTask(): boolean {
return this.isWorkingOnTask;
}
setIsWorkingOnTask(isWorkingOnTask: boolean): void {
this.isWorkingOnTask = isWorkingOnTask;
}
}