Skip to main content

Module mock

Module mock 

Source
Expand description

A minimal built-in mock LLM for the observe-to-validate E2E suite. Hidden mode: agentd --internal-mock-llm <addr-file> [script].

Binds a loopback TCP listener on 127.0.0.1:0 and writes the bound host:port into <addr-file> atomically, via tmp-plus-rename, so the launching harness can discover the endpoint by waiting for the file to appear and is never able to read a half-written address. The harness then hands agentd --intelligence http://<addr>; loopback plaintext is the dev/test carve-out, since production intelligence is HTTPS-only.

Speaks just enough OpenAI-compatible /chat/completions over that listener to drive a real agentic loop without a live model: it reads the request and returns a scripted assistant turn — a final answer or a tool call — switching to a final answer once a tool result appears in the transcript, so the ReAct cycle closes instead of spinning. Scripts: final (answer at once), read (call resource.read then answer), schedule (call the schedule self-tool then answer), subscribe (call the subscribe self-tool then answer), spawn-churn (call subagent.spawn on every turn, never converging, so a run issues a rapid burst of spawns and trips the spawn-rate limiter); slow/hang hold the response to exercise the stuck/deadline detectors. Small enough to ship, and it makes the loop and the self-* tools observable end to end.

Programmable scripts: file:<path> loads a JSON playbook, so a test can script any conversation without adding a new built-in:

{ "turns": [ {"tool_calls": [{"name": "memory.set", "arguments": {"key": "k", "value": 1}}]},
             {"content": "done", "usage": {"prompt_tokens": 100, "completion_tokens": 20}} ],
  "match": [ {"when_contains": "\"preflight\"", "content": "{\"intent\":\"task\"}"} ] }

match[] rules are tried first (the first whose when_contains substring appears in the request body answers); otherwise turns[i] answers where i is the number of role: tool messages already in the transcript (clamped to the last turn). A turn carries content (final text) or tool_calls, an optional usage, and an optional delay_ms.

Functions§

inprocess
Start the mock IN-PROCESS and return its loopback address. This backs the intelligence.endpoints: mock:<script> convenience, so a config runs fully offline in ONE process: no key, no network, no second terminal. At most one server is started per distinct script string and the address is memoised, so repeated resolves of the same script share a listener instead of leaking a thread per call. Scripts are the same as the hidden mode’s (final | read | schedule | file:<playbook.json>).
run
Serve the mock LLM until the process is killed, announcing the bound loopback address through addr_file. Returns the exit code.