For the complete documentation index, see llms.txt. This page is also available as Markdown.

Run chat completions

Run chat completions, the unified response envelope, tokenize, and count_tokens over the Algenta LLM API — with streaming, from curl, Python, TypeScript, or the CLI.

The Algenta LLM API exposes an OpenAI-shaped surface for text generation and token accounting. This guide covers the four request-side routes: POST /v1/chat/completions (a chat transcript in, an assistant message out), POST /v1/responses (a unified envelope over the same models), and POST /v1/tokenize / POST /v1/count_tokens (deterministic token accounting). Chat and responses both support streaming.

Every route names a model id. Use a built-in utility model for deterministic behavior, or a provider-backed id for a real LLM — see LLM models & providers.

Before you start

You need:

  • An API key sent as Authorization: Bearer $ALGENTA_API_KEY. See Authentication.

  • $ALGENTA_API_URL — https://api.algenta.ai (hosted) or your engine, e.g. http://localhost:8000.

  • A model id from GET /v1/models. The examples use provider.openai:gpt-4o; substitute any chat-capable id you see in the catalog (the built-in text.tokenizer also serves chat deterministically).

Chat completions

Send an ordered messages transcript. Each message has a role (system, user, assistant, developer, or tool) and content.

curl -sS -X POST "$ALGENTA_API_URL/v1/chat/completions" \
  -H "Authorization: Bearer $ALGENTA_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "provider.openai:gpt-4o",
    "messages": [
      {"role": "system", "content": "You are concise."},
      {"role": "user", "content": "Summarize the CAP theorem in one sentence."}
    ]
  }'
import os
from decision_engine import AlgentaClient

client = AlgentaClient(
    api_key=os.environ["ALGENTA_API_KEY"],
    base_url=os.environ.get("ALGENTA_API_URL", "https://api.algenta.ai"),
)

result = client.chat_completions(
    [
        {"role": "system", "content": "You are concise."},
        {"role": "user", "content": "Summarize the CAP theorem in one sentence."},
    ],
    model="provider.openai:gpt-4o",
)
print(result.choices[0].message.content)

The 200 response is a chat.completion: one assistant choices[0].message.content, plus usage and — for provider-routed models — which backend served it.

Stream the completion

Set stream: true to receive Server-Sent Events instead of one JSON body. The stream emits chat.completion.chunk events with incremental delta.content, then a final data: [DONE].

Tool calling

Pass tools (a list of function definitions) on a provider-backed model and the assistant message can answer with tool_calls instead of (or alongside) content. finish_reason is "tool_calls" whenever the model called one; a request that never sets tools keeps returning finish_reason: "stop" exactly as before. tool_choice ("auto", "required", "none", or {"type": "function", "function": {"name": "..."}}) and parallel_tool_calls are optional. Non-streaming only for now — pass stream: true together with tools and the engine returns 422 model_capability_unsupported rather than silently dropping the tool call from the SSE stream.

The assistant message carries tool_calls instead of (or alongside) text, and the arguments are always a JSON-encoded string (function.arguments), matching the OpenAI wire shape regardless of which backend actually served the request:

To continue the conversation, append the assistant's tool_calls message and a role: "tool" message carrying the result (tool_call_id must match the call it answers):

Per-backend tool-calling support

Backend

Tools

tool_choice

openai, openai_compatible, groq, xai, mistral

Full — forwarded verbatim

"auto", "required", "none", or a named-function choice

anthropic

Full — translated to Claude's tool_use/tool_result blocks

Same set, translated to Claude's tool_choice

google_genai

Full — translated to functionDeclarations; Gemini assigns no call id, so the engine synthesizes one

Same set, translated to functionCallingConfig

cohere

Full

"auto" and "required"/"none" only — forcing one named function returns 422 model_capability_unsupported (Cohere's Chat v2 API has no per-function forcing)

ollama

Full

"auto" only — forcing "required"/"none"/a named function returns 422 model_capability_unsupported (/api/chat has no request-side forcing knob)

algenta_local

Supported on a checkpoint with a chat template (rendered into the Qwen3 template's tools preamble; <tool_call> blocks in the generation are parsed back into structured tool_calls) — refuses 422 model_capability_unsupported on a checkpoint with no chat template

"auto" (default) and "none" only — the template's preamble is a hint the model may act on, not a mechanism that forces one, so "required"/a named-function choice return 422 model_capability_unsupported

text.tokenizer

Not supported — 422 model_capability_unsupported

—

The unified response envelope

POST /v1/responses runs the same models behind one envelope. The response carries output items and usage, and supports stream: true. input accepts either of two shapes, which are never mixed:

  • Legacy shape — a single string, or a list of strings. Each string is an INDEPENDENT single-turn request against the model; a list of N strings makes N separate completions in one call, not a conversation. This is unchanged from before and is what every example below without tools/previous_response_id uses.

  • Typed shape — a list of OpenResponses-style items, each an object with a type: message ({"type": "message", "role": "user"|"assistant"|"system"|"developer", "content": "..."}), function_call (an assistant tool call, mirroring tool_calls on a Chat Completions message), or function_call_output (a tool's result, mirroring a role: "tool" message). A typed array is processed as ONE multi-turn conversation, exactly like a Chat Completions messages list. The engine classifies a list as typed the moment every item is an object with a type key; anything else falls back to the legacy shape.

The 200 response is a response object with status: "completed", an output array, and usage.

Tool calling and multi-turn conversations

Pass tools/tool_choice/parallel_tool_calls together with the typed input-array shape to run a real multi-turn conversation with tool calling — the same knobs, translation, and per-backend support matrix as chat completions' tool calling above. A tool-call-only turn returns content[0].text: null alongside a populated content[0].tool_calls and finish_reason: "tool_calls". As with chat completions, stream: true together with a turn that calls a tool returns 422 model_capability_unsupported rather than dropping the tool call from the SSE stream.

To continue the conversation, send only the NEW turn's items (a function_call_output answering the prior tool call, or a new message) and set previous_response_id to the id from the prior call — the engine prepends that call's stored history for you:

Tokenize and count tokens

POST /v1/tokenize and POST /v1/count_tokens run a tokenizer model deterministically. They default to text.tokenizer; pass any tokenizer-capable model.

/v1/tokenize returns { model, tokenizer_kind, tokens, token_count }; /v1/count_tokens returns { model, tokenizer_kind, token_count }.

Troubleshooting

404 model_not_supported

The model id is not in GET /v1/models. List the catalog and copy an exact id. For a provider model, confirm the backend is configured and its key is present (provider_auth_configured: true). See LLM models & providers.

422 model_capability_unsupported

The model exists but does not support this route — for example calling /v1/chat/completions with an embeddings-only model. Check the descriptor's capabilities and supported_endpoints.

429, 502, 503, or 504

429 is a rate limit or a provider rate limit (provider_rate_limited); back off and retry. 502/503 (backend_unavailable) and 504 (provider_timeout) come from the upstream provider — retry, and for router models the engine may fail over to the next target automatically. Error bodies follow { "error": { "code", "message", "details", "request_id" } }; see Error codes.

Next steps

Last updated

Was this helpful?