feat: add /v1/video/generations and /v1/image/generations API endpoints

- wwwroot/v1/video/generations/index.dspy: video generation endpoint
  Required params: model, llmcatelogid, prompt
  Supports async task submission via existing inference infrastructure

- wwwroot/v1/image/generations/index.dspy: image generation endpoint
  Required params: model, llmcatelogid, prompt
  Supports both sync and async models depending on config

Both endpoints follow the same pattern as /v1/chat/completions:
  1. Validate required params (model + llmcatelogid + prompt)
  2. Look up llm via llm_api_map join with catalog type
  3. Check customer balance
  4. Route to inference (async/sync based on model config)
This commit is contained in:
yumoqing 2026-05-26 11:45:37 +08:00
parent b558059dc8
commit 146ebb2b4a
2 changed files with 166 additions and 0 deletions

View File

@ -0,0 +1,79 @@
# OpenAI-compatible Image Generation API
# POST /v1/image/generations
# Required params: model, llmcatelogid
# Optional params: prompt, image_url, n, size, style, quality, etc.
#
# Example request:
# {
# "model": "jimeng-4.0",
# "llmcatelogid": "文生图",
# "prompt": "A beautiful sunset over the ocean",
# "size": "1024x1024",
# "n": 1
# }
#
# Response format depends on the upstream model (sync returns image data, async returns task info)
import json
import time
from functools import partial
from appPublic.log import debug
from appPublic.dictObject import DictObject
from appPublic.uniqueID import getID
from appPublic.timeUtils import curDateString, timestampstr
from sqlor.dbpools import get_sor_context
debug(f'{params_kw=}')
userid = await get_user()
userorgid = await get_userorgid()
if userid is None:
debug('need login')
return openai_403()
# Validate required parameters
if not params_kw.model:
d = return_error('Missing required parameter: model')
return json_response(d, status=400)
if not params_kw.llmcatelogid:
d = return_error('Missing required parameter: llmcatelogid')
return json_response(d, status=400)
if not params_kw.prompt:
d = return_error('Missing required parameter: prompt')
return json_response(d, status=400)
lctype = params_kw.llmcatelogid
env = request._run_ns
async with get_sor_context(env, 'llmage') as sor:
# Look up llm by model name and catalog type through llm_api_map
sql = """select distinct a.* from llm a
join llm_api_map m on a.id = m.llmid
join llmcatelog b on m.llmcatelogid = b.id
where b.name = ${lctype}$
and a.model=${model}$"""
recs = await sor.sqlExe(sql, {
'lctype': lctype,
'model': params_kw.model
})
if len(recs) == 0:
debug(f'{params_kw.model=} not found for catalog {lctype}')
return openai_400()
params_kw.llmid = recs[0].id
debug(f'{params_kw.llmid=}')
# Check balance
f = await checkCustomerBalance(params_kw.llmid, userid, userorgid)
if not f:
debug(f'{userid=} balance not enough')
return openai_429()
# Generate task ID and attach to params
if not params_kw.transno:
params_kw.transno = getID()
# Call inference (image generation can be sync or async depending on model config)
return await inference(request, env=env)

View File

@ -0,0 +1,87 @@
# OpenAI-compatible Video Generation API
# POST /v1/video/generations
# Required params: model, llmcatelogid
# Optional params: prompt, image_url, duration, resolution, n, etc.
#
# Example request:
# {
# "model": "keling-2.1",
# "llmcatelogid": "文生视频",
# "prompt": "A beautiful sunset over the ocean",
# "duration": "5s",
# "resolution": "1080p"
# }
#
# Response (async task):
# {
# "id": "vid_xxx",
# "object": "video.generation",
# "model": "keling-2.1",
# "status": "submitted",
# "taskid": "task_xxx",
# "created": 1234567890
# }
import json
import time
from functools import partial
from appPublic.log import debug
from appPublic.dictObject import DictObject
from appPublic.uniqueID import getID
from appPublic.timeUtils import curDateString, timestampstr
from sqlor.dbpools import get_sor_context
debug(f'{params_kw=}')
userid = await get_user()
userorgid = await get_userorgid()
if userid is None:
debug('need login')
return openai_403()
# Validate required parameters
if not params_kw.model:
d = return_error('Missing required parameter: model')
return json_response(d, status=400)
if not params_kw.llmcatelogid:
d = return_error('Missing required parameter: llmcatelogid')
return json_response(d, status=400)
if not params_kw.prompt:
d = return_error('Missing required parameter: prompt')
return json_response(d, status=400)
lctype = params_kw.llmcatelogid
env = request._run_ns
async with get_sor_context(env, 'llmage') as sor:
# Look up llm by model name and catalog type through llm_api_map
sql = """select distinct a.* from llm a
join llm_api_map m on a.id = m.llmid
join llmcatelog b on m.llmcatelogid = b.id
where b.name = ${lctype}$
and a.model=${model}$"""
recs = await sor.sqlExe(sql, {
'lctype': lctype,
'model': params_kw.model
})
if len(recs) == 0:
debug(f'{params_kw.model=} not found for catalog {lctype}')
return openai_400()
params_kw.llmid = recs[0].id
debug(f'{params_kw.llmid=}')
# Check balance
f = await checkCustomerBalance(params_kw.llmid, userid, userorgid)
if not f:
debug(f'{userid=} balance not enough')
return openai_429()
# Generate task ID and attach to params
if not params_kw.transno:
params_kw.transno = getID()
# Call inference (video/image generation is typically async via callback)
return await inference(request, env=env)