step.llm
Call a language model with tools, structured output, and system prompts.
Quick Example
import { step } from '@noetic-tools/core';
const chat = step.llm({
id: 'chat',
model: 'openai/gpt-4o',
instructions: 'You are a helpful assistant.',
});What It Does
step.llm calls a language model. You provide the model name and optionally a system prompt, tools, structured output schema, and generation parameters. The interpreter handles message formatting, tool execution loops, and response parsing.
API Reference
Options
| Property | Type | Required | Description |
|---|---|---|---|
id | string | Yes | Unique step identifier |
model | Lazy<string> | Yes | Model identifier (e.g. 'openai/gpt-4o', 'claude-sonnet-4-20250514'), or a (ctx) => string getter resolved per execution |
instructions | Lazy<string | undefined> | No | System prompt prepended to the conversation. Accepts a (ctx) => string | undefined getter |
tools | Lazy<Tool[] | undefined> | No | Allowed tool subset (undefined = all, [] = none). Accepts a (ctx) => Tool[] | undefined getter; function-form tools are resolved per execution and do not contribute to the pre-computed ctx.unifiedTools |
output | ZodType<O> | No | Zod schema for structured output |
params | ModelParams | No | Generation parameters |
emit | boolean | (eventType, data) => boolean | No | Controls framework event emission for this step. Defaults to true; false suppresses all framework events; a filter function decides per event |
projection | ProjectionPolicy | No | Projection policy for this step's context window, overriding the harness-level default |
Lazy<T> means T | ((ctx: Context) => T | Promise<T>) — pass a plain value, or a function that the interpreter resolves against the live context each time the step runs (useful for model routing, dynamic prompts, and context-dependent tool sets).
ModelParams
| Property | Type | Description |
|---|---|---|
temperature | number | Sampling temperature |
topP | number | Nucleus sampling threshold |
maxTokens | number | Maximum tokens to generate |
stopSequences | string[] | Stop sequences |
Tool Interface
Tools passed to step.llm follow the standard Noetic Tool interface. See step.tool for the full definition.
Structured Output
Provide a Zod schema to output and the model response will be parsed and validated at runtime.
import { z } from 'zod';
import { step } from '@noetic-tools/core';
const extract = step.llm({
id: 'extract-entities',
model: 'openai/gpt-4o',
instructions: 'Extract named entities from the text.',
output: z.object({
people: z.array(z.string()),
places: z.array(z.string()),
organizations: z.array(z.string()),
}),
});If the model response does not match the schema, a validation error is thrown.
With Tools
import { z } from 'zod';
import { step, tool } from '@noetic-tools/core';
const searchTool = tool({
name: 'search',
description: 'Search the knowledge base',
input: z.object({
query: z.string(),
}),
output: z.object({
results: z.array(z.string()),
}),
execute: async (args) => {
return {
results: ['Result 1', 'Result 2'],
};
},
});
const researcher = step.llm({
id: 'research',
model: 'openai/gpt-4o',
instructions: 'Answer the question using the search tool.',
tools: [searchTool],
});When the model emits a tool call, the runtime executes the tool and feeds the result back. To loop until the model stops calling tools, wrap the LLM step in a loop with until.noToolCalls().
Unified Tool Set
Before execution, the harness collects all tools from every LLM step in the step tree plus layer-provided tools into a single unified set. Every LLM call sends the full set (preserving prompt cache), while tools on each step narrows which tools the model may actually invoke.
tools: undefined(or omitted) -- the model may call any tool in the unified set.tools: [searchTool]-- the model may only callsearchTool.tools: []-- no tools are available for this step.
Auto-Injected Layer Tools
Functions declared in a memory layer's provides field are automatically included in the unified tool set. These tools are namespaced as layerId/fnName (e.g., working-memory/update). You do not need to pass them in the tools array -- the runtime resolves them from the active memory layers.
Tuning Generation
import { step } from '@noetic-tools/core';
const creative = step.llm({
id: 'brainstorm',
model: 'openai/gpt-4o',
instructions: 'Generate creative ideas.',
params: {
temperature: 0.9,
maxTokens: 2e3,
},
});Related
- step.run -- custom async logic.
- step.tool -- invoke a tool directly without an LLM.
- Loop & Until -- wrap an LLM step in a ReAct-style loop.