Skip to main content

Inference REST API

Serenity Star's self-hosted inference models can be invoked through OpenAI-compatible endpoints, so you can talk to them with the same request/response shapes and the same official OpenAI SDKs you already use for OpenAI.

These endpoints expose the models directly — not agents. There is no configured agent in front of the model, and no system prompt, instructions, knowledge, plugins, or other AIHub configuration shaping the call. The response depends purely on the model you select and the request you send (your messages, tools, and parameters).

Two OpenAI APIs are supported:

APIEndpoint
Chat CompletionsPOST /api/v2/inference/{model}/chat/completions
ResponsesPOST /api/v2/inference/{model}/responses

Both endpoints support:

  • Streaming and non-streaming responses ("stream": true / false). See Streaming for the event formats.
  • Client tools — standard OpenAI function calling, with the tool executed on your side.
Base URL and authentication

All examples use https://api.serenitystar.ai as the base URL and a placeholder YOUR_API_KEY.

Authenticate with your API key using either:

  • the dedicated header — X-API-KEY: YOUR_API_KEY, or
  • a bearer token — Authorization: Bearer YOUR_API_KEY.

Because the bearer form is accepted, the official OpenAI SDKs work out of the box — just set the SDK's api_key and point base_url at the inference endpoint. The API key (or user) must be authorized to run AI services.

Selecting the model

Inference models are self-hosted — they run inside Serenity Star's own infrastructure through an OpenAI-compatible layer. Unlike AIProxy, there is no agent in front of the model: each call goes straight to the model and is billed as a metered AI-service execution.

The model is selected by the URL path, not by the model field in the request body:

/api/v2/inference/{model}/chat/completions
/api/v2/inference/{model}/responses

{model} must be one of the inference models enabled on your instance (for example qwen/qwen3.6, qwen/qwen3.6-35b-a3b, or qwen/qwen3.6-27b). If you also send a model field in the body — the OpenAI SDKs require one — it is ignored; the path always wins. Set it to the same value to keep things clear.

Using an OpenAI SDK

Point the SDK's base_url at the model path (for example https://api.serenitystar.ai/api/v2/inference/qwen/qwen3.6) and the SDK appends /chat/completions or /responses for you.


Chat Completions

POST /api/v2/inference/{model}/chat/completions

Uses the OpenAI Chat Completions shape: a messages array, max_completion_tokens, stream, and an optional tools array.

Simple request

cURL
curl https://api.serenitystar.ai/api/v2/inference/qwen/qwen3.6/chat/completions \
-H "Content-Type: application/json" \
-H "X-API-KEY: YOUR_API_KEY" \
-d '{
"model": "qwen/qwen3.6",
"stream": false,
"max_completion_tokens": 5000,
"messages": [
{ "role": "user", "content": "Hello! Tell me a fun fact about Valencia." }
]
}'

Set "stream": true to receive chat.completion.chunk events over SSE instead of a single response.

Tools

Client tools are standard OpenAI function calling: the model decides to call the function, you execute it on your side, and you send the result back for the model to complete its answer. In Chat Completions, tools use the nested function shape.

Step 1 — request that triggers the tool call.

cURL
curl https://api.serenitystar.ai/api/v2/inference/qwen/qwen3.6/chat/completions \
-H "Content-Type: application/json" \
-H "X-API-KEY: YOUR_API_KEY" \
-d '{
"model": "qwen/qwen3.6",
"stream": false,
"max_completion_tokens": 512,
"messages": [
{ "role": "user", "content": "What is the weather like in Paris right now? Use the tool." }
],
"tools": [
{
"type": "function",
"function": {
"name": "get_weather",
"description": "Get the current weather for a given city",
"parameters": {
"type": "object",
"properties": {
"city": { "type": "string", "description": "The city name" }
},
"required": ["city"]
}
}
}
]
}'

The response comes back with finish_reason: "tool_calls" and a tool_call carrying an id and the arguments:

{
"choices": [
{
"finish_reason": "tool_calls",
"message": {
"role": "assistant",
"tool_calls": [
{
"id": "call_abc123",
"type": "function",
"function": { "name": "get_weather", "arguments": "{\"city\":\"Paris\"}" }
}
]
}
}
]
}

Step 2 — run the function and send the result back. Append the assistant message (with the tool_calls) and a role: "tool" message whose tool_call_id matches the id from step 1.

cURL
curl https://api.serenitystar.ai/api/v2/inference/qwen/qwen3.6/chat/completions \
-H "Content-Type: application/json" \
-H "X-API-KEY: YOUR_API_KEY" \
-d '{
"model": "qwen/qwen3.6",
"stream": false,
"max_completion_tokens": 512,
"messages": [
{ "role": "user", "content": "What is the weather like in Paris right now? Use the tool." },
{
"role": "assistant",
"tool_calls": [
{
"id": "call_abc123",
"type": "function",
"function": { "name": "get_weather", "arguments": "{\"city\":\"Paris\"}" }
}
]
},
{
"role": "tool",
"tool_call_id": "call_abc123",
"content": "{\"city\":\"Paris\",\"temperature_c\":18,\"condition\":\"light rain\",\"humidity\":72}"
}
]
}'

