SStar Developers
动态表单、工具目录和调用查看 Markdown

Reusable AI tools#

Use the tools API for product features such as a lyrics generator, image-to-prompt analysis, background removal, or style transfer. A frontend integrates the stable tool contract; Star Task owns the private implementation, validation, charging, and output normalization.

Discover tools at runtime#

Request GET https://task.starhk.uk/v1/tools?site_id=<SITE_ID>. Catalog and individual tool-detail reads are public, but site_id is required because availability and pricing are site-scoped. Quote, upload, create, and poll requests require the current user's Authorization: Bearer <ACCESS_TOKEN>. Render only returned tools. Each item includes:

  • id, display_name, and category for catalog display;
  • execution_type: text-generation, image-understanding, or image-generation;
  • cost_points, the effective site price;
  • ui_schema, an ordered list of frontend fields;
  • constraints, including accepted image count, sizes, or text limits;
  • version, which changes when an administrator updates the tool.

Do not hard-code the backend model, provider, system prompt, or price. A site may hide any returned tool from its interface, but hiding is presentation logic and does not alter Star Task's server-side availability.

Build the interface#

Generate form controls from ui_schema. Supported field types are currently:

  • text: use label, placeholder, maxLength, and multiline;
  • select: use label, options, and default. When allowCustom is true, render an editable combobox (for example a labeled input with a datalist) rather than a closed enum; preserve keyboard access and allow values outside options.

Fields may include group: "basic" | "advanced", description, and required. Show basic fields immediately and place advanced fields in a collapsed, keyboard-accessible disclosure. Mark required fields from ui_schema[].required; do not infer required state from visual order. Older catalogs may omit grouping, in which case render fields in the returned order.

Use execution_type and constraints for media input:

  • text-generation: normally sends no image_urls;
  • image-understanding: requires the configured number of image URLs;
  • image-generation: may require reference images and may expose sizes.

When media input is enabled, build the file picker from constraints.accepted_image_types, enforce constraints.max_image_bytes before uploading, and enforce the combined image count from min_ref_images / max_ref_images. Show a local preview and upload progress. Upload each selected file through POST /v1/upload; do not send base64 data in a tool task or expose a local browser object URL to Star Task.

Uploaded input images are temporary assets. Star Task stores new image uploads under the uploads/images/ R2 prefix, which production config expires after two hours. Never present an uploaded input URL as permanent user storage. Generated task results use a separate prefix and are not covered by this temporary-input lifecycle rule.

Refresh the catalog after a tool version changes. Do not attempt to interpret or display the private prompt template.

Quote and create#

Quote with POST /v1/tools/:toolId/quote?site_id=<SITE_ID> using the same body shape as creation. Create with POST /v1/tools/:toolId/tasks?site_id=<SITE_ID> and a user bearer token:

js
const body = {
  params: {
    theme: '在香港重逢',
    keywords: '渡轮、晚风、霓虹',
    style: 'Cantopop',
    mood: '怀旧、克制',
    language: '繁体中文',
    duration: '中等(约3分钟)',
    structure: '主歌-预副歌-副歌-主歌-预副歌-副歌-桥段-副歌',
    rhyme: '自然押韵',
  },
  image_urls: [],
  idempotency_key: crypto.randomUUID(),
};

const created = await fetch(
  'https://task.starhk.uk/v1/tools/lyrics-generator/tasks?site_id=site-xx',
  {
    method: 'POST',
    headers: {
      Authorization: `Bearer ${accessToken}`,
      'Content-Type': 'application/json',
    },
    body: JSON.stringify(body),
  },
).then(response => response.json());

For lyrics-generator, only theme is required. Optional parameters are keywords, style, mood, language, title, duration, structure, rhyme, reference_artist, and extra. Style, mood, and language are editable selects; duration, structure, and rhyme are closed selects. Preserve these stable parameter names, but obtain labels, options, defaults, limits, price, and version from the runtime catalog. Never expose a model or provider selector for this tool.

For image-to-prompt, upload exactly one JPEG, PNG, or WEBP image through Star Task, then pass the returned reachable URL:

