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).

TypeScript
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.

TypeScript
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:

TypeScript
// 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.

TypeScript
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>;
TypeScript
// 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.

  1. The client posts your messages to /v1/graph/invoke.
  2. If any response message holds a remote_tool_call block, the client passes those messages to its tool executor, which calls the handlers you registered with client.registerToolHandler().
  3. The tool result messages are added to all_messages and posted to the same endpoint as the next request.
  4. Steps 2 and 3 repeat until a response has no remote tool calls or recursion_limit is 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.

TypeScript
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.

TypeScript
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.

TypeScript
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 turn

Seed the graph with initial state

initial_state sets state keys before the run. Use keys that exist in your graph’s state schema.

TypeScript
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.

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.
Last updated for v0.10.0Edit this page on GitHubReport an issue