Create a chat completion
Changed onCreates 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)})
Request
- Base URLhttps://api.textcortex.com/v1
- AuthHTTP bearer
Request body
Example request
{
"model": "gpt-6-luna",
"reasoning_effort": "high"
}Response
Chat completion response
Changes
- breaking
the
////request property type changed fromobjectto no type - breaking
added
subschema #1subschema #2to the/////response propertyoneOflist for the response status - breaking
the
/////response's property type changed fromobjectto 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 #1subschema #2to the////request propertyoneOflist
- breaking
- breaking
request property
was restricted to a list of enum values - info
added the new
highenum value to the request property - info
added the new
lowenum value to the request property - info
added the new
mediumenum value to the request property - info
added the new
noneenum value to the request property
- breaking