js
const form = new FormData();
form.append('file', file);
const uploaded = await fetch(
  `https://task.starhk.uk/v1/upload?site_id=${encodeURIComponent(siteId)}`,
  {
  method: 'POST',
  headers: { Authorization: `Bearer ${accessToken}` },
  body: form,
  },
).then(response => response.json());
js
{
  params: {
    target_platform: '通用生图模型',
    output_language: '中文',
    detail_level: '专业详细',
    include_negative_prompt: '是',
    focus: '构图、光线和镜头语言',
  },
  image_urls: [uploaded.url],
  idempotency_key: crypto.randomUUID()
}

Obtain option labels and defaults from the live ui_schema; do not hard-code the example values. The implementation is private and may change without changing the frontend contract. Any execution failure makes the whole task fail under the normal refund policy.

When structured output is valid, data can include prompt, negative_prompt, summary, subject, style, composition, lighting, camera, color_palette, and aspect_ratio. Always render text as a fallback if data is absent.

Poll the returned poll_url exactly like a normal Star Task task. Successful text and image-understanding tools return a normalized output:

json
{
  "type": "tool",
  "tool_id": "lyrics-generator",
  "tool_version": 3,
  "execution_type": "text-generation",
  "text": "...",
  "data": {}
}

The version above is illustrative. Always use the runtime tool_version; never branch on or hard-code a specific catalog version.

data appears only when the tool has a structured output schema and the result passes server-side schema validation. Always support text as a fallback. Image-generation tools retain the normal media output shape and stable Star Task/R2 URLs.

Star Task normalizes structured output inside the backend. Frontend sites must not send or select implementation-specific output modes and do not need a release when the private implementation changes.

For diagnostics, a text tool result may include structured_output.mode and structured_output.valid. Treat data as authoritative only when present. If valid is false, render or otherwise handle text; do not parse it into trusted structured data on the frontend.

General image understanding#

The stable tool ID is image-understanding. It accepts exactly one JPEG, PNG, or WEBP image. Always read constraints.max_image_bytes and constraints.accepted_image_types from the runtime catalog instead of copying current limits into frontend policy.

Upload the image with /v1/upload, then quote and create with this shape:

json
{
  "params": {
    "question": "识别图片中的文字并说明界面状态",
    "detail_level": "标准",
    "output_language": "中文"
  },
  "image_urls": ["https://task.starhk.uk/v1/assets/<upload-id>.png"],
  "idempotency_key": "<fresh UUID v4>"
}

question is optional. Current supported values for detail_level and output_language come from ui_schema; do not hard-code the example options. When structured output validation succeeds, task.output.data contains summary, scene, objects, ocr_text, composition, colors, optional chart_or_ui_data, and uncertainties. Other text-only models should consume this structured result or the text fallback, not the original image.

Web-search chat#

The stable frontend-facing tool ID is web-search-chat. Create it through POST /v1/tools/web-search-chat/tasks?site_id=<SITE_ID>. Frontend sites only use the Star Task tool contract and must not select or submit a backend model, provider, or private implementation identifier.

json
{
  "params": {
    "query": "香港今天有哪些重要的人工智能新闻?"
  },
  "image_urls": [],
  "idempotency_key": "<fresh UUID v4>"
}

query is required and limited to 128 characters. Obtain the current limits from the live ui_schema; do not hard-code the example as a frontend-owned contract.

On success, task.output.data has this contract:

ts
type WebSearchResult = {
  schema_version: string;
  provider: string;
  sources: Array<{
    url: string;
    title?: string;
    summary?: string;
    site_name?: string;
    publish_time?: string;
    logo_url?: string;
    content?: string;
    authority?: number;
    rerank_score?: number;
  }>;
};

Treat every source URL, title, and summary as untrusted external content: render text escaped, allow only safe http:/https: destinations, use target="_blank" with rel="noopener noreferrer", and never inject returned HTML. Always support task.output.text if data is absent. The returned provider is diagnostic metadata only and must not drive frontend routing. This tool returns search results and citations; it does not synthesize an answer.

End-to-end browser example#

This example intentionally obtains the current price from quote, uses the upload response's top-level url, and appends site_id when polling the returned relative URL:

ts
const TASK_BASE_URL = 'https://task.starhk.uk';

