NOETIC
Framework
Steps

callModel

Call a language model with tools, structured output, and system prompts.

Quick Example

import { callModel } from '@noetic-tools/core';

const chat = callModel({
  id: 'chat',
  model: 'openai/gpt-4o',
  instructions: 'You are a helpful assistant.',
});

What It Does

callModel 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

PropertyTypeRequiredDescription
idstringYesUnique step identifier
modelLazy<string>YesModel identifier (e.g. 'openai/gpt-4o', 'claude-sonnet-4-20250514'), or a (ctx) => string getter resolved per execution
instructionsLazy<string | undefined>NoSystem prompt prepended to the conversation. Accepts a (ctx) => string | undefined getter
toolsLazy<Tool[] | undefined>NoAllowed 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
outputStandardSchemaV1<unknown, O>NoSchema for structured output — any Standard Schema v1 validator (Zod is the default)
outputJsonSchemaRecord<string, unknown>NoExplicit JSON Schema override/fallback for non-Zod output. Zod uses its native converter; other validators can implement StandardJSONSchemaV1. A validation-only schema must provide this field or conversion throws MISSING_JSON_SCHEMA
paramsModelParamsNoGeneration parameters
emitboolean | (eventType, data) => booleanNoControls framework event emission for this step. Defaults to true; false suppresses all framework events; a filter function decides per event
projectionProjectionPolicyNoProjection 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

PropertyTypeDescription
temperaturenumberSampling temperature
topPnumberNucleus sampling threshold
maxTokensnumberMaximum tokens to generate
stopSequencesstring[]Stop sequences

Tool Interface

Tools passed to callModel follow the standard Noetic Tool interface. See invokeTool 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 { callModel } from '@noetic-tools/core';

const extract = callModel({
  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()),
  }),
});

Any Standard Schema v1 validator works in place of Zod. Non-Zod schemas are validated via ~standard.validate (sync or Promise results supported) and the parsed/transformed value is returned. The model constraint resolves through Zod, then the Standard JSON Schema v1 trait, then outputJsonSchema. For non-Zod schemas, the explicit field overrides the trait and falls back when conversion throws. See invokeTool for a Valibot trait example.

If the model response does not match the schema, a validation error is thrown (NoeticError kind model_parse_error; see Error Model).

With Tools

import { z } from 'zod';
import { callModel, 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 = callModel({
  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 callModel step in a loop with until.noToolCalls().

Unified Tool Set

Before execution, the harness collects all tools from every callModel 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 call searchTool.
  • tools: [] -- no tools are available for this step.

Auto-Injected Layer Tools

Functions declared in a context layer's provides field are automatically included in the unified tool set. These tools are namespaced as layerId/fnName (e.g., scratchpad/update). You do not need to pass them in the tools array -- the runtime resolves them from the active context layers.

Tuning Generation

import { callModel } from '@noetic-tools/core';

const creative = callModel({
  id: 'brainstorm',
  model: 'openai/gpt-4o',
  instructions: 'Generate creative ideas.',
  params: {
    temperature: 0.9,
    maxTokens: 2e3,
  },
});
  • runCode -- custom async logic.
  • invokeTool -- invoke a tool directly without an LLM.
  • Loop & Until -- wrap a callModel step in a ReAct-style loop.

On this page