3.4 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 }.specis 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(inexamples/.envor via--key). Default model in the examples:deepseek/deepseek-v4-flash-0731. .gitignoreignoresnode_modules/,examples/.env,examples/invoice.pdf.pdf-pages.jsshells out topdftotext -layout, splits pages on\f, and appends prompts toexamples/prompts.log.OpenRouterProvider._readStreamcurrently writes reasoning straight toprocess.stdout(debug-style side effect) rather than only viaonReasoning— worth knowing if refactoring.
Style conventions observed
- 4-space indent, single quotes, semicolons,
'use strict';header. - Comments use em dashes (
—) and───section dividers in examples.