469 Commits

Author SHA1 Message Date
aad779b14e fix(sync): load_product_category_product SQL缺少providerid字段 — 产品同步providerid永远为空 2026-08-05 13:42:30 +08:00
31c8fde336 fix(balance): replace GETDEL with GET+DEL in Lua scripts for Redis 6.0 compat
GETDEL requires Redis 6.2+; server runs 6.0.16, so every finalize_balance
and refund_balance call failed silently ('Unknown Redis command called
from Lua script'). Consequences observed under concurrent load test:
- reserve:* keys leaked until 600s TTL instead of being released at finalize
- balance:{org} never reconciled with actual cost (over-deduction by
  max_cost-actual accumulated per call)
- FAILED-request refunds silently lost

GET+DEL inside a Lua script executes atomically (single-threaded),
so the pop semantics are unchanged.
2026-08-05 12:05:30 +08:00
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
7fd2c5622d bugfix 2026-08-03 14:36:03 +08:00
02178cd06e bugfix 2026-08-03 14:34:17 +08:00
dff5a681a0 bugfix 2026-08-03 13:55:56 +08:00
e5d27bc6bd fix: retry write_llmusage with *1,*2 on duplicate; gen() catch+refund but yield error not raise 2026-07-31 16:48:40 +08:00
670325e8eb fix: move refund to uapi_request except, remove gen() try/except that re-raised causing 500
gen() try/except re-raised internal errors from uapi_request → 500 response.
Moved refund_balance to uapi_request's except block where errors are properly
handled (yield error, normal generator exit).
2026-07-31 16:39:43 +08:00
4955e29f7d fix: re-export reserve/finalize/refund_balance from balance.py in utils.py (init.py imports them from .utils) 2026-07-31 15:48:43 +08:00
a02cd09459 fix: Redis connection error now returns no_redis fallback instead of 429 2026-07-31 15:42:51 +08:00
6351ee9b56 feat: Redis atomic balance reservation — prevent concurrent overspend
- balance.py: reserve_balance/finalize_balance/refund_balance with Lua
- utils.py: get_model_max_cost from llmusage history, update_model_max_cost
- init.py: register reserve_balance, finalize_balance, refund_balance on env
- chat/completions/index.dspy: reserve before inference, refund on exception
- DB migration: llm.max_cost DECIMAL(10,4) for historical max charge
2026-07-31 14:57:17 +08:00
21bf30d2a6 fix: add finally block to write_llmusage — recover from GeneratorExit on client disconnect
When client disconnects mid-stream, Python GC kills the generator with
GeneratorExit (BaseException subclass), which is NOT caught by except Exception.
write_llmusage at line 93 was never reached, causing ~16% usage records lost.
finally block ensures llmusage is written even on GeneratorExit.
2026-07-30 10:49:06 +08:00
4f0a8a4c2e fix: cwidth:25 cheight:16 card-body-scroll — match old working layout 2026-07-29 17:11:40 +08:00
7148656f99 fix: dedupe providers, SearchBar input, urlwidget internal nav
- Removed iconid from get_llmproviders GROUP BY (caused duplicates)
- Replaced KeyinText with SearchBar (built-in input+search+clear)
- Switched navigation from window.location.assign to urlwidget
  targeting sage_main_content (stays in workspace, no full reload)
2026-07-29 13:22:53 +08:00
78dd2b0788 fix: cate.llms -> cate['llms'] — get_llms_by_catelog_to_customer returns plain dict not DictObject 2026-07-29 11:15:36 +08:00
1c25542e72 feat: cockpit-style model plaza with search, category tabs, provider sidebar
New layout:
- Search bar (top) with fuzzy name/description match
- Category tabs (wrapping, single-select)
- Provider sidebar (left, single-select)
- Card grid (right, sorted by name, refreshed on filter change)
- Both category/provider unselected = all published models

Added get_plaza_models() for flat filtered model listing.
Supports combined filtering: provider + category + search.
2026-07-28 17:55:17 +08:00
68dd92a353 sort: add a.name as final sort column in model listing queries
get_llmage_llm and get_llms_by_catelog had ORDER BY ending
at a.id — model names within same provider/catalog appeared
in arbitrary order. Added a.name as final sort key.
2026-07-28 16:23:41 +08:00
e246f91e77 fix: asyncinference KeyError status→.get(status) 2026-07-24 14:34:16 +08:00
ef1e228f19 perf: llmid缓存(启动预热+内存查),v1/chat/completions去3表JOIN 2026-07-23 18:26:52 +08:00
5e48622e2a bugfix 2026-07-21 14:44:27 +08:00
cf5d48d96c bugfix 2026-07-21 14:43:15 +08:00
6aa2f76c1e fix: list_llms按catelogid排序-改SQL ORDER BY替代sort参数,修复同catelog被拆分成多组 2026-07-21 10:52:05 +08:00
112c86e80b bugfix 2026-07-20 19:34:24 +08:00
7b792c03d4 bugfix 2026-07-20 19:32:48 +08:00
3ff16da646 bugfix 2026-07-20 19:20:57 +08:00
ee68fbefa7 bugfix 2026-07-20 19:19:47 +08:00
84c03a7878 bugfix 2026-07-20 19:18:17 +08:00
933085b7f3 bugfix 2026-07-20 19:13:35 +08:00
142dcbb02e bugfix 2026-07-20 11:29:06 +08:00
e0855b6415 refactor: 将 kimi-k3 多模态内容构建改为纯 Jinja2 模板
- 删除 build_kimi_content() Python 函数
- 在 SQL data 模板中用纯 Jinja2 语法构建 content 数组
- 新增 file_to_b64() 和 file_mime() 辅助函数注册到 ServerEnv
- 保持向后兼容,功能不变
2026-07-20 10:48:22 +08:00
ce17c48022 feat: build_kimi_content 迁入 llimage/utils.py,自含 webpath→base64 转换 2026-07-17 18:12:17 +08:00
83e1fa8ba3 feat: load_product_category_product返回providerid
将llm.providerid传递给product_management的导入流程
2026-07-13 14:53:46 +08:00
802ab896d8 fix: correct tab/space indentation — tabs for sync/async/llmclient, spaces for product_interface 2026-07-09 11:43:53 +08:00
064df40b4a fix: tab/space indentation in tenantid lines 2026-07-09 11:42:22 +08:00
fd65b5a59e feat: write tenantid to llmusage from API request params
Populate llmusage.tenantid from params_kw.tenantid in all 6 inference paths:
syncinference, product_interface, llmclient, asyncinference
2026-07-09 11:38:43 +08:00
8556564455 chore: remove llm_metrics and user_llm_policy from git tracking
These are generated CRUD files, not source code.
2026-07-07 18:02:57 +08:00
43e8b40774 feat: multi-currency billing — llmusage currency fields + real-time exchange 2026-07-07 15:43:03 +08:00
cf16d60893 refactor: drop cost from llmusage, add tenantid, clean references
- Remove cost field from llm_charging, get_accounting_llmusages, backup, syncinference
- Add tenantid column migration SQL
2026-07-07 12:23:51 +08:00
b5a63d412c fix: 1)get_pricing_display加model过滤避免混入其他模型价格 2)卡片布局分开描述VScrollPanel+定价VBox确保定价始终可见 2026-07-06 15:44:44 +08:00
70b5502a18 fix: always use llm.model (vendor model name) instead of user-passed model param 2026-07-01 17:24:40 +08:00
6890af7638 fix: 清理所有 dspy 文件的 import 语句
删除已在 ahserver/ServerEnv 预加载的模块:
  json, datetime, getID, debug, curDateString, timestampstr,
  get_sor_context, time, DictObject, partial, FileStorage, os

