yumoqing e3f61f446d feat(balance): extend atomic balance reserve to async+sync+product paths
- balance.py: module-level redis.asyncio singleton (works in all
  processes without env.redis injection), centralized tpac/self-org
  skip policy, userid param, extend_reserve, invalidate_balance_cache,
  finalize updates max_cost on cold start (no reserve)
- llmclient.py: unified reserve injection in _inference_generator
  (covers all 22 dspy entries); fix luid mismatch (reserve key ==
  llmusage id == finalize key); stream exception refund
- syncinference.py / asyncinference.py: luid reuse + failure refunds
  (submit failure, outer exception, query_task_status FAILED)
- product_interface.py: reserve + refund on product stream/non-stream
- accounting.py: finalize after llm_accounting (was missing for
  direct llmage accounting path -> reserve leaked until TTL)
- init.py: lambda signatures synced
- v1/chat/completions/index.dspy: pass userid to reserve
2026-08-04 18:35:53 +08:00

63 lines
1.9 KiB
Plaintext

async def gen():
env = request._run_ns.copy()
f = partial(inference_generator, request, params_kw=params_kw)
try:
if params_kw.stream:
async for l in f():
yield f'data: {l}\n'
yield 'data: [DONE]\n\n'
else:
async for l in f():
yield l
except Exception as e:
luid = params_kw.get('_luid')
if luid:
try:
await env.refund_balance(luid)
except:
pass
import json as _json
err = _json.dumps({"error": str(e), "status": "FAILED"}, ensure_ascii=False)
yield f'data: {err}\n' if params_kw.stream else err
debug_params('params_kw', params_kw)
catelogid = params_kw.catelogid or 't2t'
if params_kw.off_peak:
off_peak = params_kw.off_peak
if off_peak in [True, "Y" "y", 1, "1"]:
off_peak = True
else:
off_peak = False
params_kw.off_peak = off_peak
userid = await get_user()
userorgid = await get_userorgid()
if userid is None:
debug(f'need login')
return openai_403()
if not params_kw.prompt and not params_kw.messages:
debug(f'missing prompt and messages, model={params_kw.model}')
d = return_error('Missing need data(prompt or messages)')
return json_response(d, status=400)
env = request._run_ns
llmid = await env.get_llmid_cached(env, params_kw.model or 'qwen3-max', catelogid)
if not llmid:
debug(f'{params_kw.model=} not found for catelogid={catelogid}')
return openai_400()
params_kw.llmid = llmid
params_kw.llmcatelogid = catelogid
debug(f'{params_kw.llmid=}')
luid = getID()
reserved = await env.reserve_balance(params_kw.llmid, userorgid, luid, userid=userid)
if not reserved.get('ok'):
debug(f'{userid=} balance not enough: {reserved}')
return openai_429()
params_kw._luid = luid
params_kw._reserved = reserved
f = await checkCustomerBalance(params_kw.llmid, userid, userorgid)
if not f and reserved.get('no_redis'):
debug(f'{userid=} balance not enough (DB fallback)')
return openai_429()
return await env.stream_response(request, gen, content_type='application/json')