Agents

An agent gives the model tools it may call. Your tools run in your service, never in the API, so the conversation goes back and forth until the model has what it needs. The SDK runs that loop for you.

Run an agent

Define each tool with its JSON Schema and a run function, then call llm.runAgent. The SDK sends the conversation, runs the tools the model asks for, sends the results back, and repeats until the model answers or a brake stops it.

Agent

import { createClient, defineTool } from '@vovix/llm'

const llm = createClient({ baseUrl: 'https://api.llm.vovix.io/v1', apiKey: process.env.LLM_API_KEY! })

const getWeather = defineTool<{ city: string }>({
  name: 'get_weather',
  description: 'Current weather for a city',
  schema: {
    type: 'object',
    properties: { city: { type: 'string' } },
    required: ['city'],
  },
  run: async ({ city }) => weatherService.current(city),
})

const result = await llm.runAgent({
  model: 'openai.gpt-oss-120b',
  messages: [{ role: 'user', content: 'Should I bring an umbrella in Tokyo today?' }],
  tools: [getWeather],
  maxTokens: 600,
  maxTurns: 6,
  maxUsd: 0.05,
})

console.log(result.status, result.text)

Tool

  • Name
    name
    Type
    string
    Description

    snake_case. The name the model calls.

  • Name
    description
    Type
    string
    Description

    What the tool does, up to 1000 characters. The model reads it to decide when to call the tool.

  • Name
    schema
    Type
    object
    Description

    JSON Schema for the arguments, with a top-level type: "object".

  • Name
    parse
    Type
    function
    Description

    Optional. Validate or clamp the arguments before run — for example zodSchema.parse. The model chooses the arguments, and they can be wrong.

  • Name
    run
    Type
    function
    Description

    Your implementation. Its return value is sent to the model as JSON; a string is sent as-is.

Only name, description and schema leave your service. parse and run stay local.

Options

runAgent accepts every attribute of Chat, plus:

  • Name
    tools
    Type
    array
    Description

    One to 64 tools.

  • Name
    maxTurns
    Type
    integer
    Description

    Most model turns for the whole run. Default 8. Stops a model that keeps calling tools.

  • Name
    maxUsd
    Type
    number
    Description

    Stop once the run has spent this much, summed over every turn. Checked after each turn, so the last turn can go over. Each turn resends the whole conversation, so cost grows quickly with turns.

  • Name
    onStep
    Type
    function
    Description

    Called after each turn with that turn's step, once its tools have run — for logging or progress.

Result

  • Name
    status
    Type
    string
    Description

    done — the model answered. max_turns or max_usd — a brake stopped the run.

  • Name
    text
    Type
    string
    Description

    The model's text from the last turn.

  • Name
    messages
    Type
    array
    Description

    The whole conversation, tool calls and results included. Store it to continue the conversation later.

  • Name
    steps
    Type
    array
    Description

    One entry per turn: turn, text, toolRuns (each name, input, ok, output), usage, costUsd, latencyMs, requestId.

  • Name
    turns
    Type
    integer
    Description

    Model turns made.

  • Name
    usage
    Type
    object
    Description

    Tokens summed across every turn.

  • Name
    costUsd
    Type
    number
    Description

    Cost summed across every turn. Absent if any turn used a model without a catalog price.


Without the SDK

Each request to the endpoint is one model turn. The API keeps no session state, and it never runs tools, so a client in another language drives the loop itself:

  1. Call POST /v1/agents/run with the conversation and your tool definitions.
  2. If status is done, the model answered — stop.
  3. If status is pending_tools, run each call in pendingToolCalls, append one tool message per call to the returned messages, and call again with that array.

Stop after a fixed number of turns of your own: nothing in a single request limits how often the model calls tools across requests.

POST/v1/agents/run

Run a turn

Accepts every attribute of Chat, plus tools.

Required attributes

  • Name
    model
    Type
    string
    Description

    A model id from the catalog.

  • Name
    messages
    Type
    array
    Description

    The conversation so far, including earlier tool calls and their tool results.

  • Name
    maxTokens
    Type
    integer
    Description

    Up to 32000.

  • Name
    tools
    Type
    array
    Description

    One to 64 tools. Each has a snake_case name, an optional description (up to 1000 characters) and a JSON Schema schema for its arguments.

Optional attributes

  • Name
    maxTurns
    Type
    integer
    Description

    Accepted, 1 to 16, but it never triggers here: a request makes exactly one model call. Limit turns in your loop, or use runAgent.

  • Name
    maxUsd
    Type
    number
    Description

    Accepted, up to 10, but it never triggers here for the same reason. Your service's daily budget still applies.

Request

POST
/v1/agents/run
curl https://api.llm.vovix.io/v1/agents/run \
  -H "x-api-key: $LLM_API_KEY" \
  -H "content-type: application/json" \
  -d '{
    "model": "openai.gpt-oss-120b",
    "messages": [
      { "role": "user", "content": "Should I bring an umbrella in Tokyo today?" }
    ],
    "tools": [
      {
        "name": "get_weather",
        "description": "Current weather for a city",
        "schema": {
          "type": "object",
          "properties": { "city": { "type": "string" } },
          "required": ["city"]
        }
      }
    ],
    "maxTokens": 600
  }'

Response

{
  "status": "pending_tools",
  "text": "",
  "messages": [
    { "role": "user", "content": "Should I bring an umbrella in Tokyo today?" },
    {
      "role": "assistant",
      "content": null,
      "tool_calls": [
        {
          "id": "call_0",
          "type": "function",
          "function": { "name": "get_weather", "arguments": "{\"city\":\"Tokyo\"}" }
        }
      ]
    }
  ],
  "pendingToolCalls": [
    {
      "id": "call_0",
      "type": "function",
      "function": { "name": "get_weather", "arguments": "{\"city\":\"Tokyo\"}" }
    }
  ],
  "steps": [{ "turn": 1, "text": "", "toolRuns": [], "latencyMs": 1240 }],
  "turns": 1,
  "usage": { "inputTokens": 142, "outputTokens": 38 },
  "costUsd": 0.0000441
}

Response

  • Name
    status
    Type
    string
    Description

    done — the model answered. pending_tools — run pendingToolCalls and call again.

  • Name
    text
    Type
    string
    Description

    The model's text from this turn. It can be non-empty next to pendingToolCalls — some models say what they are about to do.

  • Name
    messages
    Type
    array
    Description

    The full conversation, including the assistant message with its tool_calls. Append your tool results and send it back.

  • Name
    pendingToolCalls
    Type
    array
    Description

    Tool calls to run. Each has an id, and a function with a name and JSON-encoded arguments.

  • Name
    steps
    Type
    array
    Description

    One entry for this turn: turn, text, toolRuns (always empty here), usage, costUsd, latencyMs, requestId.

  • Name
    turns
    Type
    integer
    Description

    Always 1.

  • Name
    usage
    Type
    object
    Description

    Tokens for this turn.

  • Name
    costUsd
    Type
    number
    Description

    Cost of this turn.

Was this page helpful?