KsADK

OpenAI-Compatible API

The local KsADK runtime exposes three OpenAI-compatible endpoints for development and debugging. Use the term OpenAI-compatible only for public protocol shapes. Mark SDK-specific fields as KsADK extensions.

Auth: Bearer Token

The hosted runtime requires Authorization: Bearer <token>. The local dev server does not enforce auth by default, but the gateway injects and validates Bearer in production. Clients should always send it so local and hosted behavior stay consistent.

Start a local server before calling these endpoints:

terminal
agentengine run . --port 8080

Endpoint Summary

EndpointMethodUse
/v1/responsesPOSTResponses-style local agent calls (preferred)
/v1/chat/completionsPOSTChat Completions compatibility clients
/v1/modelsGETList the available model catalog

/v1/responses is the recommended local runtime protocol; /v1/chat/completions targets existing Chat clients; /v1/models returns the current model catalog (with current / source local extensions).

Request Lifecycle

POST /v1/responses

POST /v1/responses is the preferred local runtime protocol. It supports non-streaming JSON, streaming SSE, checkpoint resume, and approval payloads.

Request body fields (based on the ResponsesRequest schema):

FieldTypeRequiredNotes
inputstring | object | arrayyesstring, message object, or input item list
modelstringnomodel or agent name override; falls back to runtime default
streambooleannotrue returns SSE, default false
instructionsstringnoadditional system-level instruction
metadataobjectnocaller metadata, preserved and passed through
conversationstring | {id}noconversation id or object with id (preferred over session_id)
previous_response_idstringnoprevious response reference; mutually exclusive with conversation
session_idstringnolegacy local session id; prefer conversation
userstringnocaller/user identifier, default user
safety_identifierstringnosafety identifier, falls back to user_id
prompt_cache_keystringnoprompt cache key, written into metadata
storebooleannowhether to persist, written into metadata
model_metadataobjectnoKsADK extension: model capability hints
model_optionsobjectnoKsADK extension: provider-specific options passthrough
account_idstringnoKsADK extension (0.6.5+): isolates Skill/Workspace/Sandbox/Memory by account

Field exclusivity

conversation and session_id must not disagree; conversation and previous_response_id are mutually exclusive. If both appear and target different sessions, the runtime returns 400.

Minimal request:

request.json
{
  "model": "my-agent",
  "input": "Explain what this agent can do.",
  "stream": false
}

With conversation and account_id:

request.json
{
  "model": "my-agent",
  "conversation": {"id": "local-demo-session"},
  "input": "Continue from the previous answer.",
  "account_id": "acct_42",
  "stream": false
}

The non-streaming response returns OpenAI-compatible top-level fields plus a small number of KsADK extensions:

FieldStatusNotes
idcompatibleresponse id generated by the runtime, e.g. resp_<hex>
objectcompatibleresponse
created_atcompatiblecreation timestamp
statuscompatiblecompleted / failed / incomplete
modelcompatiblemodel or agent used for the request
outputcompatibleoutput item, usually an assistant message
metadatacompatiblecaller metadata; KsADK 0.6.9+ adds metadata.last_usage when available
usagecompatible-shapedper-response accumulated usage when available, used for the real cost of this response
output_textKsADK extensionconvenient concatenated text
session_idKsADK extensionlocal session id
response.json
{
  "id": "resp_a1b2c3",
  "object": "response",
  "created_at": 1719900000,
  "status": "completed",
  "model": "my-agent",
  "output": [
    {
      "type": "message",
      "role": "assistant",
      "content": [
        {"type": "output_text", "text": "This agent can..."}
      ]
    }
  ],
  "usage": {
    "input_tokens": 1250,
    "output_tokens": 180,
    "total_tokens": 1430,
    "input_token_details": {"cached": 900}
  },
  "metadata": {
    "last_usage": {
      "input_tokens": 980,
      "output_tokens": 180,
      "total_tokens": 1160,
      "input_token_details": {"cached": 700}
    }
  },
  "output_text": "This agent can...",
  "session_id": "local-demo-session"
}

Consumers aiming for broad compatibility should read output first and treat output_text as a convenience field.

usage and last_usage

usage keeps the OpenAI-style accumulated usage for this response; if an agent loop calls the model multiple times, those calls are summed. The KsADK-specific metadata.last_usage is the final model-call usage snapshot, used by higher layers to compute current context-window occupancy. It is not a cumulative session total.

With stream: true the runtime returns text/event-stream, serializing streamed chunks as Responses-style SSE events.

sse
curl -N http://127.0.0.1:8080/v1/responses \
  -H 'Content-Type: application/json' \
  -d '{"model":"my-agent","input":"count to three","stream":true}'

