Files
llmorbiter/AGENTS.md
2026-09-25 05:42:58 +02:00

3.3 KiB

AGENTS.md

Orientation notes for AI coding assistants working in this repo. Read this first.

What this is

llmorbiter — a small (v0.1.0, 0BSD) LLM tool-calling orchestrator with provider abstraction. Pure ES modules ("type": "module"). Only runtime dependency is dotenv. No build step, no test runner, no README (this file is the orientation doc).

Layout

src/
  index.js                    # public entry — re-exports everything
  LLMOrbiter.js               # core orchestrator  (~364 lines)
  Tool.js                     # defineTool() + ToolError + DirectReturnError
  providers/
    OpenRouterProvider.js     # streaming OpenRouter SSE provider

examples/
  weather.js                  # minimal tool-calling demo
  tool-error-retry.js         # tool-error + LLM self-correction demo (has PASS/FAIL asserts)
  pdf-pages.js                # PDF -> per-page LLM processing (500 lines, largest)
  tools/weather.js            # sample static-data weather tool

Public API (src/index.js)

Exports: LLMOrbiter, OpenRouterProvider, defineTool, ToolError, DirectReturnError.

  • defineTool({ name, description, parameters, strict=true, invoke }) -> { spec, invoke, name }. spec is the OpenAI-compatible function schema sent to the LLM; invoke(args, { signal }) is the local implementation.
  • ToolError(message) — throw from a tool to signal a recoverable error back to the LLM.
  • DirectReturnError(data) — throw from a tool to stop the loop immediately; run() resolves with { directReturn: true, data, ... }.

new LLMOrbiter(provider, opts)

opts are lifecycle callbacks: onLLMCall, onUsage, onToolCall, onToolError, onToolResult, onStatus, onContent, onReasoning.

orbiter.run(opts)

{ prompt, messages, model, tools, maxIterations=10, tool_choice, extraBody, signal }.

Loop: send messages + tool specs -> if tool_calls come back, invoke locals and feed results back -> repeat until no more tool calls or maxIterations. Returns { content, toolResults, usage, rawUsage, iterations, messages, ... }; the returned messages can seed the next run() for conversation continuity (a leading {role:'system'} entry is the system prompt). orbiter.abort(reason) cancels the active run; aborts surface as DOMException('...', 'AbortError').

OpenRouterProvider

new OpenRouterProvider({ apiKey, apiUrl, siteUrl, siteName }). Method: chatCompletions({ model, messages, tools, tool_choice, signal, extraBody, onStatus, onContent, onReasoning }) — streams SSE, assembles { content, tool_calls, finish_reason, usage, reasoning_details, reasoning }. apiUrl defaults to https://openrouter.ai/api/v1/chat/completions.

Gotchas / notes

  • Examples need OPENROUTER_API_KEY (in examples/.env or via --key). Default model in the examples: deepseek/deepseek-v4-flash-0731.
  • .gitignore ignores node_modules/, examples/.env, examples/invoice.pdf.
  • pdf-pages.js shells out to pdftotext -layout, splits pages on \f, and appends prompts to examples/prompts.log.
  • OpenRouterProvider streams reasoning cumulatively via onReasoning.

Style conventions observed

  • 4-space indent, single quotes, semicolons, 'use strict'; header.
  • Comments use em dashes (—) and ─── section dividers in examples.