Chat integration#
Chat is a synchronous or streaming Star Task capability. It does not use the asynchronous POST /v1/tasks creation contract, even though each completed turn is recorded as a task for billing and audit.
Route by task type#
Runtime task_type | Submit endpoint | Completion contract |
|---|---|---|
chat | POST /v1/ai/chat | Immediate JSON or SSE stream |
| Image, video, audio, music | POST /v1/tasks | 202 plus poll_url |
| Reusable AI tool | POST /v1/tools/:tool_id/tasks | Tool-specific task result |
Use GET /v1/models?view=chat-picker&site_id=<SITE_ID> for a chat-only picker. Submit the selected mode's exact task_model as model; never derive an alias from product_id.
Quote a chat turn#
POST https://task.starhk.uk/v1/ai/chat/quote
Authorization: Bearer <access_token>
Content-Type: application/jsonThe quote endpoint accepts the same body as the send endpoint. Its JSON response contains top-level cost_points; there is no data wrapper.
Send a non-streaming message#
const body = {
site_id: SITE_ID,
model: selectedMode.task_model,
task_type: 'chat',
input: {
messages: [
{ role: 'system', content: 'Answer clearly and concisely.' },
{ role: 'user', content: '你好' },
],
},
options: {
stream: false,
web_search: false,
thinking: false,
max_tokens: 4096,
},
idempotency_key: crypto.randomUUID(),
};
const response = await fetch('https://task.starhk.uk/v1/ai/chat', {
method: 'POST',
headers: {
Authorization: `Bearer ${accessToken}`,
'Content-Type': 'application/json',
},
body: JSON.stringify(body),
});
const result = await response.json();
if (!response.ok || result.ok !== true) throw new Error(result.error?.message ?? 'Chat failed');
const reply = result.output?.choices?.[0]?.message?.content ?? result.output?.text ?? '';The native non-streaming response is:
{
"ok": true,
"task_id": "t_xxx",
"conversation_id": "c_xxx",
"model": "deepseek-v4-flash",
"output": {
"choices": [{ "message": { "role": "assistant", "content": "你好" } }]
},
"cost_points": 1
}output is the provider-compatible completion object. A site backend may normalize it to a reply string, but reply is not a native Star Task response field.
For a follow-up turn, pass the returned ID as options.conversation_id. Keep the relevant prior messages in input.messages; do not put messages at the request top level.
Stream a response#
Set options.stream=true and send the same request to POST /v1/ai/chat. The response content type is text/event-stream, with OpenAI-compatible data: frames and a final [DONE] marker. Read these response headers before consuming the stream:
X-Conversation-IdX-Task-Id
Accumulate choices[0].delta.content from each JSON frame. Do not try to parse the stream as one JSON document and do not poll /v1/tasks for the assistant text.
Upload and reference documents#
The browser integrates one Star Task attachment API. It must not choose or call a RokoAPI parser; Star Task selects the text, office, or presentation parser from the verified file type.
Read the runtime limits before rendering the file picker:
GET https://task.starhk.uk/v1/chat/attachments/capabilities?site_id=<SITE_ID>
Authorization: Bearer <access_token>The response contains max_file_bytes, max_files_per_message, image_extensions, document_extensions, and all_extensions. Build the input accept value from all_extensions; do not keep a separate site-specific extension list.
Current server limits are 10 MiB per file and six attachments per chat request. Supported document types are:
- Office and PDF:
pdf,docx,xlsx,xls,pptx. - OpenDocument:
odt,ods. - Text and data:
txt,md,markdown,csv,tsv,json,yaml,yml,xml,html, - Images accepted by the same chat attachment flow:
jpg,jpeg,png,webp.
htm, log, toml, ini.
Text files must be valid UTF-8. Legacy .doc and .ppt, macro-enabled Office documents, archives, encrypted documents, and executable files are not supported. PDF OCR, image text recognition, PowerPoint visual layout, charts, and animations are not guaranteed.
Upload one file as multipart form data:
POST https://task.starhk.uk/v1/chat/attachments
Authorization: Bearer <access_token>
Content-Type: multipart/form-data
file=<binary>
site_id=<SITE_ID>
conversation_id=<optional existing conversation ID>Images return ready; documents normally return parsing. Poll GET /v1/chat/attachments/:id?site_id=<SITE_ID> until ready or failed. A failed document can be retried with POST /v1/chat/attachments/:id/retry. Delete unused private files with DELETE /v1/chat/attachments/:id?site_id=<SITE_ID>.
Reference only a ready attachment in a user message:
{
"role": "user",
"content": [
{ "type": "text", "text": "Summarize the attached document" },
{ "type": "attachment", "attachment_id": "att_xxx" }
]
}Attachments are private to the authenticated user_id, site_id, and bound conversation. Never store or expose a RokoAPI token in frontend code.
For one model request, Star Task currently supplies at most 80,000 parsed characters from one document and 120,000 characters across all documents. If a document is larger, the beginning and end are retained with an explicit omission marker so the model cannot mistake a partial document for complete content. These values are returned by the capabilities endpoint and may change; the browser must not use them to reject otherwise valid uploads.
Request and response rules#
inputmust be an object andinput.messagesmust be a non-empty array.- Message roles are
system,user, orassistant; content may be text or supported content - Put
stream,web_search,thinking,conversation_id, andmax_tokensinoptions. - The successful JSON response is top-level `{ ok, task_id, conversation_id, model, output,
- Generate one UUID v4 per user-initiated turn and reuse it only when retrying that same turn.
- Surface structured
{ ok:false, error:{code,message,details?} }failures to the user.
parts returned by the runtime capabilities.
cost_points }; do not assume { data: ... }`.
Common integration failures#
| Failure | Cause | Fix |
|---|---|---|
Chat type rejected by /v1/tasks | Chat was sent to the async media endpoint | Use /v1/ai/chat |
input must be an object | messages was placed at the request top level | Wrap it as input.messages |
| Empty displayed reply | Client expected a top-level reply field | Read output.choices[0].message.content or normalize it in the site backend |
| Endless polling | Chat was modeled as an async task | Handle JSON/SSE directly |