invoke()
In shortReference for TenxGraphClient.invoke(): send messages to the agent graph, run remote tools automatically, and get the final result.
- 6 min read
- 11 sections
- Updated
- v0.10.0
- Markdown
client.invoke() sends messages to the agent graph and resolves with the final result. If the agent asks for a remote tool, the client runs your registered handler and calls the server again, repeating until the agent answers or recursion_limit is reached. For incremental output, use stream().
Endpoint: POST /v1/graph/invoke
Signature
invoke() takes an array of Message objects and an optional options object, and returns a promise of InvokeResult. It is a method of TenxGraphClient, so create a client first (see Client).
import { TenxGraphClient, Message } from '10xgraph-client';
// client.invoke(messages, options?) => Promise<InvokeResult>
invoke(
messages: Message[],
options?: {
initial_state?: Record<string, any>;
config?: Record<string, any>;
recursion_limit?: number;
response_granularity?: 'full' | 'partial' | 'low';
onPartialResult?: InvokeCallback;
}
): Promise<InvokeResult>Parameters
The first parameter carries the conversation input and the second tunes the run. Only messages is required.
messages
messages is an array of Message objects. The last one is normally the user’s input, and you can add a system message before it. Each message is serialised before sending. If message_id is null or undefined, the payload uses "0" and the server assigns a real id.
import { Message } from '10xgraph-client';
// A system message sets the persona, the user message is the input
const systemMsg = Message.text_message('You are a concise assistant.', 'system');
const userMsg = Message.text_message('What is 2 + 2?');options
All options are optional. The defaults below are the values the client applies when you omit the option.
| Option | Type | Default | Description |
|---|---|---|---|
initial_state |
Record<string, any> |
none | State values to seed the graph. Sent only on the first request of a call, not on the follow-up requests that return tool results. |
config |
Record<string, any> |
none | Run configuration. Put thread_id at the top level of this object to keep and checkpoint a conversation. |
recursion_limit |
number |
25 |
Maximum number of requests the client makes in one invoke() call. The server accepts values from 1 to 100. |
response_granularity |
'full' | 'partial' | 'low' |
'full' |
How much data the server returns per request. See Response granularity. |
onPartialResult |
InvokeCallback |
none | Called after every request with that iteration’s results. |
Return value
invoke() resolves with an InvokeResult. The messages, state, context, summary and meta fields come from the last server response. all_messages collects everything across iterations.
| Field | Type | Description |
|---|---|---|
messages |
Message[] |
Messages from the last response. |
state |
AgentState (optional) |
Graph state, when the granularity includes it. |
context |
Message[] (optional) |
Context messages, when the granularity includes them. |
summary |
string | null (optional) |
Conversation summary, when the granularity includes it. |
meta |
InvokeMetadata |
thread_id (string) and is_new_thread (boolean). |
all_messages |
Message[] |
Every message from every iteration, including your tool results. |
iterations |
number |
How many requests were made. |
recursion_limit_reached |
boolean |
true when iterations reached recursion_limit. |
recursion_limit_reached is computed from the iteration count, so it is also true when the final answer arrived exactly on the last allowed iteration. Check the last message for pending remote tool calls if you need to tell the two cases apart.
Use meta.thread_id to continue a conversation in a later call:
// First turn: the thread is created with the id you choose
const first = await client.invoke([Message.text_message('Hi')], {
config: { thread_id: 'conv-001' },
});
// Second turn: reuse the id reported by the server
const followUp = await client.invoke([Message.text_message('Tell me more')], {
config: { thread_id: first.meta.thread_id },
});Response granularity
response_granularity decides which parts of the graph output the server includes. Messages are always returned; the rest depends on the value.
| Value | Returns | When to use |
|---|---|---|
'full' |
Messages and full state | Debugging, or when your UI reads graph state. |
'partial' |
Messages, context and summary | When you need context or the summary but not the full state. |
'low' |
Messages only | Chat UIs that only render messages. Smallest payload. |
The client sends 'full' when you omit the option. The server’s own default is low, but a call through TenxGraphClient never relies on it.
Progress callback
onPartialResult receives an InvokePartialResult after each request, before any tool handlers run. It is the way to show progress during a multi-step run without switching to streaming. The client awaits the callback, so an async callback delays the next iteration.
interface InvokePartialResult {
iteration: number;
messages: Message[];
state?: AgentState;
context?: Message[];
summary?: string | null;
meta: InvokeMetadata;
has_tool_calls: boolean; // the response contains remote tool calls
is_final: boolean; // no remote tool calls, this is the last iteration
}
type InvokeCallback = (partial: InvokePartialResult) => void | Promise<void>;// Log progress while the agent works
const result = await client.invoke([Message.text_message('Weather in Paris?')], {
onPartialResult(partial) {
if (partial.has_tool_calls) {
console.log(`Iteration ${partial.iteration}: running tools`);
}
if (partial.is_final) {
console.log('Final answer received');
}
},
});Remote tool call loop
The client resolves remote tool calls for you. When a response contains a remote_tool_call content block and a ToolExecutor is available (it is, on every TenxGraphClient), the client runs the matching handler and sends the results back.
- The client posts your messages to
/v1/graph/invoke. - If any response message holds a
remote_tool_callblock, the client passes those messages to its tool executor, which calls the handlers you registered withclient.registerToolHandler(). - The tool result messages are added to
all_messagesand posted to the same endpoint as the next request. - Steps 2 and 3 repeat until a response has no remote tool calls or
recursion_limitis reached.
Only the first request carries initial_state. See Register remote tools for how to register handlers.
InvokeRequest
InvokeRequest is the body type of one request. You only need it if you call the endpoint yourself. The client adds messages in serialised form and fills the other fields from your options.
interface InvokeRequest {
messages: any[]; // serialised Message objects
initial_state?: Record<string, any>;
config?: Record<string, any>;
recursion_limit?: number;
response_granularity?: 'full' | 'partial' | 'low';
}Examples
The examples assume a server running at http://localhost:8000.
Minimal invoke
This call sends one user message and prints the first content block of the first returned message.
import { TenxGraphClient, Message } from '10xgraph-client';
const client = new TenxGraphClient({ baseUrl: 'http://localhost:8000' });
const result = await client.invoke([
Message.text_message('What is the capital of Japan?'),
]);
// Example output, the text depends on your agent
console.log(result.messages[0].content[0]);
// { type: 'text', text: 'The capital of Japan is Tokyo.' }Chat on a persistent thread
Reusing one thread_id makes the server load the saved state, so the agent remembers earlier turns. This needs a checkpointer on the server.
import { TenxGraphClient, Message } from '10xgraph-client';
const client = new TenxGraphClient({ baseUrl: 'http://localhost:8000' });
const THREAD_ID = 'user-123-session-456';
async function chat(userInput: string): Promise<string> {
const result = await client.invoke([Message.text_message(userInput)], {
config: { thread_id: THREAD_ID },
response_granularity: 'low', // messages only
});
// Pick the assistant's text block out of the returned messages
const assistantMsg = result.messages.find((m) => m.role === 'assistant');
const textBlock = assistantMsg?.content.find((b) => b.type === 'text');
return (textBlock as { text?: string } | undefined)?.text ?? '';
}
await chat('Hello!');
console.log(await chat('What did I just say?')); // sees the previous turnSeed the graph with initial state
initial_state sets state keys before the run. Use keys that exist in your graph’s state schema.
const result = await client.invoke([Message.text_message('Continue where we left off')], {
initial_state: { user_preferences: { language: 'en', theme: 'dark' } },
config: { thread_id: 'thread-789' },
});Common errors
A non-2xx response throws a subclass of TenxGraphError, chosen by status code. See Errors for the full list and the fields on each error.
| Error | Cause | Fix |
|---|---|---|
AuthenticationError (401) |
Missing or invalid credentials. | Set authToken or auth in the client config. |
ValidationError (422) |
The server rejected the payload, for example an empty messages array. |
Send at least one message and check config. |
ServerError (500, 502, 503, 504) |
The graph failed or the server is unavailable. | Check the server logs, then retry. |
Error: Request timeout after <n>ms |
One request exceeded the client timeout (default 300000 ms). |
Raise timeout in the client config or shorten the graph. |
recursion_limit_reached: true |
The loop used all allowed iterations. | Raise recursion_limit (maximum 100), or check that tools return and do not loop. |
Related pages
- Invoke an agent walks through using
invoke()in an app. - Register remote tools explains handlers for the tool loop.
stream()returns output incrementally.
Frequently asked questions
- When should I use invoke() instead of stream()?
- Use invoke() when you need the complete answer before updating your UI or returning from a function. Use stream() when you want tokens or messages as they are produced.
- How do I continue a conversation across invoke() calls?
- Pass the same thread_id at the top level of the config option on every call. You can read the id used by the server from result.meta.thread_id.
- Why does invoke() make more than one HTTP request?
- When the agent asks for a remote tool, the client runs your registered handler locally and sends the result back in a new request. This repeats until no remote tool calls remain or recursion_limit is reached.