All files / src/agent index.ts

28.57% Statements 20/70
100% Branches 5/5
80% Functions 4/5
28.57% Lines 20/70

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146                              1x                                                                                     1x 3x 3x 3x 3x 3x 3x 3x   3x   3x     3x 3x                                                                                                                           3x 3x 3x 3x   3x 3x   1x    
/**
 * AgentKits — Agent Loop
 *
 * Autonomous agent with tool calling, memory, and multi-step reasoning.
 *
 * Usage:
 *   import { createAgent } from 'agentkits/agent';
 *   import { defineTool } from 'agentkits';
 *   const agent = createAgent({
 *     provider: 'gemini', apiKey: '...',
 *     tools: [searchTool, calcTool],
 *   });
 *   const result = await agent.run('Find the weather in Beijing');
 */
 
import { createToolChat, defineTool, type ToolChatConfig, type ToolDefinition } from '../tools/index.js';
import type { ChatMessage } from '../llm/index.js';
 
// ── Types ──────────────────────────────────────────────────────────
 
export interface AgentConfig extends Omit<ToolChatConfig, 'tools'> {
  tools?: ToolDefinition[];
  /** System prompt */
  system?: string;
  /** Memory: persist messages across runs */
  memory?: boolean;
  /** Callback for each step */
  onStep?: (step: AgentStep) => void;
}
 
export interface AgentStep {
  index: number;
  type: 'tool_call' | 'tool_result' | 'answer';
  content: string;
  toolName?: string;
  timestamp: number;
}
 
export interface AgentResult {
  answer: string;
  steps: AgentStep[];
  toolCalls: number;
  rounds: number;
}
 
export interface AgentClient {
  /** Run agent with a task */
  run(task: string): Promise<AgentResult>;
  /** Get conversation history */
  readonly history: ChatMessage[];
  /** Clear memory */
  reset(): void;
  /** Current config */
  readonly config: Readonly<AgentConfig>;
}
 
// ── Main ──────────────────────────────────────────────────────────
 
export function createAgent(config: AgentConfig): AgentClient {
  const {
    tools = [],
    system,
    memory = false,
    onStep,
    ...toolChatConfig
  } = config;
 
  let conversationHistory: ChatMessage[] = [];
 
  const defaultSystem = system ?? `You are a helpful AI assistant with access to tools.
Think step by step. Use tools when needed. Provide a clear final answer.`;
 
  const client: AgentClient = {
    async run(task) {
      const toolChat = createToolChat({
        ...toolChatConfig,
        tools,
      });
 
      const response = await toolChat.run(task, { system: defaultSystem });
 
      const steps: AgentStep[] = [];
      let stepIdx = 0;
 
      // Log tool calls
      for (const tc of response.toolCalls) {
        const step: AgentStep = {
          index: stepIdx++,
          type: 'tool_call',
          content: `${tc.name}(${JSON.stringify(tc.arguments)})`,
          toolName: tc.name,
          timestamp: Date.now(),
        };
        steps.push(step);
        onStep?.(step);
      }
 
      // Log tool results
      for (const tr of response.toolResults) {
        const step: AgentStep = {
          index: stepIdx++,
          type: 'tool_result',
          content: tr.result,
          toolName: tr.toolCallId,
          timestamp: Date.now(),
        };
        steps.push(step);
        onStep?.(step);
      }
 
      // Final answer
      const answerStep: AgentStep = {
        index: stepIdx++,
        type: 'answer',
        content: response.content,
        timestamp: Date.now(),
      };
      steps.push(answerStep);
      onStep?.(answerStep);
 
      if (memory) {
        conversationHistory.push(
          { role: 'user', content: task },
          { role: 'assistant', content: response.content },
        );
      }
 
      return {
        answer: response.content,
        steps,
        toolCalls: response.toolCalls.length,
        rounds: response.rounds,
      };
    },
 
    get history() { return [...conversationHistory]; },
    reset() { conversationHistory = []; },
    get config() { return config; },
  };
 
  return client;
}
 
export { defineTool } from '../tools/index.js';
export type { ToolDefinition } from '../tools/index.js';