Chat Completions

Create a chat completion

Changed on

Creates a chat completion using an OpenAI-compatible request body. Use model ids returned by GET /models, and set reasoning_effort to control how much the model reasons before answering.

A -thinking or -non-thinking id that names a variant of another model — claude-5-sonnet-thinking beside claude-5-sonnet, kimi-k2p7-code-non-thinking beside kimi-k2p7-code — is deprecated and is subject to deployment-specific compatibility settings. The announced sunset target was 2026-09-09; do not rely on these aliases being accepted. The reasoning-only minimax-m3-thinking model is the exception: it is the whole model rather than a variant of one, so it is not deprecated and has no base id to move to. Use the base model id and set reasoning_effort instead.

PDF/file attachments, top-level text and augment are not supported. Query /api-platform/knowledge-bases/{kb_id}/query separately and include retrieved excerpts in messages.

Messages and model selection

Choose a chat model from GET /models. Send messages, not the legacy text or augment fields. Include previous turns yourself for a continuing conversation. For image understanding, use a vision-capable model and a content array containing text and image_url parts. This is different from generating an image. The model-list response does not expose support flags. Use the capability table on GET /models for the standard deployment.

Structured output

Ask for a table or bullet list in your messages for human-readable formatting. Use response_format when your application needs machine-readable JSON.

For models supporting JSON mode, send response_format: {"type":"json_object"} and explicitly ask for JSON in the messages. JSON mode does not enforce your own schema. Models supporting structured output can accept json_schema with json_schema.name, schema and strict. Support and schema restrictions vary by model (see the table on GET /models); validate the returned JSON in your application. A field being accepted by this endpoint does not guarantee every model honors it.

Function calls

Supply function definitions in tools. If the assistant returns tool_calls, append that assistant message, execute the functions in your application, then append a role: "tool" message for each call with the matching tool_call_id and string content. Send the updated history again to obtain the answer. The API does not execute your functions. Web search and KB retrieval are separate endpoints; passing a tool definition does not connect them automatically. Likewise, web_search_options is a provider-dependent chat parameter, not a request to run POST /web/search. Public audio and embeddings endpoints are not available; internal embedding use for retrieval does not expose one.

Reasoning

Use reasoning_effort (none, low, medium, high) when supported, or omit it for the model default. Some models ignore this field and use reasoning instead; others have fixed reasoning behavior. none therefore does not universally disable reasoning. See the parameter descriptions before selecting a control.

Streaming

Set stream: true and parse server-sent events (text/event-stream). Append choices[].delta.content; streamed tool-call arguments also arrive in pieces. A chunk can carry a role or finish reason without text. Finish reasons belong to choices; data: [DONE] terminates the stream. To request final usage, set stream_options: {"include_usage":true} and handle a usage chunk with no choices. Handle errors before the stream as HTTP errors and errors after it starts as stream payloads. Do not treat a disconnected stream as a completed answer.

Python: stream a text answer

Use Python 3.9+ and install requests (python -m pip install requests). Set TEXTCORTEX_API_KEY and TEXTCORTEX_MODEL in your environment; choose a model from GET /models. Save and run each example as a separate Python script. These calls use your account's funding. Network/read timeouts raise exceptions; there are no automatic retries. Already printed text is provisional until the completion checks pass. A read timeout is not an overall generation deadline.

import json
import os
import requests

headers = {"Authorization": f"Bearer {os.environ['TEXTCORTEX_API_KEY']}"}
model = os.environ["TEXTCORTEX_MODEL"]  # Choose an ID from GET /models.
base = "https://api.textcortex.com/v1"


def events(response):
    """Decode SSE frames, including comments and multiline data."""
    response.encoding = "utf-8"
    data = []
    for line in response.iter_lines(decode_unicode=True):
        if line == "":
            if data:
                yield "\n".join(data)
                data = []
        elif line.startswith("data:"):
            data.append(line[5:].removeprefix(" "))
    if data:
        raise RuntimeError("Stream ended inside an event")

payload = {
    "model": model,
    "messages": [{"role": "user", "content": "Explain an API in one sentence."}],
    "stream": True,
    "stream_options": {"include_usage": True},
}
finished = None
done = False
with requests.post(base + "/chat/completions", headers=headers,
                   json=payload, stream=True, timeout=(10, 120)) as response:
    response.raise_for_status()  # HTTP errors occur before streaming starts.
    for data in events(response):
        if data == "[DONE]":
            done = True
            break
        chunk = json.loads(data)
        if "error" in chunk:
            raise RuntimeError(chunk["error"])
        if chunk.get("usage"):
            print("\nUsage:", chunk["usage"])
        for choice in chunk.get("choices", []):
            if choice.get("index", 0) != 0:
                continue
            delta = choice.get("delta", {})
            if delta.get("tool_calls"):
                raise RuntimeError("This text-only example does not execute streamed tool calls")
            print(delta.get("content") or "", end="", flush=True)
            if choice.get("finish_reason") is not None:
                finished = choice["finish_reason"]
if not done or finished != "stop":
    raise RuntimeError(f"Incomplete answer: DONE={done}, finish_reason={finished}")
print()

This example handles role-only chunks, usage-only chunks, in-stream errors, and missing completion markers. length is an incomplete answer. For streamed function calls, accumulate arguments by tool-call index before parsing them; the non-streaming example below avoids partial arguments.

