Skip to content

call()

Send a single request to an LLM and get a typed response.

call() is the lowest-level non-streaming request primitive. It applies optional compression, checks budget, calls the adapter, optionally validates the response against a schema, and returns a Result.

Signature

ts
function call<T = unknown>(options: CallOptions<T>): Promise<Result<CallOutput<T>>>

CallOptions

ts
type CallOptions<T = unknown> = {
  // Required
  adapter: ProviderAdapter;
  model: string;
  messages: Message[];

  // Optional — schema validation
  schema?: StandardSchemaV1<unknown, T>;

  // Optional — budget enforcement
  budget?: Budget;

  // Optional — message compression
  compress?: Transform;

  // Optional — pass-through to adapter
  tools?: Tool[];
  maxTokens?: number;
  temperature?: number;
  stopSequences?: string[];
  cache?: 'auto' | 'off';

  // Optional — observability
  logger?: Logger;
  signal?: AbortSignal;
};
OptionTypeRequiredDescription
adapterProviderAdapterYesThe LLM provider adapter
modelstringYesModel identifier (e.g. 'claude-opus-4-7')
messagesMessage[]YesConversation history
schemaStandardSchemaV1NoValidate response as JSON against this schema
budgetBudgetNoEnforce step/token/dollar limits
compressTransformNoTransform messages before sending
toolsTool[]NoAvailable tools for this call
maxTokensnumberNoMaximum response tokens
temperaturenumberNoSampling temperature
stopSequencesstring[]NoStop generation at these sequences
cache'auto' | 'off'NoPrompt caching mode — 'auto' places cache breakpoints automatically, 'off' disables them
loggerLoggerNoDebug/info/warn/error logger
signalAbortSignalNoCancellation signal

CallOutput

ts
type CallOutput<T = unknown> = {
  message: Message & { role: 'assistant' };
  value?: T;       // populated when schema is provided and response is valid JSON
  usage: Usage;
  cost?: number;
  stopReason: StopReason;
};

Return value

Promise<Result<CallOutput<T>>> — never throws. On failure, returns { ok: false, error: Error }.

Common error types:

  • AdapterError — network or API error from the provider
  • BudgetExhausted — budget limit hit before or after the call
  • ParseError — response content was not valid JSON (when schema is set)
  • ValidationError — response JSON did not match the schema

Examples

Basic call

ts
import { call } from 'flint';
import { anthropicAdapter } from '@flint/adapter-anthropic';

const adapter = anthropicAdapter({ apiKey: process.env.ANTHROPIC_API_KEY! });

const res = await call({
  adapter,
  model: 'claude-opus-4-7',
  messages: [{ role: 'user', content: 'What is 2 + 2?' }],
});

if (res.ok) {
  console.log(res.value.message.content); // "4"
  console.log(res.value.usage);           // { input: 12, output: 3 }
}

With schema validation

ts
import { call } from 'flint';
import * as v from 'valibot';

const SentimentSchema = v.object({
  label: v.picklist(['positive', 'negative', 'neutral']),
  score: v.number(),
});

const res = await call({
  adapter,
  model: 'claude-opus-4-7',
  messages: [
    { role: 'system', content: 'Respond with JSON only.' },
    { role: 'user', content: 'Sentiment of: "I love this library!"' },
  ],
  schema: SentimentSchema,
});

if (res.ok && res.value.value) {
  console.log(res.value.value.label); // "positive"
}

With budget

ts
import { call } from 'flint';
import { budget } from 'flint/budget';

const b = budget({ maxTokens: 1000, maxDollars: 0.05 });

const res = await call({
  adapter,
  model: 'claude-opus-4-7',
  messages: [{ role: 'user', content: 'Hello' }],
  budget: b,
});

CallOptions reference

ts
type CallOptions<T = unknown> = {
  // Required
  adapter: ProviderAdapter;
  model: string;
  messages: Message[];

  // Output schema — forces JSON response and validates against schema
  schema?: StandardSchemaV1<unknown, T>;

  // LLM call parameters
  tools?: Tool[];
  maxTokens?: number;
  temperature?: number;
  stopSequences?: string[];
  cache?: CacheControl;

  // Flint features
  budget?: Budget;
  compress?: Transform;
  logger?: Logger;
  signal?: AbortSignal;
};
OptionTypeDefaultDescription
adapterProviderAdapterrequiredThe LLM provider adapter
modelstringrequiredModel identifier (e.g. 'claude-opus-4-7')
messagesMessage[]requiredConversation history
schemaStandardSchemaV1Validates response as JSON against schema. Sets output.value on success.
toolsTool[]Tools available for this call
maxTokensnumberMax output tokens (provider default if unset)
temperaturenumberSampling temperature 0-1
stopSequencesstring[]Stop generation when any sequence is encountered
cacheCacheControlExplicit cache control (adapter-specific)
budgetBudgetBudget to consume for this call
compressTransformMessage transform applied before sending
loggerLoggerReceives debug/info/warn/error log entries
signalAbortSignalCancels the request when aborted

CallOutput reference

ts
type CallOutput<T = unknown> = {
  message: Message & { role: 'assistant' };
  value?: T;           // populated when schema is set and validation passes
  usage: Usage;        // { input, output, cached? } token counts
  cost?: number;       // USD cost (populated if adapter reports it)
  stopReason: StopReason; // 'end' | 'tool_call' | 'max_tokens' | 'stop_sequence'
};

StopReason values

ValueMeaning
'end'Model finished naturally
'tool_call'Model wants to call a tool — check message.toolCalls
'max_tokens'Hit maxTokens limit or provider max
'stop_sequence'Hit one of stopSequences

Schema validation

When schema is set, call():

  1. Expects the model response to be valid JSON
  2. Parses the JSON
  3. Validates against the schema
  4. Returns { ok: false, error: ValidationError } if validation fails, or { ok: false, error: ParseError } if the response isn't JSON
ts
import * as v from 'valibot';

const res = await call({
  adapter,
  model: 'claude-opus-4-7',
  messages: [{ role: 'user', content: 'Return JSON: { "score": 0-10 }' }],
  schema: v.object({ score: v.number() }),
});

if (res.ok) {
  console.log(res.value.value?.score); // typed as number
}

Schema validation applies after tool calls

If stopReason === 'tool_call', schema validation is skipped — the message contains tool calls, not JSON output.

Common mistakes

Don't access res.value without checking res.ok first

res.value is only defined when res.ok === true. TypeScript enforces this, but be careful with type assertions.

Use compress to manage context window costs

Pass a compress transform to trim redundant messages before they're sent. See Compress & Pipeline.

See also

  • stream() — streaming variant
  • agent() — multi-step tool-calling loop
  • Budget — step/token/dollar limits
  • Error Types — AdapterError, ValidationError, ParseError

Released under the MIT License.