refactor: backend_accounting.py now uses product_management module

- Import from product_management.get_manager() instead of llmage.accounting
- Also loads load_llmage for pricing/discount initialization
This commit is contained in:
yumoqing 2026-07-07 12:27:48 +08:00
parent 5ec5301e6e
commit 4927f7f330
7 changed files with 37 additions and 69 deletions

View File

@ -1,7 +1,7 @@
#!/usr/bin/env python #!/usr/bin/env python
""" """
独立运行的LLM后台计费程序 独立运行的产品后台计费程序
sage.py llmage 模块中提取避免多进程模式下重复运行 产品记账已从 llmage 迁移到 product_management 模块
""" """
import os import os
import sys import sys
@ -25,16 +25,13 @@ from llmage.init import load_llmage
from pricing.init import load_pricing from pricing.init import load_pricing
from discount.init import load_discount from discount.init import load_discount
from dapi.init import load_dapi from dapi.init import load_dapi
from product_management import get_manager
# 初始化配置 # 初始化配置
p = ProgramPath() p = ProgramPath()
config = getConfig(NS={'workdir': os.getcwd(), 'ProgramPath': p}) config = getConfig(NS={'workdir': os.getcwd(), 'ProgramPath': p})
DBPools(config.databases) DBPools(config.databases)
# 导入 llmage 的计费函数
from llmage.accounting import (
backend_accounting
)
def get_module_dbname(m): def get_module_dbname(m):
return 'sage' return 'sage'
@ -62,8 +59,11 @@ def main():
load_pricing() load_pricing()
load_discount() load_discount()
load_dapi() load_dapi()
load_llmage()
manager = get_manager()
try: try:
loop.run_until_complete(backend_accounting()) loop.run_until_complete(manager.backend_accounting())
except asyncio.CancelledError: except asyncio.CancelledError:
pass pass
finally: finally:

View File

@ -1,54 +1,29 @@
# Demucs 任务状态查询 # Demucs 任务状态查询
# 部署到: /data/ymq/demucs-service/app/api/demucs-status.dspy
# GET /demucs/api/demucs-status?task_id=xxx # GET /demucs/api/demucs-status?task_id=xxx
import json
task_id = params_kw.task_id task_id = params_kw.task_id
if not task_id: if not task_id:
return {"error": "task_id is required"} return {"error": "task_id is required"}
env = request._run_ns env = request._run_ns
longtasks = env.longtasks longtasks = env.longtasks
if not longtasks:
return {"error": "longtasks not available"}
# longtasks 内部通过 Redis 存储结果key 格式: {queue_name}:result:{task_id} task = await longtasks.get_redis_task(task_id)
import redis.asyncio as redis if not task or not task.get("created_at"):
r = redis.Redis(host='127.0.0.1', port=6379, db=0, decode_responses=True)
# 尝试获取结果
result_key = f"demucs:result:{task_id}"
raw = await r.get(result_key)
if raw:
data = json.loads(raw)
return {
"task_id": task_id,
"status": "SUCCEEDED",
"vocals_url": data.get("vocals_path", ""),
"accompaniment_url": data.get("accompaniment_path", ""),
"duration": data.get("duration", 0),
"usage": data.get("usage", {"次": 1}),
}
# 检查是否在队列中
queue_key = f"demucs:queue"
in_queue = await r.lrange(queue_key, 0, -1)
for item in in_queue:
try:
item_data = json.loads(item)
if item_data.get("task_id") == task_id:
return {"task_id": task_id, "status": "queued"}
except:
pass
# 检查是否在处理中
processing_key = f"demucs:processing"
processing = await r.get(processing_key)
if processing:
try:
proc_data = json.loads(processing)
if proc_data.get("task_id") == task_id:
return {"task_id": task_id, "status": "processing"}
except:
pass
return {"task_id": task_id, "status": "not_found"} return {"task_id": task_id, "status": "not_found"}
status = task.get("status", "unknown")
result = {"task_id": task_id, "status": status}
if status == "SUCCEEDED":
data = task.get("result", {})
result["vocals_url"] = data.get("vocals_path", "")
result["accompaniment_url"] = data.get("accompaniment_path", "")
result["duration"] = data.get("duration", 0)
# worker 须在 result 中包含 usage 字段
result["usage"] = data.get("usage", {"audio_seconds": data.get("duration", 0)})
elif status == "FAILED":
result["error"] = task.get("error", str(task.get("result", "")))
return result