Example SSE output (each data: line is one JSON object, blank line separates events):

responses.sse
event: response.created
data: {"type":"response.created","response":{"id":"resp_a1b2c3","status":"in_progress"}}

event: response.output_text.delta
data: {"type":"response.output_text.delta","delta":"one"}

event: response.output_text.delta
data: {"type":"response.output_text.delta","delta":"two"}

event: response.completed
data: {"type":"response.completed","response":{"id":"resp_a1b2c3","status":"completed"}}

invocation_id passthrough (0.6.5+)

Pass a custom invocation id in metadata.agentengine.invocation_id; the runtime passes it through to the runner for transcript grouping and event resume. If omitted, the runtime generates one automatically.

Errors use standard HTTP status codes plus a JSON detail:

StatusScenariodetail example
400conversation conflicts with session_idResponses field 'conversation' conflicts with ksadk legacy field 'session_id'.
400conversation used with previous_response_idResponses fields 'conversation' and 'previous_response_id' cannot be used together.
400invalid conversation typeResponses field 'conversation' must be a string or an object with an 'id'.
404checkpoint resume cannot find session/checkpointCheckpoint not found
409checkpoint resume already running{"code":"resume_already_running",...}
500Runner not initialized or failed to loadRunner 未初始化
error.json
{
  "detail": "Responses field 'conversation' conflicts with ksadk legacy field 'session_id'. Use 'conversation' for OpenAI-compatible calls."
}

Input Items Shapes

input can be a string, a message object, or an array of input items. Multimodal examples use OpenAI-compatible content block names:

multimodal.json
{
  "model": "my-agent",
  "input": [
    {
      "role": "user",
      "content": [
        {"type": "input_text", "text": "Describe this image."},
        {"type": "input_image", "image_url": "data:image/png;base64,..."}
      ]
    }
  ]
}

File example:

file-input.json
{
  "model": "my-agent",
  "input": [
    {
      "role": "user",
      "content": [
        {"type": "input_text", "text": "Summarize this file."},
        {
          "type": "input_file",
          "filename": "notes.txt",
          "file_data": "data:text/plain;base64,..."
        }
      ]
    }
  ]
}

Remote file_url values are preserved as references. KsADK does not document a public guarantee that the local runtime will fetch arbitrary remote files for extraction. Use file_data or a local upload reference when you need local attachment processing.

Resume and Approval Inputs

The runtime recognizes common resume payloads in input, including function call output and MCP approval:

function-call-output.json
{
  "type": "function_call_output",
  "call_id": "call_123",
  "output": "approved result"
}
mcp-approval.json
{
  "type": "mcp_approval_response",
  "approval_request_id": "approval_123",
  "approve": true,
  "reason": "Approved by local user"
}

These payloads are passed to framework adapters as resume input. Application code still owns business-specific approval semantics.

  1. Put a resume payload (e.g. function_call_output) into the input of /v1/responses.
  2. The runtime calls extract_responses_resume_input to identify the resume type.
  3. For checkpoint resume, it locates by session_id + run_id + checkpoint_id.
  4. When found, it is converted to a runner resume input, skipping new-message normalization.
  5. The runner continues from the original checkpoint instead of restarting.

POST /v1/chat/completions

POST /v1/chat/completions is the compatibility protocol for Chat Completions clients. It supports streaming and non-streaming.

Request body fields (based on the ChatCompletionRequest schema):

FieldTypeRequiredNotes
messagesarrayyeschat message list
modelstringnomodel or agent name override
streambooleannotrue returns SSE, default false
userstringnocaller/user identifier, default user
temperaturenumbernoprovider option, default 0.7
max_tokensintegernoprovider option
session_idstringnoKsADK extension: local session id
model_metadataobjectnoKsADK extension: model capability hints
model_optionsobjectnoKsADK extension: provider-specific runtime options
account_idstringnoKsADK extension (0.6.5+): isolates by account
request.json
{
  "model": "my-agent",
  "messages": [
    {"role": "user", "content": "Say hello from KsADK."}
  ],
  "stream": false,
  "account_id": "acct_42"
}

Multimodal Chat-style content blocks:

multimodal.json
{
  "model": "my-agent",
  "messages": [
    {
      "role": "user",
      "content": [
        {"type": "text", "text": "What is in this image?"},
        {"type": "image_url", "image_url": {"url": "data:image/png;base64,..."}}
      ]
    }
  ]
}

The non-streaming response returns a Chat Completions-compatible shape:

