325 lines
16 KiB
Go
325 lines
16 KiB
Go
package httpapi
|
|
|
|
func openAPIDocument(origin string) map[string]any {
|
|
return map[string]any{
|
|
"openapi": "3.1.0",
|
|
"info": map[string]any{
|
|
"title": "智念AIGC平台 Public API",
|
|
"version": "1.0.0",
|
|
"description": "Public server-to-server API for uploading assets, creating image/video generation jobs, polling job status, downloading outputs, and receiving webhooks.",
|
|
},
|
|
"servers": []any{map[string]any{"url": origin, "description": "Current deployment"}},
|
|
"security": []any{
|
|
map[string]any{"bearerApiKey": []any{}},
|
|
map[string]any{"headerApiKey": []any{}},
|
|
},
|
|
"components": map[string]any{
|
|
"securitySchemes": map[string]any{
|
|
"bearerApiKey": map[string]any{"type": "http", "scheme": "bearer"},
|
|
"headerApiKey": map[string]any{"type": "apiKey", "in": "header", "name": "X-Zhinian-Api-Key"},
|
|
},
|
|
"schemas": openAPISchemas(),
|
|
},
|
|
"paths": openAPIPaths(),
|
|
}
|
|
}
|
|
|
|
func openAPISchemas() map[string]any {
|
|
return map[string]any{
|
|
"ErrorResponse": map[string]any{
|
|
"type": "object", "required": []string{"error"},
|
|
"properties": map[string]any{"error": map[string]any{"type": "string"}},
|
|
},
|
|
"Asset": map[string]any{
|
|
"type": "object",
|
|
"required": []string{"id", "kind", "name", "url", "source", "createdAt"},
|
|
"properties": map[string]any{
|
|
"id": map[string]any{"type": "string", "example": "asset_mpqe9g85_12635f8cd8"},
|
|
"ownerId": map[string]any{"type": "string"},
|
|
"kind": map[string]any{"type": "string", "enum": []string{"image", "video", "mask", "reference", "other"}},
|
|
"name": map[string]any{"type": "string", "example": "result.png"},
|
|
"url": map[string]any{"type": "string", "format": "uri"},
|
|
"storagePath": map[string]any{"type": "string"},
|
|
"source": map[string]any{"type": "string", "enum": []string{"upload", "generated", "external", "seed"}},
|
|
"tags": map[string]any{"type": "array", "items": map[string]any{"type": "string"}},
|
|
"metadata": map[string]any{"type": "object", "additionalProperties": true},
|
|
"createdAt": map[string]any{"type": "string", "format": "date-time"},
|
|
"updatedAt": map[string]any{"type": "string", "format": "date-time"},
|
|
},
|
|
},
|
|
"GenerationJob": map[string]any{
|
|
"type": "object",
|
|
"required": []string{"id", "capability", "provider", "status", "createdAt", "updatedAt"},
|
|
"properties": map[string]any{
|
|
"id": map[string]any{"type": "string", "example": "job_mpqe3wtt_12ed738079"},
|
|
"ownerId": map[string]any{"type": "string", "example": "api:partner-a"},
|
|
"externalClientId": map[string]any{"type": "string"},
|
|
"capability": openAPIRef("GenerationCapability"),
|
|
"provider": map[string]any{"type": "string", "enum": []string{"volcengine-visual", "evolink", "seedance", "seedream", "bailian"}},
|
|
"reqKey": map[string]any{"type": "string"},
|
|
"status": openAPIRef("GenerationStatus"),
|
|
"prompt": map[string]any{"type": "string"},
|
|
"inputAssetIds": openAPIStringArray(),
|
|
"inputUrls": map[string]any{"type": "array", "items": map[string]any{"type": "string", "format": "uri"}},
|
|
"outputAssetIds": openAPIStringArray(),
|
|
"providerTaskId": map[string]any{"type": "string"},
|
|
"error": openAPIJobError(),
|
|
"idempotencyKey": map[string]any{"type": "string"},
|
|
"priority": map[string]any{"type": "integer"},
|
|
"attempts": map[string]any{"type": "integer"},
|
|
"scheduledAt": map[string]any{"type": "string", "format": "date-time"},
|
|
"completedAt": map[string]any{"type": "string", "format": "date-time"},
|
|
"webhookUrl": map[string]any{"type": "string", "format": "uri"},
|
|
"webhookAttempts": map[string]any{"type": "integer"},
|
|
"webhookLastStatus": map[string]any{"type": "object", "additionalProperties": true},
|
|
"createdAt": map[string]any{"type": "string", "format": "date-time"},
|
|
"updatedAt": map[string]any{"type": "string", "format": "date-time"},
|
|
},
|
|
},
|
|
"GenerationCapability": map[string]any{"type": "string", "enum": []string{"image.generate", "video.generate"}},
|
|
"GenerationStatus": map[string]any{"type": "string", "enum": []string{"queued", "running", "succeeded", "failed", "expired", "cancelled"}},
|
|
"PromptMaterial": map[string]any{
|
|
"type": "object",
|
|
"properties": map[string]any{
|
|
"id": map[string]any{"type": "string"},
|
|
"url": map[string]any{"type": "string", "format": "uri"},
|
|
"type": map[string]any{"type": "string", "enum": []string{"image", "video", "audio"}},
|
|
"role": map[string]any{"type": "string"},
|
|
"label": map[string]any{"type": "string"},
|
|
"name": map[string]any{"type": "string"},
|
|
},
|
|
},
|
|
"CreateJobRequest": map[string]any{
|
|
"type": "object",
|
|
"required": []string{"capability"},
|
|
"properties": map[string]any{
|
|
"capability": openAPIRef("GenerationCapability"),
|
|
"engine": map[string]any{"type": "string", "enum": []string{"jimeng", "seedream", "evolink", "bailian", "seedance"}},
|
|
"model": map[string]any{"type": "string", "enum": []string{"doubao-seedance-2-0-260128", "doubao-seedance-2-5-260628"}, "description": "Optional allow-listed Seedance model for video.generate."},
|
|
"prompt": map[string]any{"type": "string", "description": "Prompt text. Required for generation except Seedream layer decomposition, where an empty prompt triggers automatic decomposition."},
|
|
"inputUrls": map[string]any{"type": "array", "items": map[string]any{"type": "string", "format": "uri"}, "description": "Reference image URLs for image capabilities."},
|
|
"imageUrls": map[string]any{"type": "array", "items": map[string]any{"type": "string", "format": "uri"}, "description": "Alias for image input URLs."},
|
|
"inputAssetIds": openAPIStringArray(),
|
|
"materials": map[string]any{"type": "array", "items": openAPIRef("PromptMaterial")},
|
|
"settings": map[string]any{
|
|
"type": "object", "description": "Provider-specific image or video settings.",
|
|
"properties": map[string]any{
|
|
"ratio": map[string]any{"type": "string", "enum": []string{"16:9", "4:3", "1:1", "3:4", "9:16", "21:9", "adaptive"}},
|
|
"duration": map[string]any{"type": "integer", "minimum": 4, "maximum": 30, "description": "Seedance 2.0 supports up to 15 seconds; Seedance 2.5 supports up to 30 seconds."},
|
|
"resolution": map[string]any{"type": "string", "enum": []string{"480p", "720p", "1080p"}},
|
|
"size": map[string]any{"type": "string", "enum": []string{"auto", "1K", "1.5K", "2K"}, "description": "Seedream 5.0 Pro output size. auto is available for layer decomposition only."},
|
|
"outputFormat": map[string]any{"type": "string", "enum": []string{"png", "jpeg"}},
|
|
"optimizeMode": map[string]any{"type": "string", "enum": []string{"standard", "fast"}},
|
|
"creationMode": map[string]any{"type": "string", "enum": []string{"basic", "interactive", "layers"}, "description": "Seedream 5.0 Pro image workflow."},
|
|
"layerDecomposition": map[string]any{"type": "boolean", "description": "Enables Seedream 5.0 Pro layer decomposition; exactly one image is required."},
|
|
},
|
|
},
|
|
"width": map[string]any{"type": "integer", "example": 1440},
|
|
"height": map[string]any{"type": "integer", "example": 2560},
|
|
"scale": map[string]any{"type": "number", "minimum": 1, "maximum": 100, "description": "Jimeng text influence for image.generate."},
|
|
"force_single": map[string]any{"type": "boolean"},
|
|
"quality": map[string]any{"type": "string", "enum": []string{"low", "medium", "high"}, "description": "EvoLink image quality for image.generate."},
|
|
"priority": map[string]any{"type": "integer", "minimum": -100, "maximum": 100},
|
|
"webhookUrl": map[string]any{"type": "string", "format": "uri"},
|
|
"idempotencyKey": map[string]any{"type": "string", "description": "Optional body-level idempotency key. Header Idempotency-Key is preferred."},
|
|
},
|
|
},
|
|
"RegisterAssetRequest": map[string]any{
|
|
"type": "object", "required": []string{"url"},
|
|
"properties": map[string]any{
|
|
"url": map[string]any{"type": "string", "format": "uri"},
|
|
"name": map[string]any{"type": "string"},
|
|
"kind": map[string]any{"type": "string", "enum": []string{"image", "video", "mask", "reference", "other"}},
|
|
"tags": openAPIStringArray(),
|
|
},
|
|
},
|
|
"WebhookPayload": map[string]any{
|
|
"type": "object",
|
|
"required": []string{"jobId", "status", "capability", "outputAssetIds", "updatedAt"},
|
|
"properties": map[string]any{
|
|
"jobId": map[string]any{"type": "string", "example": "job_mpqe3wtt_12ed738079"},
|
|
"status": openAPIRef("GenerationStatus"),
|
|
"capability": openAPIRef("GenerationCapability"),
|
|
"outputAssetIds": openAPIStringArray(),
|
|
"error": openAPIJobError(),
|
|
"updatedAt": map[string]any{"type": "string", "format": "date-time"},
|
|
},
|
|
},
|
|
}
|
|
}
|
|
|
|
func openAPIPaths() map[string]any {
|
|
return map[string]any{
|
|
"/api/v1/capabilities": map[string]any{
|
|
"get": map[string]any{
|
|
"summary": "List generation capabilities",
|
|
"responses": map[string]any{"200": openAPIJSONResponse("Capabilities and active providers", nil)},
|
|
},
|
|
},
|
|
"/api/v1/assets": map[string]any{
|
|
"get": map[string]any{
|
|
"summary": "List assets visible to the authenticated API client",
|
|
"responses": map[string]any{"200": openAPIJSONResponse("Assets", map[string]any{
|
|
"type": "object", "properties": map[string]any{"assets": map[string]any{"type": "array", "items": openAPIRef("Asset")}},
|
|
})},
|
|
},
|
|
"post": map[string]any{
|
|
"summary": "Upload files or register an external asset URL",
|
|
"requestBody": map[string]any{
|
|
"required": true,
|
|
"content": map[string]any{
|
|
"application/json": map[string]any{"schema": openAPIRef("RegisterAssetRequest")},
|
|
"multipart/form-data": map[string]any{"schema": map[string]any{
|
|
"type": "object", "properties": map[string]any{"files": map[string]any{"type": "array", "items": map[string]any{"type": "string", "format": "binary"}}},
|
|
}},
|
|
},
|
|
},
|
|
"responses": map[string]any{
|
|
"201": openAPIJSONResponse("Created asset", nil),
|
|
"400": openAPIErrorResponse(), "401": openAPIErrorResponse(),
|
|
},
|
|
},
|
|
},
|
|
"/api/v1/assets/{id}": map[string]any{
|
|
"get": map[string]any{
|
|
"summary": "Get one asset visible to the authenticated API client",
|
|
"parameters": []any{openAPIPathID()},
|
|
"responses": map[string]any{
|
|
"200": openAPIJSONResponse("Asset", map[string]any{"type": "object", "properties": map[string]any{"asset": openAPIRef("Asset")}}),
|
|
"404": openAPIErrorResponse(),
|
|
},
|
|
},
|
|
},
|
|
"/api/v1/assets/{id}/download": map[string]any{
|
|
"get": map[string]any{
|
|
"summary": "Download an output or uploaded asset",
|
|
"parameters": []any{openAPIPathID()},
|
|
"responses": map[string]any{
|
|
"200": map[string]any{
|
|
"description": "Binary file",
|
|
"headers": map[string]any{
|
|
"Content-Disposition": map[string]any{"schema": map[string]any{"type": "string"}},
|
|
"Content-Length": map[string]any{"schema": map[string]any{"type": "string"}},
|
|
},
|
|
"content": map[string]any{
|
|
"application/octet-stream": openAPIBinaryContent(),
|
|
"image/png": openAPIBinaryContent(),
|
|
"image/jpeg": openAPIBinaryContent(),
|
|
"video/mp4": openAPIBinaryContent(),
|
|
},
|
|
},
|
|
"404": openAPIErrorResponse(),
|
|
},
|
|
},
|
|
},
|
|
"/api/v1/jobs": map[string]any{
|
|
"get": map[string]any{
|
|
"summary": "List jobs for the authenticated API client",
|
|
"parameters": []any{
|
|
openAPIQueryParameter("status", openAPIRef("GenerationStatus")),
|
|
openAPIQueryParameter("capability", openAPIRef("GenerationCapability")),
|
|
openAPIQueryParameter("limit", map[string]any{"type": "integer", "minimum": 1, "maximum": 200}),
|
|
openAPIQueryParameter("before", map[string]any{"type": "string", "format": "date-time"}),
|
|
},
|
|
"responses": map[string]any{"200": openAPIJSONResponse("Jobs", map[string]any{
|
|
"type": "object", "properties": map[string]any{"jobs": map[string]any{"type": "array", "items": openAPIRef("GenerationJob")}},
|
|
})},
|
|
},
|
|
"post": map[string]any{
|
|
"summary": "Create a queued generation job",
|
|
"parameters": []any{map[string]any{
|
|
"name": "Idempotency-Key", "in": "header", "required": false,
|
|
"schema": map[string]any{"type": "string"},
|
|
"description": "Reuse the same key for safe retries with the same request body.",
|
|
}},
|
|
"requestBody": map[string]any{
|
|
"required": true,
|
|
"content": map[string]any{"application/json": map[string]any{
|
|
"schema": openAPIRef("CreateJobRequest"),
|
|
"examples": map[string]any{
|
|
"imageGenerate": map[string]any{"summary": "Image generation", "value": map[string]any{
|
|
"capability": "image.generate", "prompt": "生成一张专业产品主图", "width": 1440, "height": 2560,
|
|
"webhookUrl": "https://example.com/zhinian/webhook",
|
|
}},
|
|
"videoGenerate": map[string]any{"summary": "Video generation", "value": map[string]any{
|
|
"capability": "video.generate", "prompt": "生成一条 9:16 品牌短视频",
|
|
"model": "doubao-seedance-2-5-260628",
|
|
"settings": map[string]any{"ratio": "9:16", "duration": 5, "resolution": "720p"},
|
|
}},
|
|
},
|
|
}},
|
|
},
|
|
"responses": map[string]any{
|
|
"202": openAPIJSONResponse("Queued job", map[string]any{
|
|
"type": "object", "properties": map[string]any{"job": openAPIRef("GenerationJob"), "reused": map[string]any{"type": "boolean"}},
|
|
}),
|
|
"409": openAPIErrorResponse(),
|
|
},
|
|
},
|
|
},
|
|
"/api/v1/jobs/{id}": map[string]any{
|
|
"get": map[string]any{
|
|
"summary": "Get one job", "parameters": []any{openAPIPathID()},
|
|
"responses": map[string]any{
|
|
"200": openAPIJSONResponse("Job", map[string]any{"type": "object", "properties": map[string]any{"job": openAPIRef("GenerationJob")}}),
|
|
"404": openAPIErrorResponse(),
|
|
},
|
|
},
|
|
},
|
|
"/api/v1/jobs/{id}/cancel": map[string]any{
|
|
"post": map[string]any{
|
|
"summary": "Cancel a queued or running job", "parameters": []any{openAPIPathID()},
|
|
"responses": map[string]any{
|
|
"200": openAPIJSONResponse("Cancelled job", map[string]any{"type": "object", "properties": map[string]any{"job": openAPIRef("GenerationJob")}}),
|
|
"404": openAPIErrorResponse(),
|
|
},
|
|
},
|
|
},
|
|
}
|
|
}
|
|
|
|
func openAPIRef(name string) map[string]any {
|
|
return map[string]any{"$ref": "#/components/schemas/" + name}
|
|
}
|
|
|
|
func openAPIStringArray() map[string]any {
|
|
return map[string]any{"type": "array", "items": map[string]any{"type": "string"}}
|
|
}
|
|
|
|
func openAPIJobError() map[string]any {
|
|
return map[string]any{
|
|
"type": "object",
|
|
"properties": map[string]any{
|
|
"code": map[string]any{"oneOf": []any{map[string]any{"type": "string"}, map[string]any{"type": "number"}}},
|
|
"message": map[string]any{"type": "string"},
|
|
"retryable": map[string]any{"type": "boolean"},
|
|
},
|
|
}
|
|
}
|
|
|
|
func openAPIPathID() map[string]any {
|
|
return map[string]any{"name": "id", "in": "path", "required": true, "schema": map[string]any{"type": "string"}}
|
|
}
|
|
|
|
func openAPIQueryParameter(name string, schema map[string]any) map[string]any {
|
|
return map[string]any{"name": name, "in": "query", "required": false, "schema": schema}
|
|
}
|
|
|
|
func openAPIJSONResponse(description string, schema map[string]any) map[string]any {
|
|
if schema == nil {
|
|
schema = map[string]any{"type": "object", "additionalProperties": true}
|
|
}
|
|
return map[string]any{
|
|
"description": description,
|
|
"content": map[string]any{"application/json": map[string]any{"schema": schema}},
|
|
}
|
|
}
|
|
|
|
func openAPIErrorResponse() map[string]any {
|
|
return openAPIJSONResponse("Error", openAPIRef("ErrorResponse"))
|
|
}
|
|
|
|
func openAPIBinaryContent() map[string]any {
|
|
return map[string]any{"schema": map[string]any{"type": "string", "format": "binary"}}
|
|
}
|