View File

@ -0,0 +1,9 @@
# Patch for /data/ymq/demucs-service/workers/separate.py
# In run_separate_full(), ensure the return dict includes:
# "usage": {"audio_seconds": duration}
# Find the return statement in run_separate_full and add usage field.
# The function currently returns something like:
# return {"vocals_path": ..., "accompaniment_path": ..., "duration": ...}
#
# Add: "usage": {"audio_seconds": result.get("duration", 0)}

View File

@ -51,12 +51,6 @@ INSERT INTO uapiio (id, name, description, input_fields) VALUES (
'{"audio_file":{"type":"string","required":true,"description":"音频文件URL"},"scene":{"type":"string","required":false,"default":"pop","description":"场景类型"}}' '{"audio_file":{"type":"string","required":true,"description":"音频文件URL"},"scene":{"type":"string","required":false,"default":"pop","description":"场景类型"}}'
) ON DUPLICATE KEY UPDATE description=VALUES(description), input_fields=VALUES(input_fields); ) ON DUPLICATE KEY UPDATE description=VALUES(description), input_fields=VALUES(input_fields);
-- 音乐合成(异步)
INSERT INTO uapiio (id, name, description, input_fields) VALUES (
'ktv_synth_generate_io', 'KTV音乐合成', 'AI歌声合成异步任务',
'{"lyrics":{"type":"string","required":true,"description":"歌词文本"},"style":{"type":"string","required":false,"default":"pop","description":"演唱风格"},"audio_file":{"type":"string","required":false,"description":"参考音频URL声音克隆"}}'
) ON DUPLICATE KEY UPDATE description=VALUES(description), input_fields=VALUES(input_fields);
-- 合成状态查询 -- 合成状态查询
INSERT INTO uapiio (id, name, description, input_fields) VALUES ( INSERT INTO uapiio (id, name, description, input_fields) VALUES (
'ktv_synth_status_io', 'KTV合成状态', '查询音乐合成任务状态', 'ktv_synth_status_io', 'KTV合成状态', '查询音乐合成任务状态',

View File

@ -52,13 +52,7 @@ UPDATE uapi SET stream='sync',headers='{"Content-Type":"application/json"}',
response='{%if error%}{"error":"{{error}}"}{%else%}{"status":"{{status}}","score":{{score}},"details":{{json.dumps(details,ensure_ascii=False)}},"usage":{{json.dumps(usage,ensure_ascii=False)}}}{%endif%}', response='{%if error%}{"error":"{{error}}"}{%else%}{"status":"{{status}}","score":{{score}},"details":{{json.dumps(details,ensure_ascii=False)}},"usage":{{json.dumps(usage,ensure_ascii=False)}}}{%endif%}',
ioid='ktv_songrate_evaluate_io' WHERE id='uapi_ktv_songrate_evaluate'; ioid='ktv_songrate_evaluate_io' WHERE id='uapi_ktv_songrate_evaluate';
-- 9. 音乐合成 (async提交) -- 9. 合成状态查询 (sync)
UPDATE uapi SET stream='async',headers='{"Content-Type":"application/json"}',
data='{"lyrics":"{{lyrics}}"{%if style%},"style":"{{style}}"{%endif%}{%if audio_file%},"audio_file":"{{audio_file}}"{%endif%}}',
response='{"taskid":"{{task_id}}","taskstatus":"PENDING"}',
ioid='ktv_synth_generate_io' WHERE id='uapi_ktv_synth_generate';
-- 10. 合成状态查询 (sync)
UPDATE uapi SET stream='sync',headers='{"Content-Type":"application/json"}', UPDATE uapi SET stream='sync',headers='{"Content-Type":"application/json"}',
data='{"task_id":"{{task_id}}"}', data='{"task_id":"{{task_id}}"}',
response='{%if error%}{"error":"{{error}}"}{%elif status=="SUCCEEDED"%}{"taskstatus":"SUCCEEDED","output_url":"{{output_url}}","usage":{{json.dumps(usage,ensure_ascii=False)}}}{%elif status=="FAILED"%}{"taskstatus":"FAILED","error":"{{error}}"}{%else%}{"taskstatus":"PENDING"}{%endif%}', response='{%if error%}{"error":"{{error}}"}{%elif status=="SUCCEEDED"%}{"taskstatus":"SUCCEEDED","output_url":"{{output_url}}","usage":{{json.dumps(usage,ensure_ascii=False)}}}{%elif status=="FAILED"%}{"taskstatus":"FAILED","error":"{{error}}"}{%else%}{"taskstatus":"PENDING"}{%endif%}',
@ -89,7 +83,6 @@ UPDATE uapi SET stream='sync',headers='{"Content-Type":"application/json"}',
ioid='ktv_video_eval_evaluate_io' WHERE id='uapi_ktv_video_eval_evaluate'; ioid='ktv_video_eval_evaluate_io' WHERE id='uapi_ktv_video_eval_evaluate';
-- async query_apiname -- async query_apiname
UPDATE llm_api_map SET query_apiname='synth-status',query_period=5 WHERE llmid='llm_ktv_synth_generate';
UPDATE llm_api_map SET query_apiname='realesrgan-status',query_period=3 WHERE llmid='llm_ktv_realesrgan_upscale'; UPDATE llm_api_map SET query_apiname='realesrgan-status',query_period=3 WHERE llmid='llm_ktv_realesrgan_upscale';
-- ppid (v1/models 过滤要求 ppid IS NOT NULL) -- ppid (v1/models 过滤要求 ppid IS NOT NULL)

View File

@ -13,7 +13,7 @@ INSERT INTO pricing_program (id,providerid,name) VALUES
('pp_ktv_subtitle','FZoJv7EUEdKL3pUt0BQ5S','KTV-字幕渲染'), ('pp_ktv_subtitle','FZoJv7EUEdKL3pUt0BQ5S','KTV-字幕渲染'),
('pp_ktv_merge','FZoJv7EUEdKL3pUt0BQ5S','KTV-视频合并'), ('pp_ktv_merge','FZoJv7EUEdKL3pUt0BQ5S','KTV-视频合并'),
('pp_ktv_songrate','FZoJv7EUEdKL3pUt0BQ5S','KTV-歌曲评分'), ('pp_ktv_songrate','FZoJv7EUEdKL3pUt0BQ5S','KTV-歌曲评分'),
('pp_ktv_synth','FZoJv7EUEdKL3pUt0BQ5S','KTV-音乐合成'),
('pp_ktv_synth_status','FZoJv7EUEdKL3pUt0BQ5S','KTV-合成状态'), ('pp_ktv_synth_status','FZoJv7EUEdKL3pUt0BQ5S','KTV-合成状态'),
('pp_ktv_realesrgan','FZoJv7EUEdKL3pUt0BQ5S','KTV-超分辨率'), ('pp_ktv_realesrgan','FZoJv7EUEdKL3pUt0BQ5S','KTV-超分辨率'),
('pp_ktv_realesrgan_status','FZoJv7EUEdKL3pUt0BQ5S','KTV-超分状态'), ('pp_ktv_realesrgan_status','FZoJv7EUEdKL3pUt0BQ5S','KTV-超分状态'),
@ -30,7 +30,7 @@ UPDATE pricing_program_timing SET ppid='pp_ktv_face_compare' WHERE id='pt_ktv_fa
UPDATE pricing_program_timing SET ppid='pp_ktv_subtitle' WHERE id='pt_ktv_subtitle_render'; UPDATE pricing_program_timing SET ppid='pp_ktv_subtitle' WHERE id='pt_ktv_subtitle_render';
UPDATE pricing_program_timing SET ppid='pp_ktv_merge' WHERE id='pt_ktv_merge_video'; UPDATE pricing_program_timing SET ppid='pp_ktv_merge' WHERE id='pt_ktv_merge_video';
UPDATE pricing_program_timing SET ppid='pp_ktv_songrate' WHERE id='pt_ktv_songrate_evaluate'; UPDATE pricing_program_timing SET ppid='pp_ktv_songrate' WHERE id='pt_ktv_songrate_evaluate';
UPDATE pricing_program_timing SET ppid='pp_ktv_synth' WHERE id='pt_ktv_synth_generate';
UPDATE pricing_program_timing SET ppid='pp_ktv_synth_status' WHERE id='pt_ktv_synth_status'; UPDATE pricing_program_timing SET ppid='pp_ktv_synth_status' WHERE id='pt_ktv_synth_status';
UPDATE pricing_program_timing SET ppid='pp_ktv_realesrgan' WHERE id='pt_ktv_realesrgan_upscale'; UPDATE pricing_program_timing SET ppid='pp_ktv_realesrgan' WHERE id='pt_ktv_realesrgan_upscale';
UPDATE pricing_program_timing SET ppid='pp_ktv_realesrgan_status' WHERE id='pt_ktv_realesrgan_status'; UPDATE pricing_program_timing SET ppid='pp_ktv_realesrgan_status' WHERE id='pt_ktv_realesrgan_status';
@ -89,7 +89,7 @@ UPDATE llm_api_map SET ppid='pp_ktv_face_compare' WHERE llmid='llm_ktv_face_comp
UPDATE llm_api_map SET ppid='pp_ktv_subtitle' WHERE llmid='llm_ktv_subtitle_render'; UPDATE llm_api_map SET ppid='pp_ktv_subtitle' WHERE llmid='llm_ktv_subtitle_render';
UPDATE llm_api_map SET ppid='pp_ktv_merge' WHERE llmid='llm_ktv_merge_video'; UPDATE llm_api_map SET ppid='pp_ktv_merge' WHERE llmid='llm_ktv_merge_video';
UPDATE llm_api_map SET ppid='pp_ktv_songrate' WHERE llmid='llm_ktv_songrate_evaluate'; UPDATE llm_api_map SET ppid='pp_ktv_songrate' WHERE llmid='llm_ktv_songrate_evaluate';
UPDATE llm_api_map SET ppid='pp_ktv_synth' WHERE llmid='llm_ktv_synth_generate';
UPDATE llm_api_map SET ppid='pp_ktv_synth_status' WHERE llmid='llm_ktv_synth_status'; UPDATE llm_api_map SET ppid='pp_ktv_synth_status' WHERE llmid='llm_ktv_synth_status';
UPDATE llm_api_map SET ppid='pp_ktv_realesrgan' WHERE llmid='llm_ktv_realesrgan_upscale'; UPDATE llm_api_map SET ppid='pp_ktv_realesrgan' WHERE llmid='llm_ktv_realesrgan_upscale';
UPDATE llm_api_map SET ppid='pp_ktv_realesrgan_status' WHERE llmid='llm_ktv_realesrgan_status'; UPDATE llm_api_map SET ppid='pp_ktv_realesrgan_status' WHERE llmid='llm_ktv_realesrgan_status';

View File

@ -27,9 +27,6 @@ UPDATE uapi SET data='{%set c=joiner(",")%}{{%if video_file%}{{c()}}"video_url":
-- songrate: audio_file→audio_path -- songrate: audio_file→audio_path
UPDATE uapi SET data='{"audio_path":"{{audio_file}}"{%if scene%},"scene":"{{scene}}"{%endif%}}' WHERE id='uapi_ktv_songrate_evaluate'; UPDATE uapi SET data='{"audio_path":"{{audio_file}}"{%if scene%},"scene":"{{scene}}"{%endif%}}' WHERE id='uapi_ktv_songrate_evaluate';
-- synth-gen: audio_file→audio_path
UPDATE uapi SET data='{"lyrics":"{{lyrics}}"{%if style%},"style":"{{style}}"{%endif%}{%if audio_file%},"audio_path":"{{audio_file}}"{%endif%}}' WHERE id='uapi_ktv_synth_generate';
-- synth-status: GET, params in query string, no body -- synth-status: GET, params in query string, no body
UPDATE uapi SET httpmethod='GET', data=NULL, path='/synth/api/synth-status?task_id={{task_id}}' WHERE id='uapi_ktv_synth_status'; UPDATE uapi SET httpmethod='GET', data=NULL, path='/synth/api/synth-status?task_id={{task_id}}' WHERE id='uapi_ktv_synth_status';