diff --git a/llmage/init.py b/llmage/init.py index 26a429b..019d69f 100644 --- a/llmage/init.py +++ b/llmage/init.py @@ -26,6 +26,7 @@ from .utils import ( _warm_llmid_cache, get_llmid_cached, invalidate_llmid_cache, + get_plaza_models, ) from .llmclient import ( @@ -163,6 +164,7 @@ def load_llmage(): 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 diff --git a/llmage/utils.py b/llmage/utils.py index 9c2655b..2b94ec6 100644 --- a/llmage/utils.py +++ b/llmage/utils.py @@ -581,6 +581,33 @@ def _file_mime(filepath): mime, _ = _mimetypes.guess_type(filepath) return mime or 'application/octet-stream' +async def get_plaza_models(providerid='', catelogid='', search=''): + """Flat model list for cockpit plaza: filterable by provider/catalog/search, sorted by name.""" + env = ServerEnv() + data = await get_llms_by_catelog_to_customer( + catelogid=catelogid if catelogid else None, + orderby='a.name' + ) + result = [] + for cate in data: + for llm in cate.llms: + if providerid and llm.providerid != providerid: + continue + if search: + sl = search.lower() + if sl not in (llm.name or '').lower() and sl not in (llm.description or '').lower(): + continue + result.append({ + 'id': llm.id, 'name': llm.name, 'model': llm.model, + 'description': llm.description or '', 'iconid': llm.iconid, + 'providerid': llm.providerid, + 'provider_name': getattr(llm, 'provider_name', getattr(llm, 'orgname', '')), + 'catelog_id': getattr(llm, 'catelog_id', ''), + 'catelogname': getattr(llm, 'catelogname', ''), + 'pricing_display': getattr(llm, 'pricing_display', []), + }) + return result + ServerEnv().base64 = _base64 ServerEnv().mimetypes = _mimetypes ServerEnv().os = _os diff --git a/wwwroot/api/get_plaza_models.dspy b/wwwroot/api/get_plaza_models.dspy new file mode 100644 index 0000000..ebda296 --- /dev/null +++ b/wwwroot/api/get_plaza_models.dspy @@ -0,0 +1,33 @@ +import json + +providerid = (params_kw or {}).get('providerid', '') +catelogid = (params_kw or {}).get('catelogid', '') +search = (params_kw or {}).get('search', '').strip() + +data = await request._run_ns.get_llms_by_catelog_to_customer( + catelogid=catelogid if catelogid else None, + orderby='a.name' +) + +result = [] +for cate in data: + for llm in cate.llms: + if providerid and llm.providerid != providerid: + continue + if search and search.lower() not in (llm.name or '').lower(): + if search.lower() not in (llm.description or '').lower(): + continue + result.append({ + 'id': llm.id, + 'name': llm.name, + 'model': llm.model, + 'description': llm.description or '', + 'iconid': llm.iconid, + 'providerid': llm.providerid, + 'provider_name': getattr(llm, 'provider_name', getattr(llm, 'orgname', '')), + 'catelog_id': getattr(llm, 'catelog_id', ''), + 'catelogname': getattr(llm, 'catelogname', ''), + 'pricing_display': getattr(llm, 'pricing_display', []), + }) + +return json.dumps(result, ensure_ascii=False) diff --git a/wwwroot/model_plaza.ui b/wwwroot/model_plaza.ui index fe2d225..89b5bce 100644 --- a/wwwroot/model_plaza.ui +++ b/wwwroot/model_plaza.ui @@ -1,122 +1,169 @@ +{% set active_provider = params_kw.get('providerid', '') %} +{% set active_catelog = params_kw.get('catelogid', '') %} +{% set active_search = params_kw.get('search', '') %} {% set catelogs = get_llmcatelogs() %} -{% set providers = get_llms_sort_by_provider() %} +{% set providers = get_llmproviders() %} { - "widgettype": "VBox", - "options": { - "css": "filler", - "width": "100%" - }, - "subwidgets": [ - { - "widgettype": "VBox", - "options": { - "css": "plaza-header", - "width": "100%" - }, - "subwidgets": [ - { - "widgettype": "Title2", - "options": { - "otext": "模型广场", - "i18n": true, - "halign": "left" - } - }, - { - "widgettype": "Text", - "options": { - "otext": "探索和使用各类AI模型", - "i18n": true, - "halign": "left", - "wrap": true - } - }, - { - "widgettype": "HBox", - "options": { - "css": "plaza-view-switcher", - "cheight": 3 - }, - "subwidgets": [ - { - "widgettype": "Button", - "id": "btn_by_catelog", - "options": { - "label": "按分类", - "css": "plaza-view-btn plaza-view-active", - "cwidth": 15 - }, - "binds": [ - { - "wid": "self", - "event": "click", - "actiontype": "script", - "target": "self", - "script": "document.getElementById('plaza_view_provider').style.display='none'; document.getElementById('plaza_view_catelog').style.display='flex'; var a=bricks.getWidgetById('btn_by_catelog',bricks.app); var b=bricks.getWidgetById('btn_by_provider',bricks.app); if(a)a.dom_element.classList.add('plaza-view-active'); if(b)b.dom_element.classList.remove('plaza-view-active');" - } - ] - }, - { - "widgettype": "Button", - "id": "btn_by_provider", - "options": { - "label": "按供应商", - "css": "plaza-view-btn", - "cwidth": 15 - }, - "binds": [ - { - "wid": "self", - "event": "click", - "actiontype": "script", - "target": "self", - "script": "document.getElementById('plaza_view_catelog').style.display='none'; document.getElementById('plaza_view_provider').style.display='flex'; var a=bricks.getWidgetById('btn_by_catelog',bricks.app); var b=bricks.getWidgetById('btn_by_provider',bricks.app); if(a)a.dom_element.classList.remove('plaza-view-active'); if(b)b.dom_element.classList.add('plaza-view-active');" - } - ] - } - ] - } - ] - }, - { - "widgettype": "VBox", - "id": "plaza_view_catelog", - "options": { - "css": "filler", - "width": "100%", - "height": "100%" - }, - "subwidgets": [ - { - "widgettype": "urlwidget", - "options": { - "css": "filler", - "width": "100%", - "height": "100%", - "url": "{{entire_url('show_llms.ui')}}" - } - } - ] - }, - { - "widgettype": "VBox", - "id": "plaza_view_provider", - "options": { - "css": "filler", - "width": "100%", - "height": "100%" - }, - "subwidgets": [ - { - "widgettype": "urlwidget", - "options": { - "css": "filler", - "width": "100%", - "height": "100%", - "url": "{{entire_url('show_llms_by_providers.ui')}}" - } - } - ] - } - ] + "widgettype": "VBox", + "options": {"css": "filler", "width": "100%", "height": "100%", "gap": "8px"}, + "subwidgets": [ + { + "widgettype": "VBox", + "options": {"css": "plaza-header", "width": "100%"}, + "subwidgets": [ + { + "widgettype": "Title2", + "options": {"otext": "模型广场", "i18n": true, "halign": "left"} + }, + { + "widgettype": "HBox", + "options": {"width": "100%", "cheight": 4, "gap": "6px", "alignItems": "center"}, + "subwidgets": [ + { + "widgettype": "Input", + "id": "plaza_search_input", + "options": { + "cwidth": 40, + "cheight": 3, + "placeholder": "搜索模型名称或描述...", + "text": "{{active_search}}" + } + }, + { + "widgettype": "Button", + "id": "btn_search", + "options": {"label": "搜索", "css": "plaza-search-btn", "cwidth": 8, "cheight": 3}, + "binds": [ + { + "wid": "self", "event": "click", "actiontype": "script", "target": "self", + "script": "var inp=bricks.getWidgetById('plaza_search_input',bricks.app);var q=inp?inp.dom_element.querySelector('input').value:'';var u='{{entire_url('model_plaza.ui')}}?providerid={{active_provider}}&catelogid={{active_catelog}}&search='+encodeURIComponent(q);bricks.app.navigate_to(u);" + } + ] + }, +{% if active_search %} + { + "widgettype": "Button", + "options": {"label": "清除", "css": "plaza-clear-btn", "cwidth": 6, "cheight": 3}, + "binds": [ + { + "wid": "self", "event": "click", "actiontype": "urlwidget", "target": "-@", + "options": {"url": "{{entire_url('model_plaza.ui')}}?providerid={{active_provider}}&catelogid={{active_catelog}}"} + } + ] + }, +{% endif %} + {"widgettype": "Filler"} + ] + } + ] + }, + { + "widgettype": "VBox", + "options": {"width": "100%"}, + "subwidgets": [ + { + "widgettype": "HBox", + "options": {"css": "plaza-catelog-bar", "width": "100%", "wrap": true, "gap": "4px", "padding": "4px 0"}, + "binds": [], + "subwidgets": [ + { + "widgettype": "Button", + "id": "cat_all", + "options": { + "label": "全部", + "css": "{{'plaza-cat-btn selected' if not active_catelog else 'plaza-cat-btn'}}", + "cwidth": 8, "cheight": 2 + }, + "binds": [{ + "wid": "self", "event": "click", "actiontype": "urlwidget", "target": "-@", + "options": {"url": "{{entire_url('model_plaza.ui')}}?providerid={{active_provider}}&search={{active_search}}"} + }] + } +{% for c in catelogs %} + ,{ + "widgettype": "Button", + "id": "cat_{{c.id}}", + "options": { + "label": "{{c.name}}", + "css": "{{'plaza-cat-btn selected' if active_catelog==c.id else 'plaza-cat-btn'}}", + "cwidth": 10, "cheight": 2 + }, + "binds": [{ + "wid": "self", "event": "click", "actiontype": "urlwidget", "target": "-@", + "options": {"url": "{{entire_url('model_plaza.ui')}}?providerid={{active_provider}}&catelogid={{c.id}}&search={{active_search}}"} + }] + } +{% endfor %} + ] + } + ] + }, + { + "widgettype": "HBox", + "options": {"width": "100%", "height": "100%", "flex": "1", "gap": "8px"}, + "subwidgets": [ + { + "widgettype": "VBox", + "options": {"css": "plaza-sidebar", "cwidth": 20}, + "subwidgets": [ + { + "widgettype": "Title6", + "options": {"otext": "供应商", "i18n": true, "marginBottom": "4px"} + }, + { + "widgettype": "VScrollPanel", + "options": {"css": "filler", "flex": "1"}, + "subwidgets": [ + { + "widgettype": "VBox", + "options": {"gap": "2px"}, + "subwidgets": [ + { + "widgettype": "Button", + "id": "prov_all", + "options": { + "label": "全部供应商", + "css": "{{'plaza-prov-btn selected' if not active_provider else 'plaza-prov-btn'}}", + "cwidth": 18 + }, + "binds": [{ + "wid": "self", "event": "click", "actiontype": "urlwidget", "target": "-@", + "options": {"url": "{{entire_url('model_plaza.ui')}}?catelogid={{active_catelog}}&search={{active_search}}"} + }] + } +{% for p in providers %} + ,{ + "widgettype": "Button", + "id": "prov_{{p.providerid}}", + "options": { + "label": "{{p.orgname}}", + "css": "{{'plaza-prov-btn selected' if active_provider==p.providerid else 'plaza-prov-btn'}}", + "cwidth": 18 + }, + "binds": [{ + "wid": "self", "event": "click", "actiontype": "urlwidget", "target": "-@", + "options": {"url": "{{entire_url('model_plaza.ui')}}?providerid={{p.providerid}}&catelogid={{active_catelog}}&search={{active_search}}"} + }] + } +{% endfor %} + ] + } + ] + } + ] + }, + { + "widgettype": "RefreshWidget", + "id": "plaza_cards", + "options": { + "period_seconds": 0, + "url": "{{entire_url('plaza_model_cards.ui')}}?providerid={{active_provider}}&catelogid={{active_catelog}}&search={{active_search}}", + "css": "filler", + "width": "100%", + "height": "100%" + } + } + ] + } + ] } diff --git a/wwwroot/plaza_model_cards.ui b/wwwroot/plaza_model_cards.ui new file mode 100644 index 0000000..d3365a5 --- /dev/null +++ b/wwwroot/plaza_model_cards.ui @@ -0,0 +1,107 @@ +{% set providerid = params_kw.get('providerid', '') %} +{% set catelogid = params_kw.get('catelogid', '') %} +{% set search = params_kw.get('search', '') %} +{% set data = get_plaza_models(providerid, catelogid, search) %} +{% set ns = namespace(first=true) %} +{ + "widgettype": "VScrollPanel", + "options": { + "css": "filler", + "width": "100%", + "height": "100%" + }, + "subwidgets": [ + { + "widgettype": "DynamicColumn", + "options": { + "css": "plaza-grid", + "width": "100%", + "col_cwidth": 25, + "col_cgap": 1 + }, + "subwidgets": [ +{% for llm in data %} +{% if not ns.first %},{% endif %} + { + "widgettype": "VScrollPanel", + "options": { + "css": "card plaza-card", + "cwidth": 25, + "cheight": 16 + }, + "subwidgets": [ + { + "widgettype": "HBox", + "options": {"cheight": 2}, + "subwidgets": [ + { + "widgettype": "Svg", + "options": {"rate": 1.5, "url": "{{entire_url('/appbase/show_icon.dspy')}}?id={{llm.iconid}}"} + }, + { + "widgettype": "Title6", + "options": {"text": "{{llm.name}}"} + } + ] + }, + { + "widgettype": "VScrollPanel", + "options": {"css": "card-body-scroll", "flex": "1"}, + "subwidgets": [ + { + "widgettype": "Text", + "options": { + "text": {{json.dumps(llm.description, ensure_ascii=False)}}, + "wrap": true, + "halign": "left" + } + } + ] + }, + { + "widgettype": "VBox", + "options": {"css": "pricing-box", "cheight": 4}, + "subwidgets": [ +{% for pt in llm.pricing_display %} + { + "widgettype": "Text", + "options": { + "text": {{json.dumps(pt, ensure_ascii=False)}}, + "wrap": true, + "halign": "left", + "css": "pricing-text" + } + }{% if not loop.last %},{% endif %} +{% endfor %} + ] + } + ], + "binds": [ + { + "wid": "self", + "event": "click", + "actiontype": "urlwidget", + "target": "PopupWindow", + "popup_options": { + "title": "{{llm.name}}", +{% if int(params_kw._is_mobile or 0) %} + "width": "100%", + "height": "100%" +{% else %} + "width": "40%", + "height": "85%" +{% endif %} + }, + "options": { + "params": {"id": "{{llm.id}}"}, + "url": "{{entire_url('./llm_dialog.ui')}}" + } + } + ] + } +{% set ns.first = false %} +{% endfor %} + ] + } + ] +}