/** * LLMCall — the leaf primitive for a single LLM invocation (no tools). * * Pattern: Builder (GoF) → produces a Runner backed by a footprintjs FlowChart. * * Chart shape — outer client wrapper around an inner llm-subflow: * * Client → sf-llm-call → loopTo(client) * * Outer `Client` stage: * - First visit: receives args, writes userMessage to scope. * - Second visit (after the loop completes): $break()s with * scope.answer as the chart's TraversalResult. * * Inner `sf-llm-call` subflow (drill-down view): * Initialize → sf-system-prompt → sf-messages → call-llm * → [sf-thinking if handler] → extract-final * * NO `sf-tools` slot — LLMCall has no tools by design (that's Agent's * territory). Atomic LLMCall's lens chart is a clean 3-node top-level * view (Client + LLM + loop edge) that drills into the real flowchart * below. * * Loop semantics: LLMCall is one-shot. The loop fires once; the * second Client visit immediately breaks. The shape is identical to * chat-mode (future): swap `$break()` for `pause()` and the same * chart supports multi-turn conversation. * * Slot subflows write convention-keyed injections observed by * ContextRecorder. The call-llm stage typedEmits stream.llm_start * and stream.llm_end observed by StreamRecorder. When a * `ThinkingHandler` resolves for the provider, `sf-thinking` mounts * automatically (auto-wired by provider.name — same convention Agent * uses). * * Emits (through internally-attached recorders): * agentfootprint.stream.llm_start / llm_end * agentfootprint.context.injected / slot_composed * agentfootprint.stream.thinking_end (when sf-thinking mounted) */ import { type FlowchartCheckpoint, type RunOptions, type StructureRecorder } from 'footprintjs'; import type { GroupMetadata, GroupTranslator } from './translator.js'; import type { RunnerPauseOutcome } from './pause.js'; import type { LLMProvider, PricingTable } from '../adapters/types.js'; import { RunnerBase } from './RunnerBase.js'; export interface LLMCallOptions { readonly provider: LLMProvider; /** Human-friendly name shown in events/metrics. Default: 'LLMCall'. */ readonly name?: string; /** Stable id used for topology + events. Default: 'llm-call'. */ readonly id?: string; /** Model to request from the provider. */ readonly model: string; /** Optional sampling temperature. */ readonly temperature?: number; /** Optional max output tokens. */ readonly maxTokens?: number; /** * Pricing adapter. When set, LLMCall emits `agentfootprint.cost.tick` * after every LLM response with per-call and cumulative USD. Run-scoped * — the cumulative resets on each `.run()`. */ readonly pricingTable?: PricingTable; /** * Cumulative USD budget per run. When provided along with `pricingTable`, * LLMCall emits `agentfootprint.cost.limit_hit` with `action: 'warn'` * the first time cumulative USD crosses the budget. Execution continues * — consumers choose whether to abort by listening to the event. */ readonly costBudget?: number; /** * Optional build-time recorders threaded into footprintjs's * `flowChart()` factory. Each recorder observes per-node build * events (`onStageAdded` / `onSubflowMounted` / etc.) for this * LLMCall's internal chart (Initialize + slot mounts + CallLLM). When * omitted, no build-time observation is wired up. */ readonly structureRecorders?: readonly StructureRecorder[]; /** * Optional per-COMPOSITION translator (UI-agnostic). See * `core/translator.ts`. When attached, `runner.getUIGroup()` invokes * it with the LLMCall's `GroupMetadata` (kind `'LLMCall'`, id, name, * empty `members[]`, plus `extra.slots` with the three slot ids — * `system-prompt`, `messages`, `tools` — so Lens can render the slot * cards inside an LLMCall card without inspecting `buildTimeStructure`). * Returns `undefined` when omitted. */ readonly groupTranslator?: GroupTranslator; } export interface LLMCallInput { readonly message: string; } export type LLMCallOutput = string; export declare class LLMCall extends RunnerBase { readonly name: string; readonly id: string; private readonly provider; private readonly model; private readonly temperature?; private readonly maxTokens?; private readonly systemPromptValue; private readonly pricingTable?; private readonly costBudget?; private readonly structureRecorders?; private readonly groupTranslator?; /** Auto-resolved from provider.name at construction time (same * convention Agent uses — see findThinkingHandler). When undefined, * sf-thinking is NOT mounted and the chart has zero thinking * overhead (build-time conditional mount). */ private readonly thinkingHandler?; private currentRunContext; constructor(opts: LLMCallOptions, systemPromptValue: string); static create(opts: LLMCallOptions): LLMCallBuilder; protected getGroupTranslator(): GroupTranslator | undefined; /** LLMCall has no nested-runner members (slots are subflows of * the LLMCall's own chart, not Runner instances). The slot ids * are surfaced via `extra` so Lens can render the slot cards * inside an LLMCall card without inspecting `buildTimeStructure`. * * TWO slots only — LLMCall does not have tools (that's Agent's * affordance). Atomic LLMCall renders as a clean 2-pill card in * collapsed (top-level) view. */ protected buildUIGroupMetadata(): GroupMetadata; run(input: LLMCallInput, options?: RunOptions): Promise; resume(checkpoint: FlowchartCheckpoint, input?: unknown, options?: RunOptions): Promise; private createExecutor; private finalizeResult; private buildChart; } /** * Tiny fluent builder. Validates required fields at build() time. */ export declare class LLMCallBuilder { private readonly opts; private systemPromptValue; constructor(opts: LLMCallOptions); system(prompt: string): this; build(): LLMCall; }