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.
190 lines
5.8 KiB
Python
190 lines
5.8 KiB
Python
import asyncio
|
|
from appPublic.registerfunction import RegisterFunction
|
|
from sqlor.dbpools import DBPools, get_sor_context
|
|
from ahserver.serverenv import ServerEnv
|
|
from appPublic.log import debug
|
|
from .keling import keling_token
|
|
from .jimeng import jimeng_auth_headers
|
|
from .utils import (
|
|
llm_query_orders,
|
|
read_webpath,
|
|
llm_query_price,
|
|
get_user_tpac,
|
|
get_tpac_balance,
|
|
get_llm_by_model,
|
|
get_llms_by_catelog,
|
|
get_llms_sort_by_provider,
|
|
get_llmcatelogs,
|
|
get_llms_by_catelog_to_customer,
|
|
get_llmproviders,
|
|
get_llm,
|
|
get_llmage_llm,
|
|
get_llm_catelogs,
|
|
invalidate_uapi_cache,
|
|
get_llmusage_by_id,
|
|
read_ioinfo_content,
|
|
_warm_llmid_cache,
|
|
get_llmid_cached,
|
|
invalidate_llmid_cache,
|
|
get_plaza_models,
|
|
)
|
|
|
|
from .llmclient import (
|
|
inference_generator,
|
|
inference
|
|
)
|
|
from .accounting import (
|
|
checkCustomerBalance,
|
|
llm_charging,
|
|
get_accounting_llmusages,
|
|
backend_accounting,
|
|
llm_accounting,
|
|
backup_accounted_llmusage,
|
|
get_failed_accounting_records,
|
|
llm_accoung_failed
|
|
)
|
|
from .stats import get_llmage_stats
|
|
|
|
from .asyncinference import (
|
|
get_asynctask_status,
|
|
query_task_status,
|
|
get_today_asynctask_list
|
|
)
|
|
|
|
from .product_interface import (
|
|
get_product_display,
|
|
check_product_availability,
|
|
check_product_consumable,
|
|
execute_product_service,
|
|
execute_product_service_stream,
|
|
calculate_product_cost,
|
|
)
|
|
|
|
|
|
async def load_product_category_product(parent_category_id):
|
|
"""Return llmage catalogs and published models as standardized import data.
|
|
|
|
Called by product_management.import_categories_and_products().
|
|
Only reads source data and returns it in the standard format.
|
|
Does NOT write to product_management tables — that's product_management's job.
|
|
|
|
Returns:
|
|
{
|
|
'success': True,
|
|
'categories': [{'source_id', 'name', 'description', 'product_type', 'product_type_title', 'sort_order'}, ...],
|
|
'products': [{'source_category_id', 'resource_ref_id', 'product_code', 'product_name', 'product_type', 'brief_intro', 'sort_order'}, ...]
|
|
}
|
|
"""
|
|
env = ServerEnv()
|
|
|
|
# Read source data from llmage
|
|
async with get_sor_context(env, 'llmage') as sor:
|
|
catelogs = await sor.R('llmcatelog', {})
|
|
if not catelogs:
|
|
return {'success': False, 'error': 'llmage中没有产品类别数据'}
|
|
|
|
llm_sql = """select a.id, a.name, a.model, a.description, a.status,
|
|
m.llmcatelogid, lc.name as catelogname
|
|
from llm a
|
|
join llm_api_map m on a.id = m.llmid
|
|
join llmcatelog lc on m.llmcatelogid = lc.id
|
|
where m.isdefaultcatelog = '1' and a.status = 'published'
|
|
order by lc.name, a.name"""
|
|
llms = await sor.sqlExe(llm_sql, {})
|
|
|
|
# Build standardized categories
|
|
categories = []
|
|
for c in catelogs:
|
|
categories.append({
|
|
'source_id': c.id,
|
|
'name': c.name,
|
|
'description': getattr(c, 'description', '') or '',
|
|
'product_type': 'llm_model',
|
|
'product_type_title': '大模型按量',
|
|
'sort_order': 0,
|
|
})
|
|
|
|
# Build standardized products
|
|
products = []
|
|
for llm in (llms or []):
|
|
products.append({
|
|
'source_category_id': llm.llmcatelogid,
|
|
'resource_ref_id': llm.id,
|
|
'product_code': llm.model,
|
|
'product_name': llm.name,
|
|
'product_type': 'llm_model',
|
|
'brief_intro': getattr(llm, 'description', '') or '',
|
|
'sort_order': 0,
|
|
'providerid': getattr(llm, 'providerid', '') or '',
|
|
})
|
|
|
|
return {
|
|
'success': True,
|
|
'categories': categories,
|
|
'products': products,
|
|
}
|
|
|
|
|
|
def _on_hot_reload(data=None):
|
|
"""Event handler for hot_reload — invalidate caches."""
|
|
from appPublic.log import debug
|
|
debug(f'[llmage] on_hot_reload called, invalidating caches (data={data})')
|
|
invalidate_uapi_cache()
|
|
invalidate_llmid_cache()
|
|
|
|
|
|
def load_llmage():
|
|
env = ServerEnv()
|
|
env.llm_query_orders = llm_query_orders
|
|
env.read_webpath = read_webpath
|
|
env.get_llm_by_model = get_llm_by_model
|
|
env.llm_charging = llm_charging
|
|
env.get_accounting_llmusages = get_accounting_llmusages
|
|
env.llm_accounting = llm_accounting
|
|
env.get_today_asynctask_list = get_today_asynctask_list
|
|
env.get_asynctask_status = get_asynctask_status
|
|
env.query_task_status = query_task_status
|
|
env.get_llm = get_llm
|
|
env.get_llmage_llm = get_llmage_llm
|
|
env.get_llm_catelogs = get_llm_catelogs
|
|
env.invalidate_uapi_cache = invalidate_uapi_cache
|
|
env.inference = inference
|
|
env.get_user_tpac = get_user_tpac
|
|
env.get_tpac_balance = get_tpac_balance
|
|
env.inference_generator = inference_generator
|
|
env.get_llms_by_catelog = get_llms_by_catelog
|
|
env.get_llmid_cached = get_llmid_cached
|
|
env.invalidate_llmid_cache = invalidate_llmid_cache
|
|
# 启动时预热 llmid 缓存
|
|
asyncio.ensure_future(_warm_llmid_cache(env))
|
|
env.get_llmcatelogs = get_llmcatelogs
|
|
env.checkCustomerBalance = checkCustomerBalance
|
|
env.get_llmproviders = get_llmproviders
|
|
env.get_llms_sort_by_provider = get_llms_sort_by_provider
|
|
env.keling_token = keling_token
|
|
env.llm_query_price = llm_query_price
|
|
env.get_llms_by_catelog_to_customer = get_llms_by_catelog_to_customer
|
|
env.get_plaza_models = get_plaza_models
|
|
env.backup_accounted_llmusage = backup_accounted_llmusage
|
|
env.read_ioinfo_content = read_ioinfo_content
|
|
env.get_llmusage_by_id = get_llmusage_by_id
|
|
env.get_failed_accounting_records = get_failed_accounting_records
|
|
env.get_llmage_stats = get_llmage_stats
|
|
# Product module standard interface
|
|
env.product_interface = {
|
|
'module_name': 'llmage',
|
|
'get_product_display': get_product_display,
|
|
'check_product_availability': check_product_availability,
|
|
'check_product_consumable': check_product_consumable,
|
|
'execute_product_service': execute_product_service,
|
|
'execute_product_service_stream': execute_product_service_stream,
|
|
'calculate_product_cost': calculate_product_cost,
|
|
'load_product_category_product': load_product_category_product,
|
|
}
|
|
# Bind hot_reload event — module-level function, ref safe (module keeps it alive)
|
|
if hasattr(env, 'event_dispatcher'):
|
|
env.event_dispatcher.bind('hot_reload', _on_hot_reload)
|
|
rf = RegisterFunction()
|
|
rf.register('jimeng_auth_headers', jimeng_auth_headers)
|
|
|