type ToolTask = {
  id: string;
  status: 'pending' | 'running' | 'succeeded' | 'failed' | 'canceled';
  cost_points: number;
  billing_status: 'charged' | 'not_charged' | 'refunded' | 'refund_pending';
  output?: {
    type: 'tool';
    tool_id: string;
    text: string;
    data?: Record<string, unknown>;
  } | null;
  error?: string | null;
};

async function starRequest<T>(path: string, accessToken: string, init: RequestInit = {}): Promise<T> {
  const response = await fetch(`${TASK_BASE_URL}${path}`, {
    ...init,
    headers: {
      Authorization: `Bearer ${accessToken}`,
      ...(init.body instanceof FormData ? {} : { 'Content-Type': 'application/json' }),
      ...init.headers,
    },
  });
  const payload = await response.json();
  if (!response.ok || payload.ok === false) {
    const error = new Error(payload.error?.message || `Star Task HTTP ${response.status}`);
    Object.assign(error, { code: payload.error?.code, status: response.status, details: payload.error?.details });
    throw error;
  }
  return payload as T;
}

async function uploadToolImage(siteId: string, accessToken: string, file: File) {
  const form = new FormData();
  form.append('file', file);
  return starRequest<{ ok: true; url: string; content_type: string; size: number }>(
    `/v1/upload?site_id=${encodeURIComponent(siteId)}`,
    accessToken,
    { method: 'POST', body: form },
  );
}

async function runImageUnderstanding(siteId: string, accessToken: string, file: File) {
  const uploaded = await uploadToolImage(siteId, accessToken, file);
  const input = {
    params: { question: '请提取文字并概括图片', detail_level: '标准', output_language: '中文' },
    image_urls: [uploaded.url],
  };

  const quote = await starRequest<{ ok: true; tool_id: string; cost_points: number }>(
    `/v1/tools/image-understanding/quote?site_id=${encodeURIComponent(siteId)}`,
    accessToken,
    { method: 'POST', body: JSON.stringify(input) },
  );
  // Display quote.cost_points before the user confirms submission.

  const created = await starRequest<{ ok: true; task_id: string; poll_url: string; cost_points: number }>(
    `/v1/tools/image-understanding/tasks?site_id=${encodeURIComponent(siteId)}`,
    accessToken,
    { method: 'POST', body: JSON.stringify({ ...input, idempotency_key: crypto.randomUUID() }) },
  );

  for (;;) {
    const result = await starRequest<{ ok: true; task: ToolTask }>(
      `${created.poll_url}?site_id=${encodeURIComponent(siteId)}`,
      accessToken,
    );
    if (result.task.status === 'succeeded') return result.task.output;
    if (result.task.status === 'failed' || result.task.status === 'canceled') {
      throw new Error(result.task.error || '工具执行失败');
    }
    await new Promise(resolve => setTimeout(resolve, 1500));
  }
}

Use the same quote/create/poll helpers for web-search-chat, omit upload, send image_urls: [], and replace the params object with the search parameters above. Stop polling when the page is hidden or the component unmounts, and resume from the saved poll_url; do not create a replacement task merely because polling was interrupted.

Charging and failure behavior#

  • Quote validates the same tool inputs and returns the current estimate without charging. The create response's cost_points is authoritative.
  • Tool tasks use the administrator-configured fixed tool price, not the selected text model's token price.
  • image-understanding and web-search-chat currently each cost 0.10 points globally. A site override can change that value, so fetch catalog/quote at runtime and never hard-code 0.10 as authorization or billing logic.
  • Creation immediately debits the quoted fixed amount. Reuse an idempotency key only when retrying the same user action; generate a new UUID for a new user action.
  • Failed or canceled tasks are refunded by Star Task. Poll until terminal state and use task.billing_status: charged, not_charged, refunded, or refund_pending. Do not call a points consume/refund endpoint from the frontend.
  • Public charges and configured prices use 0.01-point increments. Internal final settlement may retain three decimals. User-facing balances, quotes, charges, and refunds must display exactly two decimals, for example with Intl.NumberFormat(locale, { minimumFractionDigits: 2, maximumFractionDigits: 2 }).
  • JSON numbers do not preserve trailing zeros: the API can return 0.1; display it as 0.10, but send and compare it as a number.
  • Backend selection and routing are private implementation details. Handle only the documented Star Task task states and error contract.