Somnia Agents / Base agents
LLM Inference
Deterministic on-chain LLM inference, including tool use with MCP servers and on-chain calls.
Provides access to Somnia's on-chain deterministic AI models for text generation, analysis, and decision-making. This agent enables smart contracts to leverage large language models for intelligent automation — including tool use with both MCP servers and on-chain function calls.
Methods
inferString
Simple single-turn inference with an optional system prompt.
function inferString(string prompt, string system, bool chainOfThought, string[] allowedValues) returns (string response)
Parameters
| Input | Type | Description |
|---|---|---|
prompt |
string | The user prompt to send to the model |
system |
string | The system prompt to configure model behavior (can be empty string) |
chainOfThought |
bool | Whether to enable chain-of-thought reasoning |
allowedValues |
string[] | Optional list of allowed response values — the model is constrained to return one of these. Pass an empty array for unconstrained output |
Returns
| Output | Type | Description |
|---|---|---|
response |
string | The generated text response |
inferNumber
Single-turn inference that extracts an integer from the model's response, clamped to a specified range.
function inferNumber(string prompt, string system, int256 minValue, int256 maxValue, bool chainOfThought) returns (int256 response)
Parameters
| Input | Type | Description |
|---|---|---|
prompt |
string | The user prompt to send to the model |
system |
string | The system prompt to configure model behavior (can be empty string) |
minValue |
int256 | Minimum allowed value for the response |
maxValue |
int256 | Maximum allowed value for the response |
chainOfThought |
bool | Whether to enable chain-of-thought reasoning |
Returns
| Output | Type | Description |
|---|---|---|
response |
int256 | The extracted integer, clamped to [minValue, maxValue] |
inferChat
Multi-turn conversational inference with full message history.
function inferChat(string[] roles, string[] messages, bool chainOfThought) returns (string response)
Parameters
| Input | Type | Description |
|---|---|---|
roles |
string[] | Array of message roles: "system", "user", or "assistant" |
messages |
string[] | Array of message contents (must match length of roles array) |
chainOfThought |
bool | Whether to enable chain-of-thought reasoning |
Returns
| Output | Type | Description |
|---|---|---|
response |
string | The generated text response |
inferToolsChat
Inference with tool use. The LLM can call tools provided by MCP servers (executed in-situ by the agent) and on-chain tools (yielded back to the caller as ABI-encoded calldata). When on-chain tool calls are pending, the function returns the full conversation state so the caller can execute the calls and resume.
function inferToolsChat(
string[] roles,
string[] messages,
string[] mcpServerUrls,
OnchainTool[] onchainTools,
uint256 maxIterations,
bool chainOfThought
) returns (
string finishReason,
string response,
string[] updatedRoles,
string[] updatedMessages,
string[] pendingToolCallIds,
bytes[] pendingToolCalls
)
Tool definitions
On-chain tools are defined using Solidity function signature strings, making them natural for on-chain callers:
struct OnchainTool {
string signature; // e.g. "swap(address token, uint256 amount)"
string description; // Human-readable description for the LLM
}
Supported Solidity types in signatures: string, bool, address, uint256 (and other uint/int sizes), bytes, and arrays of these types.
MCP (Model Context Protocol) tools are discovered automatically — pass the server URLs and the agent fetches the available tools at runtime.
Parameters
| Input | Type | Description |
|---|---|---|
roles |
string[] | Array of message roles (system/user/assistant/tool) |
messages |
string[] | Array of message contents (must match length of roles) |
mcpServerUrls |
string[] | URLs of MCP servers whose tools the LLM may call |
onchainTools |
OnchainTool[] | Tools yielded back to caller as calldata |
maxIterations |
uint256 | Maximum LLM↔tool round-trips before stopping |
chainOfThought |
bool | Whether to enable chain-of-thought reasoning |
Returns
| Output | Type | Description |
|---|---|---|
finishReason |
string | "stop" (LLM done), "tool_calls" (on-chain calls pending), or "max_iterations" (limit reached) |
response |
string | Final text response (populated when finishReason == "stop") |
updatedRoles |
string[] | Full conversation state — roles (for resumption) |
updatedMessages |
string[] | Full conversation state — messages (for resumption) |
pendingToolCallIds |
string[] | IDs of pending on-chain tool calls |
pendingToolCalls |
bytes[] | ABI-encoded calldata for each pending call (selector + args) |
Return semantics
finishReason == "stop": The LLM has finished.responsecontains the final text. All other outputs are empty arrays. Any MCP tool calls were already executed during processing.finishReason == "tool_calls": The LLM wants to call on-chain tool(s).responseis empty.updatedRoles/updatedMessagescontain the full conversation history (including any MCP tool results).pendingToolCallIdsandpendingToolCallsare parallel arrays — eachpendingToolCalls[i]is calldata (4-byte function selector + ABI-encoded arguments).finishReason == "max_iterations": The agent reachedmaxIterationstool round-trips without the LLM producing a final response.
Tool use flow
MCP tools (automatic)
MCP tools are executed automatically by the agent. The LLM calls the tool, the agent forwards the call to the MCP server, feeds the result back to the LLM, and continues until done.
For example, with one MCP weather server:
- Your contract calls
inferToolsChat(messages, mcpServerUrls, [], 5, false). - The agent asks the MCP server for its available tools and gets back the tool definitions.
- The agent sends the prompt and tool definitions to the language model.
- The model returns a tool call:
getWeather("Tokyo"). - The agent executes
getWeather("Tokyo")on the MCP server, which returns{"temp": "22°C", "condition": "sunny"}. - The agent passes the tool result back to the model, which answers "The weather in Tokyo is 22°C and sunny".
- The agent returns
("stop", "The weather in Tokyo is 22°C and sunny", [], [], [], [])to your contract.
On-chain tools (yield and resume)
On-chain tools are yielded back to the caller as ABI-encoded calldata. The caller executes them, then resumes the conversation with the results.
For example, with an on-chain DEX swap tool:
- Your contract calls
inferToolsChat(messages, [], onchainTools, 5, false). - The agent sends the prompt and tool definitions to the language model.
- The model returns a tool call:
swap(0xA0b8..., 1000). - The agent returns
("tool_calls", "", state, [callId], [0xd004f0f7...])to your contract. - Your contract executes the on-chain call
swap(0xA0b8..., 1000)on the DEX, which returns"success". - Your contract resumes with the tool result:
inferToolsChat(updatedState + toolResult, [], onchainTools, 5, false). - The agent passes the conversation and tool result to the model, which answers "Swapped 1000 USDC successfully".
- The agent returns
("stop", "Swapped 1000 USDC successfully", [], [], [], [])to your contract.
Resuming after on-chain tool calls
When finishReason == "tool_calls", the caller should:
- Execute each on-chain call using the calldata in
pendingToolCalls - Append the results to the conversation state:
- For each pending call, add
role: "tool"with a JSON message{"tool_call_id": pendingToolCallIds[i], "content": "result string"}
- For each pending call, add
- Call
inferToolsChatagain with the updated conversation
Deterministic execution
Because the models run deterministically across all validating nodes, consensus can be achieved on the output, making AI results trustworthy for on-chain use.
The same prompt goes to the LLM on every node in the subcommittee (Node 1, Node 2, Node 3). When their outputs match, the network accepts it as the verified response.
Example use cases
- Analyzing text content for moderation decisions
- Generating summaries of on-chain data
- Making classification decisions (sentiment, category, etc.)
- Creating dynamic NFT descriptions or game narratives
- Extracting numeric scores or ratings from text
- Agentic DeFi: LLM decides which swaps/transfers to execute and returns calldata
- AI oracles with tool use: LLM fetches external data via MCP servers before responding
Usage examples
Simple inference
const abi = [{
type: 'function',
name: 'inferString',
inputs: [
{ name: 'prompt', type: 'string' },
{ name: 'system', type: 'string' },
{ name: 'chainOfThought', type: 'bool' },
{ name: 'allowedValues', type: 'string[]' }
],
outputs: [{ name: 'response', type: 'string' }]
}];
// Content moderation example — constrain output to "safe" or "unsafe"
const calldata = encodeFunctionData({
abi,
functionName: 'inferString',
args: [
'Analyze the following text: "Check out this amazing new product!"',
'You are a content moderation assistant.',
false, // chainOfThought
['safe', 'unsafe'] // allowedValues
]
});
Numeric inference
const abi = [{
type: 'function',
name: 'inferNumber',
inputs: [
{ name: 'prompt', type: 'string' },
{ name: 'system', type: 'string' },
{ name: 'minValue', type: 'int256' },
{ name: 'maxValue', type: 'int256' },
{ name: 'chainOfThought', type: 'bool' }
],
outputs: [{ name: 'response', type: 'int256' }]
}];
// Sentiment score from 1-10
const calldata = encodeFunctionData({
abi,
functionName: 'inferNumber',
args: [
'Rate the sentiment of this review: "Absolutely loved it, best purchase ever!"',
'You are a sentiment analysis assistant. Rate sentiment from 1 (very negative) to 10 (very positive).',
1, // minValue
10, // maxValue
false // chainOfThought
]
});
Conversational example
const abi = [{
type: 'function',
name: 'inferChat',
inputs: [
{ name: 'roles', type: 'string[]' },
{ name: 'messages', type: 'string[]' },
{ name: 'chainOfThought', type: 'bool' }
],
outputs: [{ name: 'response', type: 'string' }]
}];
const calldata = encodeFunctionData({
abi,
functionName: 'inferChat',
args: [
['system', 'user', 'assistant', 'user'],
[
'You are a helpful coding assistant.',
'How do I reverse a string in JavaScript?',
'You can use: str.split("").reverse().join("")',
'Can you explain how that works step by step?'
],
false // chainOfThought
]
});
MCP tool use (Solidity)
The LLM automatically discovers and calls tools from the MCP server, gets the result, and incorporates it into its response.
interface ILLMAgent {
struct OnchainTool {
string signature;
string description;
}
function inferToolsChat(
string[] calldata roles,
string[] calldata messages,
string[] calldata mcpServerUrls,
OnchainTool[] calldata onchainTools,
uint256 maxIterations,
bool chainOfThought
) external returns (
string memory finishReason,
string memory response,
string[] memory updatedRoles,
string[] memory updatedMessages,
string[] memory pendingToolCallIds,
bytes[] memory pendingToolCalls
);
}
// Provide MCP server URLs — the agent discovers available tools automatically
string[] memory mcpServerUrls = new string[](1);
mcpServerUrls[0] = "http://weather-service:80/";
ILLMAgent.OnchainTool[] memory onchainTools = new ILLMAgent.OnchainTool[](0);
string[] memory roles = new string[](2);
roles[0] = "system";
roles[1] = "user";
string[] memory messages = new string[](2);
messages[0] = "You are a weather assistant. Use available tools when asked about weather.";
messages[1] = "What is the weather in Tokyo?";
bytes memory payload = abi.encodeWithSelector(
ILLMAgent.inferToolsChat.selector,
roles, messages, mcpServerUrls, onchainTools, 5, false
);
On-chain tool use with yield and resume (Solidity)
The LLM returns calldata for on-chain tools. Your contract executes them and resumes the conversation.
// Define on-chain tools
ILLMAgent.OnchainTool[] memory onchainTools = new ILLMAgent.OnchainTool[](1);
onchainTools[0] = ILLMAgent.OnchainTool(
"swap(address token, uint256 amount)",
"Swap tokens on the DEX"
);
// First call — LLM decides to use swap tool
(
string memory finishReason,
string memory response,
string[] memory updatedRoles,
string[] memory updatedMessages,
string[] memory pendingToolCallIds,
bytes[] memory pendingToolCalls
) = llmAgent.inferToolsChat(roles, messages, mcpServerUrls, onchainTools, 5, false);
if (keccak256(bytes(finishReason)) == keccak256("tool_calls")) {
// Execute each pending on-chain call
for (uint i = 0; i < pendingToolCalls.length; i++) {
// pendingToolCalls[i] is calldata: swap(0xA0b8..., 1000)
(bool success, bytes memory result) = dexContract.call(pendingToolCalls[i]);
// Append tool result to conversation for resumption
// ... add ("tool", '{"tool_call_id":"...","content":"success"}') to updatedRoles/updatedMessages
}
// Resume — call inferToolsChat again with updated conversation
// The LLM will see the tool results and produce a final response
}