删除模块内部 import:
  from llmage.utils import get_llmusage_by_id, read_ioinfo_content

同步将 get_llmusage_by_id, read_ioinfo_content 加入 load_llmage() 导出,
dspy 中通过 ServerEnv 全局调用。
2026-06-30 17:38:37 +08:00
3e386ed86f debug: backend_accounting 加 info 级别日志追踪记账循环
- info 替代 debug 确保启动日志必定输出
- 每轮循环后打印 got N records
- exception 消息加前缀便于搜索

用于排查 receive 1166 条记录但循环不处理的根因
2026-06-30 11:53:33 +08:00
f6d4462ed8 fix: 后台任务引用保持+异常处理
- 保存asyncio.create_task引用防止GC回收
- query_task_status添加try/except和CancelledError处理
- 确保连接在任务异常时正确释放
2026-06-27 12:54:10 +08:00
6c2ccda2c7 修复llmusage展示脚本500错误并优化CRUD性能
- 将llmusage_ioinfo_display.ui和llmusage_usages_display.ui从Jinja模板改为.dspy脚本
- 在utils.py中添加get_llmusage_by_id和read_ioinfo_content两个函数
- 重写get_llmusage.dspy,避免sqlPaging子查询包装,分离count和data查询
- 为llmusage表添加use_time索引提升排序性能
- 更新load_path.py和json/llmusage.json引用新的.dspy文件
2026-06-26 17:55:52 +08:00
Hermes Agent
9d2f8edc51 fix: load_product_category_product SQL移除不存在的sort_order字段 2026-06-23 18:01:56 +08:00
Hermes Agent
64f10e177b refactor: load_product_category_product returns data only, no DB writes
- Returns standardized {categories, products} format
- product_management handles dedup/update logic
- Categories: source_id, name, description, product_type, sort_order
- Products: source_category_id, resource_ref_id, product_code, product_name, etc.
2026-06-23 16:06:09 +08:00
Hermes Agent
a060dd1405 refactor: use resource_ref_id (llm.id) instead of product_code
- product_interface: all functions take resource_ref_id as primary param
- _resolve_llm: lookup by llm.id directly instead of model name
- load_product_category_product: write llm.id as resource_ref_id in product table
- No more code→id mapping needed in resource module
2026-06-23 15:38:34 +08:00
Hermes Agent
8910019528 feat: implement product module standard interface (product_interface.py)
- get_product_display: pricing display text + extra info
- check_product_availability: ppid + pricing data validation
- check_product_consumable: full pre-check (availability + balance + pricing)
- execute_product_service: non-streaming inference (no HTTP request dependency)
- execute_product_service_stream: streaming inference as async generator
- calculate_product_cost: buffered_charging with customer discount
- sync_uapi_request_product: sync inference product wrapper
- async_uapi_request_product: async task submission product wrapper
- Register product_interface dict on ServerEnv in load_llmage()
2026-06-23 15:09:00 +08:00
Hermes Agent
510122a282 refactor: replace import_products_from_llmage with load_product_category_product
- New function: load_product_category_product(parent_category_id)
- Reads llmage catalogs + published models, writes sub-categories + products
  into product_management DB directly
- Removed env registration (called via importlib, not ServerEnv)
2026-06-23 14:47:15 +08:00
Hermes Agent
df41478170 feat: register import_products_from_llmage for product_management import
- Add import_products_from_llmage() function that exports llmage catalogs
  and published models as product categories and products
- Register on ServerEnv so product_management can call it when
  resource_module='llmage'
2026-06-23 13:47:15 +08:00