A model's catalog entry now says what its workflow takes and makes:
comfyui.inputs, each a placeholder of its name with a type (text, int,
float, bool, choice, image, mask, audio, video, file), bounds, choices
and a default; and comfyui.output, which gains video, audio and files
beside pictures and meshes. POST /v1/workflows/runs with {model, inputs}
checks a request against them before a job is written — a required input
left out, an unknown one, a number out of bounds, a song sent as a
picture, another owner's file id are each refused with what was wrong —
and the job worker replays it to a replica, where the files go into
ComfyUI's input directory, the workflow is filled and run, and the first
file of the model's output kind is the job's result. /v1/models lists
each ComfyUI model's workflow, so a client can build its form.
A PNG result carries the licence in an iTXt chunk, as a GLB does in
asset.extras. A file whose server named no type is typed by its name
before its bytes, so a WAV is audio/wav, not audio/wave.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
527 lines
18 KiB
Go
527 lines
18 KiB
Go
// Package clientapi describes the API clients use, the one on :8080 — OpenAI's
|
|
// shape and what llmgw adds to it — for its OpenAPI document.
|
|
//
|
|
// The types here are the bodies as they go over the wire, written for the
|
|
// document: the handlers read a request's routing fields and pass the rest
|
|
// to the engine as it came, so a request may carry more than is listed, and
|
|
// an engine's answer may say more than is listed. The route table is checked
|
|
// against the router's own by a test, so an endpoint cannot be added without
|
|
// being described.
|
|
package clientapi
|
|
|
|
import "encoding/json"
|
|
|
|
// Error is every refusal: OpenAI's shape.
|
|
type Error struct {
|
|
Error ErrorBody `json:"error"`
|
|
}
|
|
|
|
// ErrorBody says why.
|
|
type ErrorBody struct {
|
|
Message string `json:"message"`
|
|
// Type is invalid_request_error, server_error, rate_limit_exceeded …
|
|
Type string `json:"type"`
|
|
// Code, where there is one a client branches on: model_not_found,
|
|
// content_policy_violation, job_not_found, system_stopped.
|
|
Code *string `json:"code"`
|
|
}
|
|
|
|
// ChatMessage is one turn.
|
|
type ChatMessage struct {
|
|
// Role is system, user, assistant or tool.
|
|
Role string `json:"role"`
|
|
// Content is a string, or a list of parts: text, and image_url for a
|
|
// model with vision.
|
|
Content json.RawMessage `json:"content"`
|
|
Name string `json:"name,omitempty"`
|
|
ToolCalls json.RawMessage `json:"tool_calls,omitempty"`
|
|
ToolCallID string `json:"tool_call_id,omitempty"`
|
|
}
|
|
|
|
// StreamOptions asks for usage in the last chunk of a stream.
|
|
type StreamOptions struct {
|
|
IncludeUsage bool `json:"include_usage"`
|
|
}
|
|
|
|
// ChatCompletionRequest is a chat turn asked of a model.
|
|
type ChatCompletionRequest struct {
|
|
// Model is a model's name or alias, or an adapter's name.
|
|
Model string `json:"model"`
|
|
Messages []ChatMessage `json:"messages"`
|
|
// Stream answers as server-sent events.
|
|
Stream bool `json:"stream,omitempty"`
|
|
StreamOptions *StreamOptions `json:"stream_options,omitempty"`
|
|
MaxTokens int `json:"max_tokens,omitempty"`
|
|
MaxCompletionTokens int `json:"max_completion_tokens,omitempty"`
|
|
Temperature *float64 `json:"temperature,omitempty"`
|
|
TopP *float64 `json:"top_p,omitempty"`
|
|
Seed *int64 `json:"seed,omitempty"`
|
|
// Stop is a string or a list of them.
|
|
Stop json.RawMessage `json:"stop,omitempty"`
|
|
Tools json.RawMessage `json:"tools,omitempty"`
|
|
ToolChoice json.RawMessage `json:"tool_choice,omitempty"`
|
|
ResponseFormat json.RawMessage `json:"response_format,omitempty"`
|
|
// User is the client's own name for whoever it serves, kept with the
|
|
// usage.
|
|
User string `json:"user,omitempty"`
|
|
// Metadata are tags kept with the usage, never sent to the engine.
|
|
Metadata map[string]string `json:"metadata,omitempty"`
|
|
}
|
|
|
|
// Usage is what a request used.
|
|
type Usage struct {
|
|
PromptTokens int `json:"prompt_tokens"`
|
|
CompletionTokens int `json:"completion_tokens"`
|
|
TotalTokens int `json:"total_tokens"`
|
|
}
|
|
|
|
// ChatChoice is one answer.
|
|
type ChatChoice struct {
|
|
Index int `json:"index"`
|
|
Message ChatMessage `json:"message"`
|
|
// FinishReason is stop, length or tool_calls.
|
|
FinishReason string `json:"finish_reason"`
|
|
}
|
|
|
|
// ChatCompletion is a model's answer.
|
|
type ChatCompletion struct {
|
|
ID string `json:"id"`
|
|
Object string `json:"object"`
|
|
Created int64 `json:"created"`
|
|
Model string `json:"model"`
|
|
Choices []ChatChoice `json:"choices"`
|
|
Usage Usage `json:"usage"`
|
|
}
|
|
|
|
// CompletionRequest is a prompt to continue.
|
|
type CompletionRequest struct {
|
|
Model string `json:"model"`
|
|
// Prompt is a string or a list of them.
|
|
Prompt json.RawMessage `json:"prompt"`
|
|
MaxTokens int `json:"max_tokens,omitempty"`
|
|
Temperature *float64 `json:"temperature,omitempty"`
|
|
Stream bool `json:"stream,omitempty"`
|
|
Stop json.RawMessage `json:"stop,omitempty"`
|
|
Seed *int64 `json:"seed,omitempty"`
|
|
}
|
|
|
|
// CompletionChoice is one continuation.
|
|
type CompletionChoice struct {
|
|
Index int `json:"index"`
|
|
Text string `json:"text"`
|
|
FinishReason string `json:"finish_reason"`
|
|
}
|
|
|
|
// Completion is a continued prompt.
|
|
type Completion struct {
|
|
ID string `json:"id"`
|
|
Object string `json:"object"`
|
|
Created int64 `json:"created"`
|
|
Model string `json:"model"`
|
|
Choices []CompletionChoice `json:"choices"`
|
|
Usage Usage `json:"usage"`
|
|
}
|
|
|
|
// EmbeddingRequest asks for vectors.
|
|
type EmbeddingRequest struct {
|
|
Model string `json:"model"`
|
|
// Input is a string or a list of them; each is embedded on its own, and
|
|
// the longest must fit the model's context.
|
|
Input json.RawMessage `json:"input"`
|
|
EncodingFormat string `json:"encoding_format,omitempty"`
|
|
Dimensions int `json:"dimensions,omitempty"`
|
|
}
|
|
|
|
// Embedding is one input's vector.
|
|
type Embedding struct {
|
|
Object string `json:"object"`
|
|
Index int `json:"index"`
|
|
Embedding []float64 `json:"embedding"`
|
|
}
|
|
|
|
// EmbeddingList is every input's vector.
|
|
type EmbeddingList struct {
|
|
Object string `json:"object"`
|
|
Data []Embedding `json:"data"`
|
|
Model string `json:"model"`
|
|
Usage Usage `json:"usage"`
|
|
}
|
|
|
|
// RerankRequest orders documents by a query, for a reranker vLLM serves.
|
|
type RerankRequest struct {
|
|
Model string `json:"model"`
|
|
Query string `json:"query"`
|
|
Documents []string `json:"documents"`
|
|
TopN int `json:"top_n,omitempty"`
|
|
}
|
|
|
|
// ScoreRequest scores pairs of texts, for a scorer vLLM serves.
|
|
type ScoreRequest struct {
|
|
Model string `json:"model"`
|
|
// TextA and TextB are strings or lists of them.
|
|
TextA json.RawMessage `json:"text_1"`
|
|
TextB json.RawMessage `json:"text_2"`
|
|
}
|
|
|
|
// TranscriptionForm is speech to text, sent as a form.
|
|
type TranscriptionForm struct {
|
|
// File is the audio.
|
|
File []byte `json:"file"`
|
|
Model string `json:"model"`
|
|
Language string `json:"language,omitempty"`
|
|
Prompt string `json:"prompt,omitempty"`
|
|
ResponseFormat string `json:"response_format,omitempty"`
|
|
Temperature float64 `json:"temperature,omitempty"`
|
|
}
|
|
|
|
// Transcription is the text.
|
|
type Transcription struct {
|
|
Text string `json:"text"`
|
|
}
|
|
|
|
// ImageRequest asks for pictures, of a model that makes them.
|
|
type ImageRequest struct {
|
|
Model string `json:"model"`
|
|
Prompt string `json:"prompt"`
|
|
// N is how many, at most 4.
|
|
N int `json:"n,omitempty"`
|
|
// Size is WIDTHxHEIGHT, each a multiple of 16 from 256 to 2048.
|
|
Size string `json:"size,omitempty"`
|
|
// ResponseFormat is b64_json, the only one offered.
|
|
ResponseFormat string `json:"response_format,omitempty"`
|
|
Seed *int64 `json:"seed,omitempty"`
|
|
Steps int `json:"steps,omitempty"`
|
|
NegativePrompt string `json:"negative_prompt,omitempty"`
|
|
Guidance *float64 `json:"guidance,omitempty"`
|
|
}
|
|
|
|
// Image is one picture.
|
|
type Image struct {
|
|
B64JSON string `json:"b64_json"`
|
|
}
|
|
|
|
// ImagesResponse is the pictures made; x-llmgw-seed says the seed.
|
|
type ImagesResponse struct {
|
|
Created int64 `json:"created"`
|
|
Data []Image `json:"data"`
|
|
}
|
|
|
|
// ModerationRequest asks the guardrails' judge.
|
|
type ModerationRequest struct {
|
|
// Input is a string or up to 32 of them.
|
|
Input json.RawMessage `json:"input"`
|
|
Model string `json:"model,omitempty"`
|
|
}
|
|
|
|
// ModerationResult is one input's verdict.
|
|
type ModerationResult struct {
|
|
Flagged bool `json:"flagged"`
|
|
Categories map[string]bool `json:"categories"`
|
|
CategoryScores map[string]float64 `json:"category_scores"`
|
|
}
|
|
|
|
// ModerationResponse is every input's verdict.
|
|
type ModerationResponse struct {
|
|
ID string `json:"id"`
|
|
Model string `json:"model"`
|
|
Results []ModerationResult `json:"results"`
|
|
}
|
|
|
|
// ResponsesRequest is OpenAI's Responses API, translated to chat completions.
|
|
type ResponsesRequest struct {
|
|
Model string `json:"model"`
|
|
// Input is a string or a list of items: messages, function calls and
|
|
// their outputs.
|
|
Input json.RawMessage `json:"input"`
|
|
Instructions string `json:"instructions,omitempty"`
|
|
Stream bool `json:"stream,omitempty"`
|
|
Tools json.RawMessage `json:"tools,omitempty"`
|
|
// Text carries format: a JSON schema the answer follows.
|
|
Text json.RawMessage `json:"text,omitempty"`
|
|
MaxOutputTokens int `json:"max_output_tokens,omitempty"`
|
|
}
|
|
|
|
// MessagesRequest is Anthropic's Messages API, translated to chat completions.
|
|
type MessagesRequest struct {
|
|
Model string `json:"model"`
|
|
Messages json.RawMessage `json:"messages"`
|
|
System json.RawMessage `json:"system,omitempty"`
|
|
MaxTokens int `json:"max_tokens"`
|
|
Stream bool `json:"stream,omitempty"`
|
|
Tools json.RawMessage `json:"tools,omitempty"`
|
|
}
|
|
|
|
// TokenCount is how many tokens a Messages request would be.
|
|
type TokenCount struct {
|
|
InputTokens int `json:"input_tokens"`
|
|
}
|
|
|
|
// License is the terms what a model makes comes under.
|
|
type License struct {
|
|
// ID is SPDX where there is one, else the licence's own short name.
|
|
ID string `json:"id,omitempty"`
|
|
Name string `json:"name"`
|
|
URL string `json:"url,omitempty"`
|
|
Notice string `json:"notice,omitempty"`
|
|
// Commercial is whether the output may be used commercially; absent is
|
|
// not said.
|
|
Commercial *bool `json:"commercial,omitempty"`
|
|
// ExcludedRegions are ISO 3166 codes, EU for the union, where it grants
|
|
// nothing.
|
|
ExcludedRegions []string `json:"excluded_regions,omitempty"`
|
|
RevenueCapUSD int64 `json:"revenue_cap_usd,omitempty"`
|
|
Attribution string `json:"attribution,omitempty"`
|
|
}
|
|
|
|
// WorkflowInfo is what a ComfyUI model takes and makes, for a run at
|
|
// /v1/workflows/runs.
|
|
type WorkflowInfo struct {
|
|
// Output is pictures, meshes, video, audio or files.
|
|
Output string `json:"output"`
|
|
Inputs []WorkflowInput `json:"inputs"`
|
|
}
|
|
|
|
// WorkflowInput is one input a workflow takes.
|
|
type WorkflowInput struct {
|
|
Name string `json:"name"`
|
|
// Type is text, int, float, bool, choice, image, mask, audio, video or
|
|
// file; the last five are sent as base64, a data URL, or a file's id.
|
|
Type string `json:"type"`
|
|
Required bool `json:"required,omitempty"`
|
|
Default any `json:"default,omitempty"`
|
|
Min *float64 `json:"min,omitempty"`
|
|
Max *float64 `json:"max,omitempty"`
|
|
Choices []string `json:"choices,omitempty"`
|
|
MaxMB int `json:"max_mb,omitempty"`
|
|
Doc string `json:"doc,omitempty"`
|
|
}
|
|
|
|
// RunRequest asks a ComfyUI model to run its workflow.
|
|
type RunRequest struct {
|
|
Model string `json:"model"`
|
|
// Inputs are by name, as the model's workflow lists them in /v1/models.
|
|
Inputs map[string]any `json:"inputs"`
|
|
// Seed is the workflow's {{seed}}, where it has one; random otherwise.
|
|
Seed *int64 `json:"seed,omitempty"`
|
|
}
|
|
|
|
// ModelInfo is a model a key may use.
|
|
type ModelInfo struct {
|
|
ID string `json:"id"`
|
|
Object string `json:"object"`
|
|
Created int64 `json:"created"`
|
|
OwnedBy string `json:"owned_by"`
|
|
// Loaded is whether a replica is up now; a model that loads when asked
|
|
// is listed asleep too, and its first request waits for it to load.
|
|
Loaded bool `json:"loaded"`
|
|
// ContextLength and MaxOutputTokens, where the catalog says.
|
|
ContextLength int `json:"context_length,omitempty"`
|
|
MaxOutputTokens int `json:"max_output_tokens,omitempty"`
|
|
Description string `json:"description,omitempty"`
|
|
// Capabilities: tools, vision, embeddings, images, meshes, workflows.
|
|
Capabilities map[string]bool `json:"capabilities"`
|
|
License *License `json:"license,omitempty"`
|
|
// Workflow is what a ComfyUI model takes and makes.
|
|
Workflow *WorkflowInfo `json:"workflow,omitempty"`
|
|
}
|
|
|
|
// ModelList is the models a key may use.
|
|
type ModelList struct {
|
|
Object string `json:"object"`
|
|
Data []ModelInfo `json:"data"`
|
|
}
|
|
|
|
// FileForm is a file uploaded, as a form.
|
|
type FileForm struct {
|
|
File []byte `json:"file"`
|
|
// Purpose is batch for a batch's input.
|
|
Purpose string `json:"purpose"`
|
|
}
|
|
|
|
// File is a file kept: a batch's input or output, a job's result.
|
|
type File struct {
|
|
ID string `json:"id"`
|
|
Object string `json:"object"`
|
|
Bytes int64 `json:"bytes"`
|
|
CreatedAt int64 `json:"created_at"`
|
|
Filename string `json:"filename"`
|
|
// Purpose is batch, batch_output or job_output.
|
|
Purpose string `json:"purpose"`
|
|
Status string `json:"status"`
|
|
ExpiresAt int64 `json:"expires_at,omitempty"`
|
|
}
|
|
|
|
// FileList is the caller's files.
|
|
type FileList struct {
|
|
Object string `json:"object"`
|
|
Data []File `json:"data"`
|
|
}
|
|
|
|
// Deleted says a thing is gone.
|
|
type Deleted struct {
|
|
ID string `json:"id"`
|
|
Object string `json:"object"`
|
|
Deleted bool `json:"deleted"`
|
|
}
|
|
|
|
// BatchRequest runs a file of requests, at the lowest priority.
|
|
type BatchRequest struct {
|
|
InputFileID string `json:"input_file_id"`
|
|
// Endpoint is /v1/chat/completions, /v1/completions or /v1/embeddings.
|
|
Endpoint string `json:"endpoint"`
|
|
// CompletionWindow is 24h.
|
|
CompletionWindow string `json:"completion_window"`
|
|
Metadata map[string]string `json:"metadata,omitempty"`
|
|
}
|
|
|
|
// RequestCounts is how a batch's lines went.
|
|
type RequestCounts struct {
|
|
Total int `json:"total"`
|
|
Completed int `json:"completed"`
|
|
Failed int `json:"failed"`
|
|
}
|
|
|
|
// Batch is a batch and how far it is.
|
|
type Batch struct {
|
|
ID string `json:"id"`
|
|
Object string `json:"object"`
|
|
Endpoint string `json:"endpoint"`
|
|
InputFileID string `json:"input_file_id"`
|
|
CompletionWindow string `json:"completion_window"`
|
|
Status string `json:"status"`
|
|
OutputFileID *string `json:"output_file_id"`
|
|
ErrorFileID *string `json:"error_file_id"`
|
|
CreatedAt int64 `json:"created_at"`
|
|
InProgressAt *int64 `json:"in_progress_at"`
|
|
ExpiresAt *int64 `json:"expires_at"`
|
|
FinalizingAt *int64 `json:"finalizing_at"`
|
|
CompletedAt *int64 `json:"completed_at"`
|
|
FailedAt *int64 `json:"failed_at"`
|
|
ExpiredAt *int64 `json:"expired_at"`
|
|
CancellingAt *int64 `json:"cancelling_at"`
|
|
CancelledAt *int64 `json:"cancelled_at"`
|
|
RequestCounts RequestCounts `json:"request_counts"`
|
|
Errors json.RawMessage `json:"errors"`
|
|
Metadata json.RawMessage `json:"metadata"`
|
|
}
|
|
|
|
// BatchList is the caller's batches.
|
|
type BatchList struct {
|
|
Object string `json:"object"`
|
|
Data []Batch `json:"data"`
|
|
HasMore bool `json:"has_more"`
|
|
}
|
|
|
|
// MeshRequest asks for a 3D model from a picture, of a model that makes
|
|
// them: a job.
|
|
type MeshRequest struct {
|
|
Model string `json:"model"`
|
|
// Image is the picture, base64 or a data URL: PNG, JPEG or WebP, one
|
|
// object on a plain background.
|
|
Image string `json:"image"`
|
|
Seed *int64 `json:"seed,omitempty"`
|
|
Steps int `json:"steps,omitempty"`
|
|
// Resolution is the model's grid, a multiple of 32 from 64 to 2048.
|
|
Resolution int `json:"resolution,omitempty"`
|
|
}
|
|
|
|
// JobProgress is how far along a job is.
|
|
type JobProgress struct {
|
|
// State is queued, running, done or failed.
|
|
State string `json:"state"`
|
|
// Ahead is how many wait before it, while queued.
|
|
Ahead int `json:"ahead,omitempty"`
|
|
NodeClass string `json:"node_class,omitempty"`
|
|
Step int `json:"step,omitempty"`
|
|
Steps int `json:"steps,omitempty"`
|
|
Percent int `json:"percent,omitempty"`
|
|
}
|
|
|
|
// JobUsage is what a finished job used.
|
|
type JobUsage struct {
|
|
PromptTokens int64 `json:"prompt_tokens"`
|
|
CompletionTokens int64 `json:"completion_tokens"`
|
|
Images int64 `json:"images"`
|
|
Meshes int64 `json:"meshes"`
|
|
}
|
|
|
|
// Job is a 3D model asked for: its status, progress and, when done, the
|
|
// file its result is in.
|
|
type Job struct {
|
|
ID string `json:"id"`
|
|
Object string `json:"object"`
|
|
Endpoint string `json:"endpoint"`
|
|
Model string `json:"model"`
|
|
// Status is queued, running, cancelling, completed, failed, cancelled
|
|
// or expired.
|
|
Status string `json:"status"`
|
|
Progress *JobProgress `json:"progress"`
|
|
OutputFileID *string `json:"output_file_id"`
|
|
ContentType *string `json:"content_type"`
|
|
Error *ErrorBody `json:"error"`
|
|
CreatedAt int64 `json:"created_at"`
|
|
StartedAt *int64 `json:"started_at"`
|
|
FinishedAt *int64 `json:"finished_at"`
|
|
ExpiresAt *int64 `json:"expires_at"`
|
|
Usage *JobUsage `json:"usage"`
|
|
License *License `json:"license,omitempty"`
|
|
}
|
|
|
|
// JobList is the caller's jobs.
|
|
type JobList struct {
|
|
Object string `json:"object"`
|
|
Data []Job `json:"data"`
|
|
HasMore bool `json:"has_more"`
|
|
FirstID *string `json:"first_id"`
|
|
LastID *string `json:"last_id"`
|
|
}
|
|
|
|
// MemoryWrite is a note kept for the caller's project.
|
|
type MemoryWrite struct {
|
|
Text string `json:"text"`
|
|
Tags []string `json:"tags,omitempty"`
|
|
}
|
|
|
|
// MemoryNote is a note kept.
|
|
type MemoryNote struct {
|
|
Key string `json:"key"`
|
|
Text string `json:"text"`
|
|
Tags []string `json:"tags"`
|
|
CreatedAt string `json:"created_at"`
|
|
UpdatedAt string `json:"updated_at"`
|
|
}
|
|
|
|
// MemoryList is the project's notes.
|
|
type MemoryList struct {
|
|
Object string `json:"object"`
|
|
Data []MemoryNote `json:"data"`
|
|
}
|
|
|
|
// Me is the caller as the gateway sees it: its key, place and limits.
|
|
type Me struct {
|
|
Authenticated bool `json:"authenticated"`
|
|
KeyID string `json:"key_id,omitempty"`
|
|
Key string `json:"key,omitempty"`
|
|
Project string `json:"project,omitempty"`
|
|
Team string `json:"team,omitempty"`
|
|
Organization string `json:"organization,omitempty"`
|
|
// Limits and Usage are what applies and what has been used of it.
|
|
Limits json.RawMessage `json:"limits,omitempty"`
|
|
Usage json.RawMessage `json:"usage,omitempty"`
|
|
}
|
|
|
|
// MCPRequest is a JSON-RPC 2.0 call to the gateway's MCP server: its tools
|
|
// are the memory notes.
|
|
type MCPRequest struct {
|
|
JSONRPC string `json:"jsonrpc"`
|
|
ID json.RawMessage `json:"id"`
|
|
Method string `json:"method"`
|
|
Params json.RawMessage `json:"params,omitempty"`
|
|
}
|
|
|
|
// KBDeleted says a document left a collection.
|
|
type KBDeleted struct {
|
|
Name string `json:"name"`
|
|
Deleted bool `json:"deleted"`
|
|
}
|