Python: execute a function and return its result

The API proposes calls; your application validates and executes them. This read-only demo handles multiple calls and returns one result per call ID. The model can also answer without calling a function. For functions that write data, add authorization and duplicate-execution protection.

import json
import os
import requests

base = "https://api.textcortex.com/v1"
messages = [{"role": "user", "content": "Look up the delivery status of order 123."}]
tools = [{"type": "function", "function": {
    "name": "lookup_order", "description": "Read an order's delivery status",
    "parameters": {"type": "object", "properties": {"order_id": {"type": "string"}},
                   "required": ["order_id"], "additionalProperties": False},
}}]

with requests.Session() as client:
    client.headers["Authorization"] = f"Bearer {os.environ['TEXTCORTEX_API_KEY']}"
    # This example's turn limit prevents an unbounded loop of billable requests.
    max_requests = 3
    for request_index in range(max_requests):
        response = client.post(base + "/chat/completions", json={
            "model": os.environ["TEXTCORTEX_MODEL"], "messages": messages, "tools": tools,
        }, timeout=120)
        response.raise_for_status()
        choice = response.json()["choices"][0]
        message = choice["message"]
        calls = message.get("tool_calls") or []
        if not calls:
            if choice["finish_reason"] != "stop":
                raise RuntimeError(f"Incomplete answer: {choice['finish_reason']}")
            print(message.get("content"))
            break
        if choice["finish_reason"] != "tool_calls":
            raise RuntimeError("Tool call was not completed; do not execute partial arguments")
        if request_index == max_requests - 1:
            raise RuntimeError("Tool round-trip limit reached; final tool batch was not executed")
        messages.append({"role": "assistant", "content": message.get("content"), "tool_calls": calls})
        for call in calls:
            function = call["function"]
            # Never eval model output or dispatch arbitrary function names.
            try:
                args = json.loads(function["arguments"])
                if function["name"] != "lookup_order" or not isinstance(args, dict):
                    raise ValueError("Unsupported function")
                if set(args) != {"order_id"} or not isinstance(args["order_id"], str):
                    raise ValueError("Expected a string order_id")
                # Local demo data. A real lookup must enforce the caller's permissions.
                result = {"order_id": args["order_id"],
                          "status": "shipped" if args["order_id"] == "123" else "not_found"}
            except (ValueError, TypeError) as error:
                result = {"error": str(error)}
            messages.append({"role": "tool", "tool_call_id": call["id"], "content": json.dumps(result)})
post/chat/completions

Request

  • Base URL
    https://api.textcortex.com/v1
  • Auth
    HTTP bearer

Request body

audioobject nullable
frequency_penaltynumber nullable
functionsobject[] nullable
logit_biasobject nullable
logprobsboolean nullable
max_completion_tokensinteger nullable
max_tokensinteger nullable
metadataobject nullable
modalitiesstring[] nullable
modelstring required

Model id returned by GET /models. A -thinking or -non-thinking id that names a variant of another model — claude-5-sonnet-thinking beside claude-5-sonnet, kimi-k2p7-code-non-thinking beside kimi-k2p7-code — is deprecated and is subject to deployment-specific compatibility settings. The announced sunset target was 2026-09-09; do not rely on these aliases being accepted. The reasoning-only minimax-m3-thinking model is the exception: it is the whole model rather than a variant of one, so it is not deprecated and has no base id to move to. Use the base model id and set reasoning_effort instead.

ninteger nullable
parallel_tool_callsboolean nullable
predictionobject nullable
presence_penaltynumber nullable
prompt_cache_keystring nullable
prompt_cache_retentionstring nullable
reasoningobject nullable
reasoning_effort'none' | 'low' | 'medium' | 'high' nullable

How much reasoning the model should spend before answering, for models that grade it. none turns reasoning off for those; ones that only switch reasoning on or off treat every active level the same. Omit to use the model's default.

Ignored rather than rejected by models that do not take it, so sending it is always safe but does not always have an effect. Models that take reasoning instead ignore this field entirely, none included, so none will not turn their reasoning off. Which applies is documented per model.

response_formatobject nullable
safety_identifierstring nullable
seedinteger nullable
service_tierstring nullable
storeboolean nullable
streamboolean nullable
temperaturenumber nullable
toolsobject[] nullable
top_logprobsinteger nullable
top_pnumber nullable
userstring nullable
verbositystring nullable
web_search_optionsobject nullable

Example request

{
  "model": "gpt-6-luna",
  "reasoning_effort": "high"
}

Response

Chat completion response

createdinteger required
idstring required
modelstring required
objectstring required

Changes

    • breaking

      the //// request property type changed from object to no type

    • breaking

      added subschema #1 subschema #2 to the ///// response property oneOf list for the response status

    • breaking

      the ///// response's property type changed from object to no type for status

    • breaking

      removed the required property ////// from the response with the status

    • warning

      removed the request property /////

    • warning

      removed the request property /////

    • warning

      removed the request property /////

    • warning

      removed the optional property ////// from the response with the status

    • warning

      removed the optional property ////// from the response with the status

    • info

      added subschema #1 subschema #2 to the //// request property oneOf list

    • breaking

      request property was restricted to a list of enum values

    • info

      added the new high enum value to the request property

    • info

      added the new low enum value to the request property

    • info

      added the new medium enum value to the request property

    • info

      added the new none enum value to the request property