ModelRouterModelRouter
User GuideInstallationAPI ReferenceAI ApplicationsSkillsHelp & SupportBusiness Cooperation

Task Plugin API v1 Reference

Manifest, context, lifecycle, native routes, host protocols, usage, artifacts, and streaming capabilities of task plugins.

Contract Status and Authoritative Sources

This repository marks API v1 as a contract that has not yet been officially released. New capabilities may still use apiVersion: 1, and older hosts may reject unrecognized fields. This page summarizes the contract; the complete signature and validation structure are subject to v1.d.ts, v1.schema.json and the original specification.

Manifest: meta

FieldTypeDescription
apiVersion1Contract version
keystringPlugin identifier, max 30 characters, consistent with marketplace directory name
namestringDisplay name
versionstringSemantic version, consistent with version directory
author{ name, url? }Author name is required, URL is HTTP(S) address; self-declared author information
modelsstring[]Declares supported models
fetchModeper_task / batchSingle-task or batch polling
descriptionLocalizedTextPlugin description
iconstringLobeHub icon name or text / text:<label>, remote URLs or inline images are not accepted
websitestringOptional plugin official website, must be a valid HTTPS URL if not empty
sortPriorityintegerDisplay sort order, larger value means higher priority; does not affect routing priority
baseUrlstringDefault upstream address usable by channel type 61
allowedHostsstring[]Additional hosts allowed to access beyond channel host, can include port
upstreams("vendor" | "new_api")[]Optional upstream kinds; vendor support is implied, while new_api enables binding to type-60 ModelRouter channels
authstring / objectnone, api_key, vertex_oauth or authentication object defined by specification
channelTypesnumber[]Adaptable legacy channel types; third-party plugins typically use key binding for type 61
routesNativeRoute[]Plugin's own native routes
protocolsProtocolClaim[]Host protocol declarations
usageSchema / usageExamplesobject / arrayDefault usage fields and examples
usageProfilesarrayProvides complete usage schema and examples per model
requiredCapabilitiesstring[]Versioned capabilities that must be supported by the host
submitResponseTypesarrayUpstream submission response types, default ["json"], can declare "sse"

baseUrl must not contain credentials, query strings, or fragments, and must use an ASCII hostname; self-hosted HTTP or private addresses are allowed. allowedHosts uses host / host:port, without protocol or path, and the port will be part of the matching. The default address does not implicitly expand the set of allowed hosts.

Localized Text

LocalizedText can use a string or a language map containing en. Strings will be normalized to an English map. The matching order is current language, primary language, English:

description: {
  en: "Video generation through the vendor API",
  zh: "通过厂商接口生成视频",
  "zh-TW": "透過廠商介面產生影片",
}

Text in plugin data should not be used as translation keys for the management frontend. Model names, field keys, and raw enum values must remain stable.

Lifecycle Hooks

ExportInputPrimary Return Content
buildSubmitRequestDriverContextHTTP request descriptor
parseSubmitResponsectx, { statusCode, headers, body }{ taskId, taskData?, immediate?, state? }
buildQueryRequestTaskQueryContextSingle-task query descriptor, per_task is required
parseTaskResultQuery context, body, { status, headers }Standardized status, optional progress/reason/result, etc.
buildBatchQueryRequestBatch context, task arrayBatch query descriptor, batch is required
parseBatchResultBatch context, body, HTTP infoArray of results, each containing taskId, batch is required

All plugins must export meta, buildSubmitRequest, parseSubmitResponse, and parseTaskResult, including batch plugins.

Standard statuses include NOT_START, SUBMITTED, QUEUED, IN_PROGRESS, SUCCESS, FAILURE, UNKNOWN. Unknown statuses return UNKNOWN; unknown results should not be defaulted to in-progress.

HTTP status from upstream responses also participates in host determination: 404/410 leads to failure and refund; 401/403, 429, 5xx, and transport errors accumulate polling failures. After reaching TASK_POLL_MAX_FAILURES (default 20), failure cleanup is initiated, and the task timeout mechanism remains the outer deadline.

Request and Query Context

DriverContext provides normalized requestBody, request headers, action, model / upstreamModel, channel baseUrl, authentication information, file references, public task ID, and optional originTasks.

TaskQueryContext reconstructs from saved tasks:

FieldMeaning
taskIdUpstream task ID
publicTaskIdModelRouter public task ID
model / upstreamModelUser model name and upstream model name after channel mapping
actionPersisted standardized operation
dataCurrent Task.Data snapshot
statePlugin's private cross-polling state
baseUrl / Auth fieldsCurrently used channel information

The query side does not have requestBody. The saved field name is data, there is no raw alias. If the parsing hook omits state, the original state is retained; it is only updated if explicitly returned. Request and state inputs should be treated as read-only and not rely on module global variables to save task data.

Upstream kinds and gateway connections