The final response contains the assistant answer that reflects the tool result.

You can also pin the model to a specific function with tool_choice:

"tool_choice": { "type": "function", "function": { "name": "get_weather" } }
Streaming

The same flow works with "stream": true. In step 1 the tool call arrives via delta.tool_calls and the stream finishes with finish_reason: "tool_calls"; in step 2 the final answer streams as delta.content chunks.


Responses

POST /api/v2/inference/{model}/responses

Uses the OpenAI Responses shape: an input array, max_output_tokens, stream, and an optional tools array. Note that in the Responses API tools are a flat array ({ "type": "function", "name", ... }) and the model returns a function_call output item.

Simple request

cURL
curl https://api.serenitystar.ai/api/v2/inference/qwen/qwen3.6/responses \
-H "Content-Type: application/json" \
-H "X-API-KEY: YOUR_API_KEY" \
-d '{
"model": "qwen/qwen3.6",
"stream": false,
"max_output_tokens": 5000,
"input": [
{ "role": "user", "content": "Hello! Tell me a fun fact about Valencia." }
]
}'

Set "stream": true to receive typed Responses events over SSE. See Streaming for the event sequence.

Tools

Client tools follow OpenAI function calling for the Responses API. Tools are declared flat, and the model returns a function_call output item (with a call_id, name, and arguments).

Step 1 — request that triggers the function call.

cURL
curl https://api.serenitystar.ai/api/v2/inference/qwen/qwen3.6/responses \
-H "Content-Type: application/json" \
-H "X-API-KEY: YOUR_API_KEY" \
-d '{
"model": "qwen/qwen3.6",
"stream": false,
"max_output_tokens": 5000,
"input": [
{ "role": "user", "content": "What is the weather like in Berlin right now? Use the tool." }
],
"tools": [
{
"type": "function",
"name": "get_weather",
"description": "Get the current weather for a given city",
"parameters": {
"type": "object",
"properties": {
"city": { "type": "string", "description": "The city name" }
},
"required": ["city"]
}
}
]
}'

The response output contains a function_call item:

{
"output": [
{
"type": "function_call",
"call_id": "call_abc123",
"name": "get_weather",
"arguments": "{\"city\":\"Berlin\"}"
}
]
}

Step 2 — run the function and send the result back. Replay the function_call in input, then append a function_call_output item with the same call_id.

cURL
curl https://api.serenitystar.ai/api/v2/inference/qwen/qwen3.6/responses \
-H "Content-Type: application/json" \
-H "X-API-KEY: YOUR_API_KEY" \
-d '{
"model": "qwen/qwen3.6",
"stream": false,
"max_output_tokens": 5000,
"input": [
{ "role": "user", "content": "What is the weather like in Berlin right now? Use the tool." },
{
"type": "function_call",
"call_id": "call_abc123",
"name": "get_weather",
"arguments": "{\"city\":\"Berlin\"}"
},
{
"type": "function_call_output",
"call_id": "call_abc123",
"output": "{\"city\":\"Berlin\",\"temperature_c\":12,\"condition\":\"overcast\",\"humidity\":80}"
}
],
"tools": [
{
"type": "function",
"name": "get_weather",
"description": "Get the current weather for a given city",
"parameters": {
"type": "object",
"properties": { "city": { "type": "string" } },
"required": ["city"]
}
}
]
}'

The final response contains the assistant message that reflects the tool result.

You can also pin the model to a specific function with tool_choice (Responses object form):

"tool_choice": { "type": "function", "name": "get_weather" }
Streaming

With "stream": true, step 1 emits typed events (response.output_item.added for the function_call, response.function_call_arguments.delta, .done, response.output_item.done, response.completed); step 2 streams the final answer as response.output_text.delta events.


Differences from AIProxy

If you are coming from the OpenAI Compatible AIProxy endpoints, note that inference talks to a raw self-hosted model rather than a configured agent:

  • The model is chosen by the URL path (/api/v2/inference/{model}/...), not by a {agentCode}:{vendor}:{model} identifier in the body.
  • Server tools are not available — there is no agent to run web search, image generation, speech generation, or workbench (code execution) on the server. Only client-side function calling is supported.
  • No extra_body — inference executions are attributed to the API channel automatically; there is no channel / user_identifier / group_identifier metadata to send.

Limitations

Work in progress

This section is still being finalized.

  • Client-tool history is optimized for delivering the latest turn's tool outputs. Fully interleaved multi-turn tool histories are not yet fully supported.
  • More limitations to be documented.