response.json
{
  "id": "chatcmpl-<uuid>",
  "object": "chat.completion",
  "created": 1719900000,
  "model": "my-agent",
  "choices": [
    {
      "index": 0,
      "message": {
        "role": "assistant",
        "content": "Hello from KsADK!"
      },
      "finish_reason": "stop"
    }
  ],
  "session_id": "<resolved-session-id>"
}

The runtime converts supported Chat Completions requests into the KsADK canonical runner input.

With stream: true the runtime returns text/event-stream using Chat Completions-style chunks:

sse
curl -N http://127.0.0.1:8080/v1/chat/completions \
  -H 'Content-Type: application/json' \
  -d '{
    "model": "my-agent",
    "messages": [{"role": "user", "content": "count to three"}],
    "stream": true
  }'
chat.sse
data: {"id":"chatcmpl-abc","object":"chat.completion.chunk","created":1719900000,"model":"my-agent","choices":[{"index":0,"delta":{"role":"assistant"},"finish_reason":null}]}

data: {"id":"chatcmpl-abc","object":"chat.completion.chunk","created":1719900000,"model":"my-agent","choices":[{"index":0,"delta":{"content":"one"},"finish_reason":null}]}

data: {"id":"chatcmpl-abc","object":"chat.completion.chunk","created":1719900000,"model":"my-agent","choices":[{"index":0,"delta":{"content":"two"},"finish_reason":null}]}

data: {"id":"chatcmpl-abc","object":"chat.completion.chunk","created":1719900000,"model":"my-agent","choices":[{"index":0,"delta":{},"finish_reason":"stop"}]}

data: [DONE]
StatusScenarioNotes
400messages missing or invalidPydantic validation failure
500Runner not initialized or failed to loadRunner 未初始化
error.json
{
  "detail": "Runner 未初始化"
}

GET /v1/models

Lists the current available model catalog. The local runtime first fetches upstream /v1/models from OPENAI_BASE_URL / OPENAI_API_BASE; on failure it falls back to a single-item catalog from the local MODEL_NAME.

terminal
curl http://127.0.0.1:8080/v1/models
response.json
{
  "object": "list",
  "data": [
    {"id": "deepseek-v3.2", "object": "model", "owned_by": "kspmas"}
  ],
  "current": "deepseek-v3.2",
  "source": "MODEL_NAME"
}
FieldNotes
objectlist
dataarray of models, each with id, object, owned_by and other canonical metadata
currentKsADK extension: the currently effective model id
sourceKsADK extension: source env var name (OPENAI_MODEL_NAME / MODEL_NAME / COZE_MODEL_NAME)

Streaming Event Reference

/v1/responses streaming event names follow the OpenAI Responses style:

EventMeaning
response.createdresponse object allocated
response.in_progressmodel or agent execution started
response.output_item.addedoutput item started
response.content_part.addedmessage content part started
response.output_text.deltatext delta
response.output_text.donetext content completed
response.reasoning.deltareasoning delta; not emitted when thinking is disabled
response.function_call_arguments.deltafunction-call arguments delta
response.function_call_arguments.donefunction-call arguments completed
response.ksadk.tool_resultKsADK tool result extension
response.incompleteinterrupted or waiting for approval/resume
response.completedfinal successful response
response.failedterminal error

Clients should ignore unknown events they do not support, to remain forward-compatible with new runner event types.

KsADK Extension Fields

model_metadata, model_options, account_id, session_id, and output_text are KsADK extensions and must not be described as official OpenAI fields.

account_id passthrough (0.6.5+)

account_id isolates Skill / Workspace / Sandbox / Memory by account boundary, written into the runtime PlatformInvocationContext. Supported by both /v1/responses and /v1/chat/completions.

invocation_id passthrough (0.6.5+)

Pass a custom invocation id in metadata.agentengine.invocation_id; the runtime passes it through to the runner for transcript grouping and event resume. If omitted, the runtime generates one automatically.

thinking-disable injection (0.6.7)

model_options passes provider-specific options through to supported runners. As of 0.6.7, when thinking is disabled (reasoning.effort=none, thinking.type=disabled, or max_reasoning_tokens<=0), the runtime automatically injects enable_thinking=false and chat_template_kwargs.enable_thinking=false into extra_body (DeepSeek compatibility) and filters streaming reasoning output items.

  • CHAT-style reasoning.effort values: low / medium / high / none
  • Responses-style values: none / minimal / low / medium / high / xhigh

The response.reasoning.delta event is not emitted when thinking is disabled (filtered by the runtime).

Implementation Boundary

At runtime, protocol handlers normalize incoming requests into a common runner payload, then call the active framework runner through a narrow interface:

OpenAI-compatible API request and response flow

The public contract is the endpoint behavior. Internal event names, session storage details, and runner payload extensions may evolve across releases.

On this page