Direct LLM Access
Direct LLM Access
Agent inherits runDirect() and streamDirect() methods from BaseAgent. These methods bypass all context management for simple, stateless LLM calls.
When to Use Direct Access
| Use Case | Recommended Method |
|---|---|
| Conversational agent with history | run() |
| Task with memory and tools | run() with context features |
| Per-call reasoning control | run(input, { thinking }) |
| Quick one-off query | runDirect() |
| Embedding-like simplicity | runDirect() |
| Testing/debugging | runDirect() |
| Hybrid workflows | Mix both |
Basic Usage
const agent = Agent.create({ connector: 'openai', model: 'gpt-4.1' });
// Direct call - bypasses all context management
const response = await agent.runDirect('What is 2 + 2?');
console.log(response.output_text); // "4"
// Conversation is NOT affected
console.log(agent.context.getConversation().length); // 0
DirectCallOptions
interface DirectCallOptions {
/** System instructions */
instructions?: string;
/** Include registered tools (default: false) */
includeTools?: boolean;
/** Explicit per-call definitions (used instead of registered tools) */
tools?: Tool[];
/** Tool-selection policy for explicit or included tools */
toolChoice?: 'auto' | 'required' | { type: 'function'; function: { name: string } };
/** Temperature for generation */
temperature?: number;
/** Maximum output tokens */
maxOutputTokens?: number;
/** Continue a provider-stored response/interaction */
previousResponseId?: string;
/** Vendor-agnostic structured (JSON) output */
responseFormat?:
| { type: 'json_object' }
| { type: 'json_schema'; schema: Record<string, unknown>; name?: string; description?: string; strict?: boolean };
/** Vendor-agnostic thinking/reasoning configuration */
thinking?: {
enabled: boolean;
budgetTokens?: number; // Fixed-budget models
effort?: 'none' | 'minimal' | 'low' | 'medium' | 'high' | 'xhigh' | 'max';
};
/** Vendor-specific options */
vendorOptions?: Record<string, unknown>;
/** Provider-neutral prompt-cache policy */
promptCache?: PromptCachePolicy;
/** Provider-hosted tools (not executed by ToolManager) */
nativeTools?: NativeToolRequest[];
/** Explicit permission for retention-sensitive provider features */
dataHandling?: DataHandlingPolicy;
/** Skip the provider pre-flight context limit check (default: false) */
skipContextLimitCheck?: boolean;
}
Examples
// With options
const response = await agent.runDirect('Summarize this text', {
instructions: 'Be concise. Use bullet points.',
temperature: 0.5,
maxOutputTokens: 200,
});
// JSON response
const responseVariant2 = await agent.runDirect('List 3 fruits', {
responseFormat: { type: 'json_object' },
instructions: 'Return a JSON array of fruit names',
});
// Multimodal (text + image)
const responseVariant3 = await agent.runDirect([
{
type: 'message',
role: 'user',
content: [
{ type: 'input_text', text: 'What is in this image?' },
{ type: 'input_image', image_url: 'https://example.com/image.png' }
]
}
]);
// With tools (single call - you handle tool calls manually)
const responseVariant4 = await agent.runDirect('Get the weather in Paris', {
includeTools: true,
});
// If response contains tool_calls, you must execute them yourself
if (responseVariant4.output.some(item => item.type === 'function_call')) {
// Handle tool calls manually
}
Streaming
// Stream responses for real-time output
for await (const event of agent.streamDirect('Tell me a story')) {
if (event.type === 'response.output_text.delta') {
process.stdout.write(event.delta);
}
}
// With options
for await (const event of agent.streamDirect('Explain quantum computing', {
instructions: 'Use simple terms',
temperature: 0.7,
})) {
// Handle events...
}
Per-Call RunOptions
run() and stream() accept an optional second argument to override agent-level config for a single invocation:
interface RunOptions {
/** Vendor-agnostic thinking/reasoning configuration */
thinking?: {
enabled: boolean;
budgetTokens?: number; // Fixed-budget models
effort?: 'none' | 'minimal' | 'low' | 'medium' | 'high' | 'xhigh' | 'max';
};
/** Temperature override */
temperature?: number;
/** Vendor-specific options (shallow-merged with agent-level vendorOptions) */
vendorOptions?: Record<string, unknown>;
/** Provider-neutral prompt-cache policy */
promptCache?: PromptCachePolicy;
/** Provider-hosted tools (not executed by ToolManager) */
nativeTools?: NativeToolRequest[];
/** Explicit permission for retention-sensitive provider features */
dataHandling?: DataHandlingPolicy;
/** Resume an incomplete provider decode on the first request of this run */
continuationToken?: string;
/** Vendor-agnostic structured (JSON) output override for this call */
responseFormat?:
| { type: 'json_object' }
| { type: 'json_schema'; schema: Record<string, unknown>; name?: string; description?: string; strict?: boolean };
}
// Override reasoning effort per call
const deep = await agent.run('Prove this theorem', {
thinking: { enabled: true, effort: 'high' },
});
const quick = await agent.run('What is 2+2?', {
thinking: { enabled: true, effort: 'low' },
});
// Override temperature per call
const creative = await agent.run('Write a poem', { temperature: 0.9 });
// Streaming with per-call options
for await (const event of agent.stream('Analyze this', {
thinking: { enabled: true, budgetTokens: 16384 },
})) {
// ...
}
RunOptions take precedence over agent-level config. vendorOptions are shallow-merged (per-call keys override agent-level keys). Advanced inference options replace their agent-level value as a whole; see Advanced Inference for the complete contract.
Thinking / Reasoning
Vendor-agnostic reasoning configuration that maps to each provider's native API:
| Provider | effort maps to |
budgetTokens maps to |
|---|---|---|
| OpenAI | reasoning.effort |
N/A |
| Anthropic | output_config.effort |
thinking.budget_tokens on fixed-budget models |
| Gemini thinking level (the exact supported subset is model-dependent) | thinkingConfig.thinkingBudget on fixed-budget models |
// Agent-level (applies to all calls)
const agent = Agent.create({
connector: 'openai', model: 'o3-mini',
thinking: { enabled: true, effort: 'medium' },
});
// Per-call override
await agent.run('Complex analysis', { thinking: { enabled: true, effort: 'high' } });
await agent.run('Simple lookup', { thinking: { enabled: true, effort: 'low' } });
// Anthropic with budget tokens
const agent2 = Agent.create({ connector: 'anthropic', model: 'claude-sonnet-4-6' });
await agent2.run('Deep reasoning task', { thinking: { enabled: true, budgetTokens: 16384 } });
// runDirect() also supports thinking
await agent.runDirect('Quick Q', { thinking: { enabled: true, effort: 'low' } });
The normalized thinking.enabled flag gates both its nested effort and
budget. Anthropic also supports native effort independently of thinking; use
vendorOptions: { effort: 'low' | 'medium' | 'high' | ... } when that distinct
provider behavior is intended while normalized thinking is disabled.
Comparison: run() vs runDirect()
| Aspect | run() |
runDirect() |
|---|---|---|
| History tracking | ✅ Automatic | ❌ None |
| WorkingMemory | ✅ Available | ❌ Not used |
| Context preparation | ✅ Full preparation | ❌ None |
| Agentic loop | ✅ Executes tools automatically | ❌ Single call only |
| Compaction | ✅ Auto-compacts when needed | ❌ None |
| Overhead | Full context management | Minimal |
Hybrid Workflows
You can mix both approaches in the same agent:
const agent = Agent.create({
connector: 'openai',
model: 'gpt-4.1',
tools: [weatherTool, searchTool],
});
// Use run() for complex interactions with tool use
await agent.run('Search for the latest news and summarize');
// Use runDirect() for quick follow-ups that don't need context
const clarification = await agent.runDirect(
'What is a good synonym for "excellent"?',
{ temperature: 0.3 }
);
// Back to run() for continued conversation
await agent.run('Now tell me more about the first item');