meta.upstreams accepts unique vendor and new_api values. Every plugin implicitly supports vendor; new_api means the driver also addresses an upstream ModelRouter gateway with the same plugin installed. Type-60 channels bind plugins through setting.task_plugin_key / setting.task_extend_plugin_keys. Neither 60 nor 61 may appear in channelTypes, and each legacy channel type still belongs to only one plugin.

The host supplies ctx.upstream on DriverContext, TaskQueryContext, BatchQueryContext, and the buildContentRequest context as { kind: "vendor" } or { kind: "new_api" }. Treat a missing field as vendor. For new_api, construct the plugin's own native paths, such as /doubao/api/v3/... or /ali/api/v1/..., instead of copying vendor paths. No prefix change is needed when the paths already match native routes or the plugin uses only host protocols.

For type-60 channels, the host has already set authHeader to Bearer <channel key> and apiKey to the channel key; vendor OAuth/JWT authentication is not run. Submission parsers must accept the upstream plugin's native presentation, and queries use the public task ID returned by that gateway. upstreams is an additive API v1 extension: older hosts reject the unknown field, so upgrade the host before installation.

HTTP Descriptors and Files

Construction hooks return { url, method?, headers?, body?, ... }, which are validated and sent by the host. JSON is the default body type, and multipart can also be constructed via bodyType: "multipart" and parts.

Inbound body is uniformly parsed by the host into the following union type:

type RequestBody =
  | { kind: 'json'; value: unknown }
  | { kind: 'form'; fields: Record<string, readonly string[]> }
  | {
      kind: 'multipart';
      fields: Record<string, readonly string[]>;
      files: readonly FileReference[];
    }
  | { kind: 'none' };

Files are referenced into JavaScript only as { ref, field, filename, mimeType, size }; plugins cannot directly read file bytes. Multipart outbound uses parts[].fileRef; JSON outbound can embed placeholders, which the host replaces with encoded content:

{ __fileRef: "request_file:input_reference", encoding: "base64" }
{ __fileRef: "request_file:input_reference", encoding: "dataUrl", mimeType: "image/png" }

Placeholders can optionally have maxBytes; the host will still perform file size limits and total volume checks. References must not be treated as file paths.

A repeated field such as image[] can contain several files. Each file has its own ref: request_file:<field> for the first, then request_file:<field>#<index> (zero-based, so the second file uses #1). Copy the corresponding files[].ref into parts[].fileRef or the JSON placeholder; do not construct references by hand or collapse all files in one field into a single reference.

Native Routes and Host Protocols

Native Routes

meta.routes defines plugin-specific URLs, where function names point to synchronous functions within the native object:

routes: [
  {
    method: 'POST',
    path: '/vendor/jobs',
    type: 'submit',
    decode: 'create',
    render: 'created',
  },
  {
    method: 'GET',
    path: '/vendor/jobs/:task_id',
    type: 'query',
    render: 'status',
  },
];
  • submit / dynamic must specify decode and render; query only specifies render and cannot declare a decoder.
  • The default task parameter name for query is task_id, which can be specified via taskIdParam.
  • Decoders return { kind: "submit", model, action?, requestBody?, originTaskIds? } or a query intent.
  • routes[].models can restrict top-level models for submit/dynamic, but not for query; when models are nested within the vendor body, the decoder should determine them.
  • The host is responsible for authentication, ownership, and task persistence; renderers only handle external responses. Error messages thrown by hooks may be returned to the caller and should use readable error text without sensitive data.

originTaskIds uses public task IDs. After the host checks ownership and channel consistency, originTasks containing internal upstream IDs are injected into the driver; they are not passed to external renderers.

Result retention

routes[].retainResult is an optional boolean for submit/dynamic routes, defaults to true, and is prohibited on query routes. Set it to false only when every successful submission completes immediately. An immediate terminal result does not persist task.data; native queries, Responses/Video retrieval, and artifact listing/content subsequently cannot find the task. The task record remains for billing and logs. The submit presenter still receives the full in-memory data, and completion usage hooks run before that data is discarded.

If a route declaring false returns an asynchronous task, the host retains the result and logs a warning. Responses and Video always retain results. Image returns immediate terminal results inline without retaining result data; results completed by in-request polling retain the poller's snapshot. When a discarded result is referenced, originTasks[].data is null. Older hosts reject the retainResult field.

Host Protocols

meta.protocols declares protocol paths uniformly managed by the host and should not duplicate these paths in meta.routes:

ProtocolHost PathPlugin Export
openai_videoPOST /v1/videos, GET /v1/videos/{id}, GET / HEAD /v1/videos/{id}/contentprotocols.openai_video.decodeRequest and render
openai_responsesPOST /v1/responses, GET /v1/responses/{id}decodeRequest, and pattern-matching render hooks
openai_imagePOST /v1/images/generations, POST /v1/images/editsprotocols.openai_image.decodeRequest and render

Responses must explicitly declare supports as an object: stream requires renderEvents, sync or background requires renderFinal. Missing required hooks, or exporting hooks not used by any declared pattern, will result in rejection.

