diff --git a/load_test.py b/load_test.py new file mode 100644 index 0000000..9f1cfdf --- /dev/null +++ b/load_test.py @@ -0,0 +1,163 @@ +#!/usr/bin/env python3 +"""并发压力测试 llmage /v1/chat/completions,统计 TTFB / 完成时间 / QPM""" + +import asyncio +import aiohttp +import time +import json +import sys +import statistics +from dataclasses import dataclass, field +from typing import List + +URL = "https://token.opencomputing.cn/llmage/v1/chat/completions" +TOKEN = "V9J41PngWBUU6gdHWJWDJ" +MODEL = "qwen3.6-35b-a3b" +DURATION = 180 # 3 分钟 +CONCURRENCIES = [10, 50, 100, 200] + + +@dataclass +class ReqStat: + prompt_idx: int + start_ts: float + first_byte_ts: float | None = None + end_ts: float | None = None + + +async def worker(session: aiohttp.ClientSession, idx: int, stats_out: list): + """单个请求:发送 stream 请求,记录首字时间和完成时间""" + prompt = f"请用一句话介绍你自己,编号{idx}" + payload = { + "model": MODEL, + "stream": True, + "messages": [{"role": "user", "content": prompt}], + } + stat = ReqStat(prompt_idx=idx, start_ts=time.monotonic()) + try: + async with session.post( + URL, + json=payload, + headers={ + "Content-Type": "application/json", + "Authorization": f"Bearer {TOKEN}", + }, + timeout=aiohttp.ClientTimeout(total=120), + ) as resp: + first = True + async for line in resp.content: + if first: + stat.first_byte_ts = time.monotonic() + first = False + # 读完所有 chunk 才算完成 + stat.end_ts = time.monotonic() + except Exception as e: + # 异常请求也记录(TTFB=None 表示失败) + stat.end_ts = time.monotonic() + stats_out.append(stat) + + +async def run_concurrency(concurrency: int): + """以固定并发运行 DURATION 秒,持续发起新请求""" + stats: List[ReqStat] = [] + idx = 0 + stop_at = time.monotonic() + DURATION + + connector = aiohttp.TCPConnector(limit=concurrency + 20, force_close=True) + async with aiohttp.ClientSession(connector=connector) as session: + tasks: list[asyncio.Task] = [] + + while time.monotonic() < stop_at: + # 保持并发数:补满到 concurrency + while len(tasks) < concurrency and time.monotonic() < stop_at: + idx += 1 + tasks.append( + asyncio.create_task(worker(session, idx, stats)) + ) + + if not tasks: + break + + # 等待任意一个完成,腾出槽位 + done, tasks = await asyncio.wait( + tasks, return_when=asyncio.FIRST_COMPLETED, timeout=0.5 + ) + # 清理已完成的 + tasks = list(tasks) + + # 时间到,等待所有进行中的请求完成 + if tasks: + await asyncio.wait(tasks) + + return stats + + +def analyze(name: str, stats: List[ReqStat]): + """分析并打印统计""" + ttfb_list = [s.first_byte_ts - s.start_ts for s in stats if s.first_byte_ts] + total_list = [s.end_ts - s.start_ts for s in stats if s.end_ts and s.first_byte_ts] + failed = sum(1 for s in stats if s.first_byte_ts is None) + total_req = len(stats) + elapsed = DURATION + qpm = total_req / (elapsed / 60) + + print(f"\n{'='*60}") + print(f" 并发={name} | 运行{DURATION}s | 总请求={total_req} | 失败={failed}") + print(f"{'='*60}") + if ttfb_list: + print(f" TTFB (s): min={min(ttfb_list):.3f} avg={statistics.mean(ttfb_list):.3f} " + f"p50={statistics.median(ttfb_list):.3f} p95={_pct(ttfb_list, 95):.3f} p99={_pct(ttfb_list, 99):.3f}") + if total_list: + print(f" 完成 (s): min={min(total_list):.3f} avg={statistics.mean(total_list):.3f} " + f"p50={statistics.median(total_list):.3f} p95={_pct(total_list, 95):.3f} p99={_pct(total_list, 99):.3f}") + print(f" QPM: {qpm:.1f}") + print(f" QPS: {total_req / elapsed:.1f}") + + # 按分钟分段统计 + for minute in range(int(elapsed / 60)): + win_start = minute * 60 + win_end = (minute + 1) * 60 + cnt = sum(1 for s in stats if s.end_ts and (s.end_ts - s.start_ts) >= 0 + and win_start <= (s.start_ts - stats[0].start_ts) < win_end) + print(f" 第{minute+1}分钟完成请求数: {cnt}") + + return { + "concurrency": name, + "total": total_req, + "failed": failed, + "ttfb_avg": statistics.mean(ttfb_list) if ttfb_list else None, + "ttfb_p50": statistics.median(ttfb_list) if ttfb_list else None, + "ttfb_p95": _pct(ttfb_list, 95) if ttfb_list else None, + "total_avg": statistics.mean(total_list) if total_list else None, + "total_p50": statistics.median(total_list) if total_list else None, + "qpm": qpm, + } + + +def _pct(data, p): + return sorted(data)[int(len(data) * p / 100)] + + +async def main(): + results = [] + for c in CONCURRENCIES: + print(f"\n>>> 开始测试 并发={c} ...") + stats = await run_concurrency(c) + r = analyze(str(c), stats) + results.append(r) + + # 汇总表格 + print(f"\n{'='*60}") + print(" 汇总对比") + print(f"{'='*60}") + print(f" {'并发':>6} {'总请求':>8} {'失败':>5} {'TTFB_avg':>9} {'TTFB_p50':>9} {'TTFB_p95':>9} {'完成_avg':>9} {'QPM':>8}") + for r in results: + print(f" {r['concurrency']:>6} {r['total']:>8} {r['failed']:>5} " + f"{r['ttfb_avg']:.3f}s" if r['ttfb_avg'] else "N/A".rjust(9) + " " + f"{(r['ttfb_p50'] or 0):.3f}s".rjust(9) + " " + f"{(r['ttfb_p95'] or 0):.3f}s".rjust(9) + " " + f"{r['total_avg']:.3f}s".rjust(9) if r['total_avg'] else "N/A".rjust(9)) + + +if __name__ == "__main__": + asyncio.run(main()) diff --git a/wwwroot/v1/audio/speech/index.dspy b/wwwroot/v1/audio/speech/index.dspy index c9487b9..6ee1a33 100644 --- a/wwwroot/v1/audio/speech/index.dspy +++ b/wwwroot/v1/audio/speech/index.dspy @@ -41,22 +41,11 @@ if not params_kw.prompt: lctype = params_kw.catelogid 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.id = ${lctype}$ OR b.name = ${lctype}$) - and a.name=${model}$ - and a.status = 'published'""" - 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 +llmid = await env.get_llmid_cached(env, params_kw.model, lctype) +if not llmid: + debug(f'{params_kw.model=} not found for catalog {lctype}') + return openai_400() +params_kw.llmid = llmid params_kw.llmcatelogid = lctype debug(f'{params_kw.llmid=}') diff --git a/wwwroot/v1/audio/transcriptions/index.dspy b/wwwroot/v1/audio/transcriptions/index.dspy index 42f6664..203ce99 100644 --- a/wwwroot/v1/audio/transcriptions/index.dspy +++ b/wwwroot/v1/audio/transcriptions/index.dspy @@ -38,22 +38,11 @@ if not params_kw.audio_file: lctype = params_kw.catelogid 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.id = ${lctype}$ OR b.name = ${lctype}$) - and a.name=${model}$ - and a.status = 'published'""" - 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 +llmid = await env.get_llmid_cached(env, params_kw.model, lctype) +if not llmid: + debug(f'{params_kw.model=} not found for catalog {lctype}') + return openai_400() +params_kw.llmid = llmid params_kw.llmcatelogid = lctype debug(f'{params_kw.llmid=}') diff --git a/wwwroot/v1/image/generations/index.dspy b/wwwroot/v1/image/generations/index.dspy index bf2300f..2c4695d 100644 --- a/wwwroot/v1/image/generations/index.dspy +++ b/wwwroot/v1/image/generations/index.dspy @@ -39,22 +39,11 @@ if not params_kw.prompt: lctype = params_kw.catelogid 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.id = ${lctype}$ OR b.name = ${lctype}$) - and a.name=${model}$ - and a.status = 'published'""" - 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 +llmid = await env.get_llmid_cached(env, params_kw.model, lctype) +if not llmid: + debug(f'{params_kw.model=} not found for catalog {lctype}') + return openai_400() +params_kw.llmid = llmid params_kw.llmcatelogid = lctype debug(f'{params_kw.llmid=}') diff --git a/wwwroot/v1/media/asr-transcribe/index.dspy b/wwwroot/v1/media/asr-transcribe/index.dspy index 357b373..37c859a 100644 --- a/wwwroot/v1/media/asr-transcribe/index.dspy +++ b/wwwroot/v1/media/asr-transcribe/index.dspy @@ -1,34 +1,30 @@ -# KTV Media API - asr-transcribe -# /v1/media/asr-transcribe - -debug_params('params_kw', params_kw) - -userid = await get_user() -userorgid = await get_userorgid() -if userid is None: - return openai_403() - -catelogid = 'ktv_pipeline' -model_name = 'asr-transcribe' -params_kw.model = model_name -params_kw.catelogid = catelogid - -env = request._run_ns -async with get_sor_context(env, 'llmage') as sor: - 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.id = ${catelogid}$ OR b.name = ${catelogid}$) - and a.model=${model}$ - and a.status = 'published'""" - recs = await sor.sqlExe(sql, {'catelogid': catelogid, 'model': model_name}) - if len(recs) == 0: - return openai_400() - params_kw.llmid = recs[0].id - params_kw.llmcatelogid = catelogid - -f = await checkCustomerBalance(params_kw.llmid, userid, userorgid) -if not f: - return openai_429() - -return await inference(request, env=env) +1|# KTV Media API - asr-transcribe +2|# /v1/media/asr-transcribe +3| +4|debug_params('params_kw', params_kw) +5| +6|userid = await get_user() +7|userorgid = await get_userorgid() +8|if userid is None: +9| return openai_403() +10| +11|catelogid = 'ktv_pipeline' +12|model_name = 'asr-transcribe' +13|params_kw.model = model_name +14|params_kw.catelogid = catelogid +15| +16|env = request._run_ns +17|# llmid from cache (model+catelogid -> llmid) +llmid = await env.get_llmid_cached(env, params_kw.model or 'qwen3-max', 'ktv_pipeline') +if not llmid: + debug(f'model not found: params_kw.model') + return openai_400() +params_kw.llmid = llmid +28| params_kw.llmcatelogid = catelogid +29| +30|f = await checkCustomerBalance(params_kw.llmid, userid, userorgid) +31|if not f: +32| return openai_429() +33| +34|return await inference(request, env=env) +35| \ No newline at end of file diff --git a/wwwroot/v1/media/demucs-separate/index.dspy b/wwwroot/v1/media/demucs-separate/index.dspy index c1514b3..0119edb 100644 --- a/wwwroot/v1/media/demucs-separate/index.dspy +++ b/wwwroot/v1/media/demucs-separate/index.dspy @@ -1,34 +1,30 @@ -# KTV Media API - demucs-separate -# /v1/media/demucs-separate - -debug_params('params_kw', params_kw) - -userid = await get_user() -userorgid = await get_userorgid() -if userid is None: - return openai_403() - -catelogid = 'ktv_pipeline' -model_name = 'demucs-separate' -params_kw.model = model_name -params_kw.catelogid = catelogid - -env = request._run_ns -async with get_sor_context(env, 'llmage') as sor: - 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.id = ${catelogid}$ OR b.name = ${catelogid}$) - and a.model=${model}$ - and a.status = 'published'""" - recs = await sor.sqlExe(sql, {'catelogid': catelogid, 'model': model_name}) - if len(recs) == 0: - return openai_400() - params_kw.llmid = recs[0].id - params_kw.llmcatelogid = catelogid - -f = await checkCustomerBalance(params_kw.llmid, userid, userorgid) -if not f: - return openai_429() - -return await inference(request, env=env) +1|# KTV Media API - demucs-separate +2|# /v1/media/demucs-separate +3| +4|debug_params('params_kw', params_kw) +5| +6|userid = await get_user() +7|userorgid = await get_userorgid() +8|if userid is None: +9| return openai_403() +10| +11|catelogid = 'ktv_pipeline' +12|model_name = 'demucs-separate' +13|params_kw.model = model_name +14|params_kw.catelogid = catelogid +15| +16|env = request._run_ns +17|# llmid from cache (model+catelogid -> llmid) +llmid = await env.get_llmid_cached(env, params_kw.model or 'qwen3-max', 'ktv_pipeline') +if not llmid: + debug(f'model not found: params_kw.model') + return openai_400() +params_kw.llmid = llmid +28| params_kw.llmcatelogid = catelogid +29| +30|f = await checkCustomerBalance(params_kw.llmid, userid, userorgid) +31|if not f: +32| return openai_429() +33| +34|return await inference(request, env=env) +35| \ No newline at end of file diff --git a/wwwroot/v1/media/face-compare/index.dspy b/wwwroot/v1/media/face-compare/index.dspy index 3379346..a2a4960 100644 --- a/wwwroot/v1/media/face-compare/index.dspy +++ b/wwwroot/v1/media/face-compare/index.dspy @@ -1,34 +1,30 @@ -# KTV Media API - face-compare -# /v1/media/face-compare - -debug_params('params_kw', params_kw) - -userid = await get_user() -userorgid = await get_userorgid() -if userid is None: - return openai_403() - -catelogid = 'ktv_pipeline' -model_name = 'face-compare' -params_kw.model = model_name -params_kw.catelogid = catelogid - -env = request._run_ns -async with get_sor_context(env, 'llmage') as sor: - 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.id = ${catelogid}$ OR b.name = ${catelogid}$) - and a.model=${model}$ - and a.status = 'published'""" - recs = await sor.sqlExe(sql, {'catelogid': catelogid, 'model': model_name}) - if len(recs) == 0: - return openai_400() - params_kw.llmid = recs[0].id - params_kw.llmcatelogid = catelogid - -f = await checkCustomerBalance(params_kw.llmid, userid, userorgid) -if not f: - return openai_429() - -return await inference(request, env=env) +1|# KTV Media API - face-compare +2|# /v1/media/face-compare +3| +4|debug_params('params_kw', params_kw) +5| +6|userid = await get_user() +7|userorgid = await get_userorgid() +8|if userid is None: +9| return openai_403() +10| +11|catelogid = 'ktv_pipeline' +12|model_name = 'face-compare' +13|params_kw.model = model_name +14|params_kw.catelogid = catelogid +15| +16|env = request._run_ns +17|# llmid from cache (model+catelogid -> llmid) +llmid = await env.get_llmid_cached(env, params_kw.model or 'qwen3-max', 'ktv_pipeline') +if not llmid: + debug(f'model not found: params_kw.model') + return openai_400() +params_kw.llmid = llmid +28| params_kw.llmcatelogid = catelogid +29| +30|f = await checkCustomerBalance(params_kw.llmid, userid, userorgid) +31|if not f: +32| return openai_429() +33| +34|return await inference(request, env=env) +35| \ No newline at end of file diff --git a/wwwroot/v1/media/face-detect/index.dspy b/wwwroot/v1/media/face-detect/index.dspy index 2d982e5..c999e71 100644 --- a/wwwroot/v1/media/face-detect/index.dspy +++ b/wwwroot/v1/media/face-detect/index.dspy @@ -1,34 +1,30 @@ -# KTV Media API - face-detect -# /v1/media/face-detect - -debug_params('params_kw', params_kw) - -userid = await get_user() -userorgid = await get_userorgid() -if userid is None: - return openai_403() - -catelogid = 'ktv_pipeline' -model_name = 'face-detect' -params_kw.model = model_name -params_kw.catelogid = catelogid - -env = request._run_ns -async with get_sor_context(env, 'llmage') as sor: - 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.id = ${catelogid}$ OR b.name = ${catelogid}$) - and a.model=${model}$ - and a.status = 'published'""" - recs = await sor.sqlExe(sql, {'catelogid': catelogid, 'model': model_name}) - if len(recs) == 0: - return openai_400() - params_kw.llmid = recs[0].id - params_kw.llmcatelogid = catelogid - -f = await checkCustomerBalance(params_kw.llmid, userid, userorgid) -if not f: - return openai_429() - -return await inference(request, env=env) +1|# KTV Media API - face-detect +2|# /v1/media/face-detect +3| +4|debug_params('params_kw', params_kw) +5| +6|userid = await get_user() +7|userorgid = await get_userorgid() +8|if userid is None: +9| return openai_403() +10| +11|catelogid = 'ktv_pipeline' +12|model_name = 'face-detect' +13|params_kw.model = model_name +14|params_kw.catelogid = catelogid +15| +16|env = request._run_ns +17|# llmid from cache (model+catelogid -> llmid) +llmid = await env.get_llmid_cached(env, params_kw.model or 'qwen3-max', 'ktv_pipeline') +if not llmid: + debug(f'model not found: params_kw.model') + return openai_400() +params_kw.llmid = llmid +28| params_kw.llmcatelogid = catelogid +29| +30|f = await checkCustomerBalance(params_kw.llmid, userid, userorgid) +31|if not f: +32| return openai_429() +33| +34|return await inference(request, env=env) +35| \ No newline at end of file diff --git a/wwwroot/v1/media/face-recognize/index.dspy b/wwwroot/v1/media/face-recognize/index.dspy index 28a06b9..e081dfd 100644 --- a/wwwroot/v1/media/face-recognize/index.dspy +++ b/wwwroot/v1/media/face-recognize/index.dspy @@ -1,34 +1,30 @@ -# KTV Media API - face-recognize -# /v1/media/face-recognize - -debug_params('params_kw', params_kw) - -userid = await get_user() -userorgid = await get_userorgid() -if userid is None: - return openai_403() - -catelogid = 'ktv_pipeline' -model_name = 'face-recognize' -params_kw.model = model_name -params_kw.catelogid = catelogid - -env = request._run_ns -async with get_sor_context(env, 'llmage') as sor: - 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.id = ${catelogid}$ OR b.name = ${catelogid}$) - and a.model=${model}$ - and a.status = 'published'""" - recs = await sor.sqlExe(sql, {'catelogid': catelogid, 'model': model_name}) - if len(recs) == 0: - return openai_400() - params_kw.llmid = recs[0].id - params_kw.llmcatelogid = catelogid - -f = await checkCustomerBalance(params_kw.llmid, userid, userorgid) -if not f: - return openai_429() - -return await inference(request, env=env) +1|# KTV Media API - face-recognize +2|# /v1/media/face-recognize +3| +4|debug_params('params_kw', params_kw) +5| +6|userid = await get_user() +7|userorgid = await get_userorgid() +8|if userid is None: +9| return openai_403() +10| +11|catelogid = 'ktv_pipeline' +12|model_name = 'face-recognize' +13|params_kw.model = model_name +14|params_kw.catelogid = catelogid +15| +16|env = request._run_ns +17|# llmid from cache (model+catelogid -> llmid) +llmid = await env.get_llmid_cached(env, params_kw.model or 'qwen3-max', 'ktv_pipeline') +if not llmid: + debug(f'model not found: params_kw.model') + return openai_400() +params_kw.llmid = llmid +28| params_kw.llmcatelogid = catelogid +29| +30|f = await checkCustomerBalance(params_kw.llmid, userid, userorgid) +31|if not f: +32| return openai_429() +33| +34|return await inference(request, env=env) +35| \ No newline at end of file diff --git a/wwwroot/v1/media/merge-video/index.dspy b/wwwroot/v1/media/merge-video/index.dspy index 2bba72f..f981cb4 100644 --- a/wwwroot/v1/media/merge-video/index.dspy +++ b/wwwroot/v1/media/merge-video/index.dspy @@ -1,34 +1,30 @@ -# KTV Media API - merge-video -# /v1/media/merge-video - -debug_params('params_kw', params_kw) - -userid = await get_user() -userorgid = await get_userorgid() -if userid is None: - return openai_403() - -catelogid = 'ktv_pipeline' -model_name = 'merge-video' -params_kw.model = model_name -params_kw.catelogid = catelogid - -env = request._run_ns -async with get_sor_context(env, 'llmage') as sor: - 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.id = ${catelogid}$ OR b.name = ${catelogid}$) - and a.model=${model}$ - and a.status = 'published'""" - recs = await sor.sqlExe(sql, {'catelogid': catelogid, 'model': model_name}) - if len(recs) == 0: - return openai_400() - params_kw.llmid = recs[0].id - params_kw.llmcatelogid = catelogid - -f = await checkCustomerBalance(params_kw.llmid, userid, userorgid) -if not f: - return openai_429() - -return await inference(request, env=env) +1|# KTV Media API - merge-video +2|# /v1/media/merge-video +3| +4|debug_params('params_kw', params_kw) +5| +6|userid = await get_user() +7|userorgid = await get_userorgid() +8|if userid is None: +9| return openai_403() +10| +11|catelogid = 'ktv_pipeline' +12|model_name = 'merge-video' +13|params_kw.model = model_name +14|params_kw.catelogid = catelogid +15| +16|env = request._run_ns +17|# llmid from cache (model+catelogid -> llmid) +llmid = await env.get_llmid_cached(env, params_kw.model or 'qwen3-max', 'ktv_pipeline') +if not llmid: + debug(f'model not found: params_kw.model') + return openai_400() +params_kw.llmid = llmid +28| params_kw.llmcatelogid = catelogid +29| +30|f = await checkCustomerBalance(params_kw.llmid, userid, userorgid) +31|if not f: +32| return openai_429() +33| +34|return await inference(request, env=env) +35| \ No newline at end of file diff --git a/wwwroot/v1/media/realesrgan-upscale/index.dspy b/wwwroot/v1/media/realesrgan-upscale/index.dspy index 6ae1cd8..1d76ae4 100644 --- a/wwwroot/v1/media/realesrgan-upscale/index.dspy +++ b/wwwroot/v1/media/realesrgan-upscale/index.dspy @@ -1,34 +1,30 @@ -# KTV Media API - realesrgan-upscale -# /v1/media/realesrgan-upscale - -debug_params('params_kw', params_kw) - -userid = await get_user() -userorgid = await get_userorgid() -if userid is None: - return openai_403() - -catelogid = 'ktv_pipeline' -model_name = 'realesrgan-upscale' -params_kw.model = model_name -params_kw.catelogid = catelogid - -env = request._run_ns -async with get_sor_context(env, 'llmage') as sor: - 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.id = ${catelogid}$ OR b.name = ${catelogid}$) - and a.model=${model}$ - and a.status = 'published'""" - recs = await sor.sqlExe(sql, {'catelogid': catelogid, 'model': model_name}) - if len(recs) == 0: - return openai_400() - params_kw.llmid = recs[0].id - params_kw.llmcatelogid = catelogid - -f = await checkCustomerBalance(params_kw.llmid, userid, userorgid) -if not f: - return openai_429() - -return await inference(request, env=env) +1|# KTV Media API - realesrgan-upscale +2|# /v1/media/realesrgan-upscale +3| +4|debug_params('params_kw', params_kw) +5| +6|userid = await get_user() +7|userorgid = await get_userorgid() +8|if userid is None: +9| return openai_403() +10| +11|catelogid = 'ktv_pipeline' +12|model_name = 'realesrgan-upscale' +13|params_kw.model = model_name +14|params_kw.catelogid = catelogid +15| +16|env = request._run_ns +17|# llmid from cache (model+catelogid -> llmid) +llmid = await env.get_llmid_cached(env, params_kw.model or 'qwen3-max', 'ktv_pipeline') +if not llmid: + debug(f'model not found: params_kw.model') + return openai_400() +params_kw.llmid = llmid +28| params_kw.llmcatelogid = catelogid +29| +30|f = await checkCustomerBalance(params_kw.llmid, userid, userorgid) +31|if not f: +32| return openai_429() +33| +34|return await inference(request, env=env) +35| \ No newline at end of file diff --git a/wwwroot/v1/media/rvc-convert/index.dspy b/wwwroot/v1/media/rvc-convert/index.dspy index 505622a..6fb4492 100644 --- a/wwwroot/v1/media/rvc-convert/index.dspy +++ b/wwwroot/v1/media/rvc-convert/index.dspy @@ -1,34 +1,30 @@ -# KTV Media API - rvc-convert -# /v1/media/rvc-convert - -debug_params('params_kw', params_kw) - -userid = await get_user() -userorgid = await get_userorgid() -if userid is None: - return openai_403() - -catelogid = 'ktv_pipeline' -model_name = 'rvc-convert' -params_kw.model = model_name -params_kw.catelogid = catelogid - -env = request._run_ns -async with get_sor_context(env, 'llmage') as sor: - 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.id = ${catelogid}$ OR b.name = ${catelogid}$) - and a.model=${model}$ - and a.status = 'published'""" - recs = await sor.sqlExe(sql, {'catelogid': catelogid, 'model': model_name}) - if len(recs) == 0: - return openai_400() - params_kw.llmid = recs[0].id - params_kw.llmcatelogid = catelogid - -f = await checkCustomerBalance(params_kw.llmid, userid, userorgid) -if not f: - return openai_429() - -return await inference(request, env=env) +1|# KTV Media API - rvc-convert +2|# /v1/media/rvc-convert +3| +4|debug_params('params_kw', params_kw) +5| +6|userid = await get_user() +7|userorgid = await get_userorgid() +8|if userid is None: +9| return openai_403() +10| +11|catelogid = 'ktv_pipeline' +12|model_name = 'rvc-convert' +13|params_kw.model = model_name +14|params_kw.catelogid = catelogid +15| +16|env = request._run_ns +17|# llmid from cache (model+catelogid -> llmid) +llmid = await env.get_llmid_cached(env, params_kw.model or 'qwen3-max', 'ktv_pipeline') +if not llmid: + debug(f'model not found: params_kw.model') + return openai_400() +params_kw.llmid = llmid +28| params_kw.llmcatelogid = catelogid +29| +30|f = await checkCustomerBalance(params_kw.llmid, userid, userorgid) +31|if not f: +32| return openai_429() +33| +34|return await inference(request, env=env) +35| \ No newline at end of file diff --git a/wwwroot/v1/media/songrate-evaluate/index.dspy b/wwwroot/v1/media/songrate-evaluate/index.dspy index 85fbf83..6dc7ac2 100644 --- a/wwwroot/v1/media/songrate-evaluate/index.dspy +++ b/wwwroot/v1/media/songrate-evaluate/index.dspy @@ -1,34 +1,30 @@ -# KTV Media API - songrate-evaluate -# /v1/media/songrate-evaluate - -debug_params('params_kw', params_kw) - -userid = await get_user() -userorgid = await get_userorgid() -if userid is None: - return openai_403() - -catelogid = 'ktv_pipeline' -model_name = 'songrate-evaluate' -params_kw.model = model_name -params_kw.catelogid = catelogid - -env = request._run_ns -async with get_sor_context(env, 'llmage') as sor: - 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.id = ${catelogid}$ OR b.name = ${catelogid}$) - and a.model=${model}$ - and a.status = 'published'""" - recs = await sor.sqlExe(sql, {'catelogid': catelogid, 'model': model_name}) - if len(recs) == 0: - return openai_400() - params_kw.llmid = recs[0].id - params_kw.llmcatelogid = catelogid - -f = await checkCustomerBalance(params_kw.llmid, userid, userorgid) -if not f: - return openai_429() - -return await inference(request, env=env) +1|# KTV Media API - songrate-evaluate +2|# /v1/media/songrate-evaluate +3| +4|debug_params('params_kw', params_kw) +5| +6|userid = await get_user() +7|userorgid = await get_userorgid() +8|if userid is None: +9| return openai_403() +10| +11|catelogid = 'ktv_pipeline' +12|model_name = 'songrate-evaluate' +13|params_kw.model = model_name +14|params_kw.catelogid = catelogid +15| +16|env = request._run_ns +17|# llmid from cache (model+catelogid -> llmid) +llmid = await env.get_llmid_cached(env, params_kw.model or 'qwen3-max', 'ktv_pipeline') +if not llmid: + debug(f'model not found: params_kw.model') + return openai_400() +params_kw.llmid = llmid +28| params_kw.llmcatelogid = catelogid +29| +30|f = await checkCustomerBalance(params_kw.llmid, userid, userorgid) +31|if not f: +32| return openai_429() +33| +34|return await inference(request, env=env) +35| \ No newline at end of file diff --git a/wwwroot/v1/media/subtitle-render/index.dspy b/wwwroot/v1/media/subtitle-render/index.dspy index 4fd9949..940fb4a 100644 --- a/wwwroot/v1/media/subtitle-render/index.dspy +++ b/wwwroot/v1/media/subtitle-render/index.dspy @@ -1,34 +1,30 @@ -# KTV Media API - subtitle-render -# /v1/media/subtitle-render - -debug_params('params_kw', params_kw) - -userid = await get_user() -userorgid = await get_userorgid() -if userid is None: - return openai_403() - -catelogid = 'ktv_pipeline' -model_name = 'subtitle-render' -params_kw.model = model_name -params_kw.catelogid = catelogid - -env = request._run_ns -async with get_sor_context(env, 'llmage') as sor: - 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.id = ${catelogid}$ OR b.name = ${catelogid}$) - and a.model=${model}$ - and a.status = 'published'""" - recs = await sor.sqlExe(sql, {'catelogid': catelogid, 'model': model_name}) - if len(recs) == 0: - return openai_400() - params_kw.llmid = recs[0].id - params_kw.llmcatelogid = catelogid - -f = await checkCustomerBalance(params_kw.llmid, userid, userorgid) -if not f: - return openai_429() - -return await inference(request, env=env) +1|# KTV Media API - subtitle-render +2|# /v1/media/subtitle-render +3| +4|debug_params('params_kw', params_kw) +5| +6|userid = await get_user() +7|userorgid = await get_userorgid() +8|if userid is None: +9| return openai_403() +10| +11|catelogid = 'ktv_pipeline' +12|model_name = 'subtitle-render' +13|params_kw.model = model_name +14|params_kw.catelogid = catelogid +15| +16|env = request._run_ns +17|# llmid from cache (model+catelogid -> llmid) +llmid = await env.get_llmid_cached(env, params_kw.model or 'qwen3-max', 'ktv_pipeline') +if not llmid: + debug(f'model not found: params_kw.model') + return openai_400() +params_kw.llmid = llmid +28| params_kw.llmcatelogid = catelogid +29| +30|f = await checkCustomerBalance(params_kw.llmid, userid, userorgid) +31|if not f: +32| return openai_429() +33| +34|return await inference(request, env=env) +35| \ No newline at end of file diff --git a/wwwroot/v1/media/synth-generate/index.dspy b/wwwroot/v1/media/synth-generate/index.dspy index 470c79f..0fdf7a9 100644 --- a/wwwroot/v1/media/synth-generate/index.dspy +++ b/wwwroot/v1/media/synth-generate/index.dspy @@ -1,34 +1,30 @@ -# KTV Media API - synth-generate -# /v1/media/synth-generate - -debug_params('params_kw', params_kw) - -userid = await get_user() -userorgid = await get_userorgid() -if userid is None: - return openai_403() - -catelogid = 'ktv_pipeline' -model_name = 'synth-generate' -params_kw.model = model_name -params_kw.catelogid = catelogid - -env = request._run_ns -async with get_sor_context(env, 'llmage') as sor: - 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.id = ${catelogid}$ OR b.name = ${catelogid}$) - and a.model=${model}$ - and a.status = 'published'""" - recs = await sor.sqlExe(sql, {'catelogid': catelogid, 'model': model_name}) - if len(recs) == 0: - return openai_400() - params_kw.llmid = recs[0].id - params_kw.llmcatelogid = catelogid - -f = await checkCustomerBalance(params_kw.llmid, userid, userorgid) -if not f: - return openai_429() - -return await inference(request, env=env) +1|# KTV Media API - synth-generate +2|# /v1/media/synth-generate +3| +4|debug_params('params_kw', params_kw) +5| +6|userid = await get_user() +7|userorgid = await get_userorgid() +8|if userid is None: +9| return openai_403() +10| +11|catelogid = 'ktv_pipeline' +12|model_name = 'synth-generate' +13|params_kw.model = model_name +14|params_kw.catelogid = catelogid +15| +16|env = request._run_ns +17|# llmid from cache (model+catelogid -> llmid) +llmid = await env.get_llmid_cached(env, params_kw.model or 'qwen3-max', 'ktv_pipeline') +if not llmid: + debug(f'model not found: params_kw.model') + return openai_400() +params_kw.llmid = llmid +28| params_kw.llmcatelogid = catelogid +29| +30|f = await checkCustomerBalance(params_kw.llmid, userid, userorgid) +31|if not f: +32| return openai_429() +33| +34|return await inference(request, env=env) +35| \ No newline at end of file diff --git a/wwwroot/v1/media/video-eval-evaluate/index.dspy b/wwwroot/v1/media/video-eval-evaluate/index.dspy index 4dae516..ea63d21 100644 --- a/wwwroot/v1/media/video-eval-evaluate/index.dspy +++ b/wwwroot/v1/media/video-eval-evaluate/index.dspy @@ -1,34 +1,30 @@ -# KTV Media API - video-eval-evaluate -# /v1/media/video-eval-evaluate - -debug_params('params_kw', params_kw) - -userid = await get_user() -userorgid = await get_userorgid() -if userid is None: - return openai_403() - -catelogid = 'ktv_pipeline' -model_name = 'video-eval-evaluate' -params_kw.model = model_name -params_kw.catelogid = catelogid - -env = request._run_ns -async with get_sor_context(env, 'llmage') as sor: - 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.id = ${catelogid}$ OR b.name = ${catelogid}$) - and a.model=${model}$ - and a.status = 'published'""" - recs = await sor.sqlExe(sql, {'catelogid': catelogid, 'model': model_name}) - if len(recs) == 0: - return openai_400() - params_kw.llmid = recs[0].id - params_kw.llmcatelogid = catelogid - -f = await checkCustomerBalance(params_kw.llmid, userid, userorgid) -if not f: - return openai_429() - -return await inference(request, env=env) +1|# KTV Media API - video-eval-evaluate +2|# /v1/media/video-eval-evaluate +3| +4|debug_params('params_kw', params_kw) +5| +6|userid = await get_user() +7|userorgid = await get_userorgid() +8|if userid is None: +9| return openai_403() +10| +11|catelogid = 'ktv_pipeline' +12|model_name = 'video-eval-evaluate' +13|params_kw.model = model_name +14|params_kw.catelogid = catelogid +15| +16|env = request._run_ns +17|# llmid from cache (model+catelogid -> llmid) +llmid = await env.get_llmid_cached(env, params_kw.model or 'qwen3-max', 'ktv_pipeline') +if not llmid: + debug(f'model not found: params_kw.model') + return openai_400() +params_kw.llmid = llmid +28| params_kw.llmcatelogid = catelogid +29| +30|f = await checkCustomerBalance(params_kw.llmid, userid, userorgid) +31|if not f: +32| return openai_429() +33| +34|return await inference(request, env=env) +35| \ No newline at end of file diff --git a/wwwroot/v1/music/generations/index.dspy b/wwwroot/v1/music/generations/index.dspy index 160b238..79e364d 100644 --- a/wwwroot/v1/music/generations/index.dspy +++ b/wwwroot/v1/music/generations/index.dspy @@ -47,22 +47,11 @@ if not params_kw.lyrics: lctype = params_kw.catelogid 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.id = ${lctype}$ OR b.name = ${lctype}$) - and a.name=${model}$ - and a.status = 'published'""" - 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 +llmid = await env.get_llmid_cached(env, params_kw.model, lctype) +if not llmid: + debug(f'{params_kw.model=} not found for catalog {lctype}') + return openai_400() +params_kw.llmid = llmid params_kw.llmcatelogid = lctype debug(f'{params_kw.llmid=}') diff --git a/wwwroot/v1/pipeline/submit/index.dspy b/wwwroot/v1/pipeline/submit/index.dspy index a339d2c..525ceb7 100644 --- a/wwwroot/v1/pipeline/submit/index.dspy +++ b/wwwroot/v1/pipeline/submit/index.dspy @@ -1,74 +1,64 @@ -# KTV Pipeline / GPU Service Inference API -# POST /v1/pipeline/submit -# catelogid 固定为 ktv_pipeline,无需传 catelogid -# -# Required params: -# model: string - 模型名称,如 "ky-asr-transcribe" -# -# 各服务特定参数见 API 文档 -# -# 同步服务直接返回结果: -# { -# "taskid": "luid_xxx", -# "taskstatus": "SUCCEEDED", -# ...服务特定结果字段..., -# "usage": {...} -# } -# -# 异步服务返回任务信息: -# { -# "taskid": "luid_xxx", -# "taskstatus": "PENDING" -# } -# 异步结果通过 /v1/tasks?taskid=xxx 查询 - -debug_params('params_kw', 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) - -catelogid = 'ktv_pipeline' -params_kw.catelogid = catelogid - -env = request._run_ns -async with get_sor_context(env, 'llmage') as sor: - # Look up llm by model name through llm_api_map (fixed catelogid=ktv_pipeline) - 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.id = ${catelogid}$ OR b.name = ${catelogid}$) - and a.name=${model}$ - and a.status = 'published'""" - recs = await sor.sqlExe(sql, { - 'catelogid': catelogid, - 'model': params_kw.model - }) - if len(recs) == 0: - debug(f'{params_kw.model=} not found under ktv_pipeline catalog') - d = return_error(f'Model "{params_kw.model}" not found or not available') - return json_response(d, status=400) - params_kw.llmid = recs[0].id - params_kw.llmcatelogid = catelogid - -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 -if not params_kw.transno: - params_kw.transno = getID() - -# Call inference -return await inference(request, env=env) +1|# KTV Pipeline / GPU Service Inference API +2|# POST /v1/pipeline/submit +3|# catelogid 固定为 ktv_pipeline,无需传 catelogid +4|# +5|# Required params: +6|# model: string - 模型名称,如 "ky-asr-transcribe" +7|# +8|# 各服务特定参数见 API 文档 +9|# +10|# 同步服务直接返回结果: +11|# { +12|# "taskid": "luid_xxx", +13|# "taskstatus": "SUCCEEDED", +14|# ...服务特定结果字段..., +15|# "usage": {...} +16|# } +17|# +18|# 异步服务返回任务信息: +19|# { +20|# "taskid": "luid_xxx", +21|# "taskstatus": "PENDING" +22|# } +23|# 异步结果通过 /v1/tasks?taskid=xxx 查询 +24| +25|debug_params('params_kw', params_kw) +26| +27|userid = await get_user() +28|userorgid = await get_userorgid() +29|if userid is None: +30| debug('need login') +31| return openai_403() +32| +33|# Validate required parameters +34|if not params_kw.model: +35| d = return_error('Missing required parameter: model') +36| return json_response(d, status=400) +37| +38|catelogid = 'ktv_pipeline' +39|params_kw.catelogid = catelogid +40| +41|env = request._run_ns +42|# llmid from cache (model+catelogid -> llmid) +llmid = await env.get_llmid_cached(env, params_kw.model or 'qwen3-max', 'ktv_pipeline') +if not llmid: + debug(f'model not found: params_kw.model') + return {{error_response}} +params_kw.llmid = llmid +59| params_kw.llmcatelogid = catelogid +60| +61|debug(f'{params_kw.llmid=}') +62| +63|# Check balance +64|f = await checkCustomerBalance(params_kw.llmid, userid, userorgid) +65|if not f: +66| debug(f'{userid=} balance not enough') +67| return openai_429() +68| +69|# Generate task ID +70|if not params_kw.transno: +71| params_kw.transno = getID() +72| +73|# Call inference +74|return await inference(request, env=env) +75| \ No newline at end of file diff --git a/wwwroot/v1/video/generations/index.dspy b/wwwroot/v1/video/generations/index.dspy index a24c326..b4801ec 100644 --- a/wwwroot/v1/video/generations/index.dspy +++ b/wwwroot/v1/video/generations/index.dspy @@ -47,22 +47,11 @@ if not params_kw.prompt: lctype = params_kw.catelogid 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.id = ${lctype}$ OR b.name = ${lctype}$) - and a.name=${model}$ - and a.status = 'published'""" - 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 +llmid = await env.get_llmid_cached(env, params_kw.model, lctype) +if not llmid: + debug(f'{params_kw.model=} not found for catalog {lctype}') + return openai_400() +params_kw.llmid = llmid params_kw.llmcatelogid = lctype debug(f'{params_kw.llmid=}')