Decoders may execute multiple times after candidate filtering and channel selection, and should remain deterministic. Multiple plugins can share models under the same protocol; the actual plugin is determined by the selected channel.

Video render must return a JSON object; the host overrides standard ID, model, status, and time fields, and preserves compliant vendor extensions. Successful results of Responses reference artifacts via ctx.artifacts[key].url injected by the host.

OpenAI Image protocol

Declare "openai_image" or { name: "openai_image", models: [...] } without supports. Like openai_video, it defines no request modes; both allow string claims or objects with model restrictions. Generation accepts JSON; editing accepts JSON or multipart. The host pins ctx.model and provides ctx.operation: "generate" | "edit".

Export decodeRequest and render. A synchronous upstream returns a terminal immediate from parseSubmitResponse; an asynchronous upstream returns a task ID, and the host waits within the client request using the ordinary query hooks. TASK_PLUGIN_PROTOCOL_TIMEOUT_SECONDS defaults to 600 seconds; a timeout returns 504 task_timeout. After a timeout or client disconnect, the submitted task still completes and settles through background polling. Terminal task failure returns 400 with the failure reason.

On success, render(ctx, task) returns an object with a data array of { url } or { b64_json } entries, optionally including revised_prompt. The host adds created when absent. For response_format: "b64_json", it downloads image URLs and adds Base64 while preserving the URLs; failed downloads produce a warning and leave the URLs. This hook receives no ctx.artifacts; image URLs come from the upstream result.

Usage Hooks

Optional exports extractUsage, extractUsageOnSubmit, and extractUsageOnComplete extract usage from requests, submission results, or completion results, respectively. Only facts conforming to the selected schema are returned, not prices or quotas.

usageProfiles supplies complete schemas and examples for its models, replacing defaults without merging or inheritance. Runtime selection first matches the final upstream model. If that name has no profile (for example, it is mapped to a vendor endpoint ID), it falls back to the client-facing model, then to the plugin defaults. Saved prices are not automatically migrated. See Usage and Billing.

Numeric field descriptions name the billing subject and unit price, such as Image generation unit price / 图片生成单价; the actual field value remains a usage quantity. Put units in unit, count display words in unitLabel, and enum display names in enumLabels. Action, boolean, and enum descriptions name the action, the state represented by true, and the condition, respectively. Keep translations equivalent, without numeric prices, protocol details, or trailing punctuation.

Artifacts and Content Requests

Artifact hooks must be exported in pairs:

  • listArtifacts(task): Projects a stable list of { key, type, mimeType? } from persisted data, without returning a second persisted record or temporary download URL.
  • buildContentRequest(ctx): Constructs the read descriptor for this request based on the selected artifact key, data, production version, upstream task ID, channel information, and secure Range/conditional request headers.

Content requests with channel credentials can only access channel hosts or allowedHosts. Public dynamic CDNs can use credentialless: true; in this case, only GET/HEAD is allowed, no plugin headers or body can be attached, and the host will check the initial address and redirects.

Host artifact links use TaskPublicAddress, falling back to ServerAddress by default. Multi-node setups require a shared valid CRYPTO_SECRET; rotating it will invalidate issued addresses.

Immediate Completion, SSE, and Host Capabilities

parseSubmitResponse can return an immediate final state result, allowing the host to complete persistence and settlement during the submission phase; these tasks will not continue polling.

When upstream submission uses SSE, declare submitResponseTypes: ["json", "sse"] and select responseType: "sse" in the descriptor:

ModeRequired Declarations and ExportsData Flow
SnapshotparseSubmitEventEach event returns { state, done }, and the complete state after completion serves as the body of parseSubmitResponse
IncrementalrequiredCapabilities: ["submit-sse-delta@1"], parseSubmitEventDeltaReturns { changes, state, done }, the host applies set / append / appendText, and the body is formed upon completion

SSE mode does not directly pass upstream events to the client. The plugin interprets event semantics and termination conditions, while the host manages connections, frame parsing, size limits, and timeouts; read failures after successfully accepting upstream SSE will not automatically retry submission, to avoid creating duplicate billing tasks.

json-clone@1 provides a synchronous utils.json.clone(value) for creating mutable, independent JSON snapshots. Other utilities include time, UUID, Base64, HMAC, JWT, and Volc signature tools; see type declarations for full signatures. requiredCapabilities must declare the exact version; unknown or unsupported capabilities will be rejected at load time.

Management and Diagnostic Interfaces

The Root management interface is located at /api/plugin/task, including upload, version activation, status switching, deletion, marketplace source, dry run, and /runtime/status. These management operations are not part of the same permission system as /v1/tasks accessed with an API key.

The runtime is published atomically as a complete generation. Requests consistently use one generation, while background polling may use updated plugins. Multi-node troubleshooting should compare database override revisions, not directly compare the auto-incrementing generation numbers of each node.

For debugging steps, see Development Guide; for release checks, see Publishing Specification.

How is this guide?

Last updated on