diff --git a/wwwroot/llm/add_llm.dspy b/wwwroot/llm/add_llm.dspy new file mode 100644 index 0000000..7f1f689 --- /dev/null +++ b/wwwroot/llm/add_llm.dspy @@ -0,0 +1,51 @@ + +ns = params_kw.copy() +for k,v in ns.items(): + if v == 'NaN' or v == 'null': + ns[k] = None +id = params_kw.id +if not id or len(id) > 32: + id = uuid() +ns['id'] = id + + + +userorgid = await get_userorgid() +if not userorgid: + return { + "widgettype":"Error", + "options":{ + "title":"Authorization Error", + "timeout":3, + "cwidth":16, + "cheight":9, + "message":"Please login" + } + } +ns['org_id'] = userorgid + +db = DBPools() +dbname = get_module_dbname('pipeline_core') +async with db.sqlorContext(dbname) as sor: + r = await sor.C('llm', ns.copy()) + return { + "widgettype":"Message", + "options":{ + "cwidth":16, + "cheight":9, + "title":"Add Success", + "timeout":3, + "message":"ok" + } + } + +return { + "widgettype":"Error", + "options":{ + "title":"Add Error", + "cwidth":16, + "cheight":9, + "timeout":3, + "message":"failed" + } +} \ No newline at end of file diff --git a/wwwroot/llm/delete_llm.dspy b/wwwroot/llm/delete_llm.dspy new file mode 100644 index 0000000..53760d5 --- /dev/null +++ b/wwwroot/llm/delete_llm.dspy @@ -0,0 +1,47 @@ + +ns = { + 'id':params_kw['id'], +} + + +userorgid = await get_userorgid() +if not userorgid: + return { + "widgettype":"Error", + "options":{ + "title":"Authorization Error", + "timeout":3, + "cwidth":16, + "cheight":9, + "message":"Please login" + } + } +ns['org_id'] = userorgid + +db = DBPools() +dbname = get_module_dbname('pipeline_core') +async with db.sqlorContext(dbname) as sor: + r = await sor.D('llm', ns) + debug('delete success'); + return { + "widgettype":"Message", + "options":{ + "title":"Delete Success", + "timeout":3, + "cwidth":16, + "cheight":9, + "message":"ok" + } + } + +debug('Delete failed'); +return { + "widgettype":"Error", + "options":{ + "title":"Delete Error", + "timeout":3, + "cwidth":16, + "cheight":9, + "message":"failed" + } +} \ No newline at end of file diff --git a/wwwroot/llm/get_llm.dspy b/wwwroot/llm/get_llm.dspy new file mode 100644 index 0000000..325430a --- /dev/null +++ b/wwwroot/llm/get_llm.dspy @@ -0,0 +1,162 @@ + +ns = params_kw.copy() + + +userorgid = await get_userorgid() +if not userorgid: + return { + "widgettype":"Error", + "options":{ + "title":"Authorization Error", + "timeout":3, + "cwidth":16, + "cheight":9, + "message":"Please login" + } + } +ns['org_id'] = userorgid +ns['userorgid'] = userorgid + +debug(f'get_llm.dspy:{ns=}') +if not ns.get('page'): + ns['page'] = 1 +if not ns.get('sort'): + + + ns['sort'] = 'created_at' + + + +sql = '''select * from llm where 1=1 [[filterstr]]''' + +filterjson = params_kw.get('data_filter') +if filterjson and isinstance(filterjson, str): + try: + filterjson = json.loads(filterjson) + except (json.JSONDecodeError, TypeError): + filterjson = None +fields_str=r'''[ + { + "name": "id", + "title": "主键", + "type": "str", + "length": 32, + "nullable": "no" + }, + { + "name": "name", + "title": "模型名称", + "type": "str", + "length": 100, + "nullable": "no" + }, + { + "name": "provider", + "title": "供应商", + "type": "str", + "length": 50, + "nullable": "no" + }, + { + "name": "model_id", + "title": "模型标识", + "type": "str", + "length": 100 + }, + { + "name": "api_base", + "title": "API地址", + "type": "str", + "length": 500 + }, + { + "name": "api_key", + "title": "API密钥", + "type": "str", + "length": 500 + }, + { + "name": "max_tokens", + "title": "最大Token", + "type": "int", + "default": "8192" + }, + { + "name": "status", + "title": "状态", + "type": "str", + "length": 20, + "default": "active" + }, + { + "name": "capabilities", + "title": "能力分类", + "type": "str", + "length": 20, + "default": "text" + }, + { + "name": "org_id", + "title": "所属机构ID", + "type": "str", + "length": 32, + "default": "0" + }, + { + "name": "description", + "title": "描述", + "type": "text" + }, + { + "name": "created_at", + "title": "创建时间", + "type": "timestamp" + }, + { + "name": "updated_at", + "title": "更新时间", + "type": "timestamp" + } +]''' +ori_fields = json.loads(fields_str) +if not filterjson: + fields = [ f['name'] for f in ori_fields ] + filterjson = default_filterjson(fields, ns) + +# 确保 logined 过滤条件始终生效 +if filterjson: + if not isinstance(filterjson, dict) or 'AND' not in filterjson: + filterjson = {'AND': [filterjson] if filterjson else []} + + filterjson['AND'].append({'field': 'org_id', 'op': '=', 'var': '__logined_orgid__'}) + ns['__logined_orgid__'] = userorgid + + + +filterdic = ns.copy() +filterdic['filterstr'] = '' +filterdic['userorgid'] = '${userorgid}$' +filterdic['userid'] = '${userid}$' +if filterjson: + dbf = DBFilter(filterjson) + conds = dbf.gen(ns) + if conds: + ns.update(dbf.consts) + conds = f' and {conds}' + filterdic['filterstr'] = conds +ac = ArgsConvert('[[', ']]') +vars = ac.findAllVariables(sql) +NameSpace = {v:'${' + v + '}$' for v in vars if v != 'filterstr' } +filterdic.update(NameSpace) +sql = ac.convert(sql, filterdic) + +debug(f'{sql=}') +db = DBPools() +dbname = get_module_dbname('pipeline_core') +async with db.sqlorContext(dbname) as sor: + r = await sor.sqlPaging(sql, ns) + return r +return { + "total":0, + "rows":[] +} \ No newline at end of file diff --git a/wwwroot/llm/index.ui b/wwwroot/llm/index.ui new file mode 100644 index 0000000..4aecf39 --- /dev/null +++ b/wwwroot/llm/index.ui @@ -0,0 +1,207 @@ + +{ + "widgettype":"VBox", + "options":{"cheight":40,"width":"100%"}, + "subwidgets":[{ + "id":"llm_tbl", + "widgettype":"Tabular", + "options":{ + "width":"100%", + "height":"100%", + + + "title":"大语言模型表", + + + + + "css":"card", + + + "editable":{ + + "new_data_url":"{{entire_url('add_llm.dspy')}}", + + + "delete_data_url":"{{entire_url('delete_llm.dspy')}}", + + + "update_data_url":"{{entire_url('update_llm.dspy')}}" + + }, + + + "data_url":"{{entire_url('./get_llm.dspy')}}", + + "data_method":"GET", + "data_params":{{json.dumps(params_kw, indent=4, ensure_ascii=False)}}, + "row_options":{ + + + + "browserfields": { + "exclouded": [ + "id", + "api_key" + ], + "cwidth": {} +}, + + + "editexclouded":[ + "id", + "created_at", + "updated_at" +], + + "fields":[ + { + "name": "id", + "title": "主键", + "type": "str", + "length": 32, + "nullable": "no", + "cwidth": 18, + "uitype": "str", + "datatype": "str", + "label": "主键" + }, + { + "name": "name", + "title": "模型名称", + "type": "str", + "length": 100, + "nullable": "no", + "cwidth": 18, + "uitype": "str", + "datatype": "str", + "label": "模型名称" + }, + { + "name": "provider", + "title": "供应商", + "type": "str", + "length": 50, + "nullable": "no", + "cwidth": 18, + "uitype": "str", + "datatype": "str", + "label": "供应商" + }, + { + "name": "model_id", + "title": "模型标识", + "type": "str", + "length": 100, + "cwidth": 18, + "uitype": "str", + "datatype": "str", + "label": "模型标识" + }, + { + "name": "api_base", + "title": "API地址", + "type": "str", + "length": 500, + "cwidth": 18, + "uitype": "str", + "datatype": "str", + "label": "API地址" + }, + { + "name": "api_key", + "title": "API密钥", + "type": "str", + "length": 500, + "cwidth": 18, + "uitype": "str", + "datatype": "str", + "label": "API密钥" + }, + { + "name": "max_tokens", + "title": "最大Token", + "type": "int", + "default": "8192", + "length": 0, + "uitype": "int", + "datatype": "int", + "label": "最大Token" + }, + { + "name": "status", + "title": "状态", + "type": "str", + "length": 20, + "default": "active", + "cwidth": 18, + "uitype": "str", + "datatype": "str", + "label": "状态" + }, + { + "name": "capabilities", + "title": "能力分类", + "type": "str", + "length": 20, + "default": "text", + "cwidth": 18, + "uitype": "str", + "datatype": "str", + "label": "能力分类" + }, + { + "name": "org_id", + "title": "所属机构ID", + "type": "str", + "length": 32, + "default": "0", + "cwidth": 18, + "uitype": "str", + "datatype": "str", + "label": "所属机构ID" + }, + { + "name": "description", + "title": "描述", + "type": "text", + "length": 0, + "uitype": "text", + "datatype": "text", + "label": "描述" + }, + { + "name": "created_at", + "title": "创建时间", + "type": "timestamp", + "length": 0, + "uitype": "str", + "datatype": "timestamp", + "label": "创建时间" + }, + { + "name": "updated_at", + "title": "更新时间", + "type": "timestamp", + "length": 0, + "uitype": "str", + "datatype": "timestamp", + "label": "更新时间" + } +] + }, + + + + + + + + "page_rows":160, + "cache_limit":5 + } + + ,"binds":[] + +}] +} \ No newline at end of file diff --git a/wwwroot/llm/update_llm.dspy b/wwwroot/llm/update_llm.dspy new file mode 100644 index 0000000..b54b0a5 --- /dev/null +++ b/wwwroot/llm/update_llm.dspy @@ -0,0 +1,70 @@ + +ns = params_kw.copy() +for k,v in ns.items(): + if v == 'NaN' or v == 'null': + ns[k] = None + + +userorgid = await get_userorgid() +if not userorgid: + return { + "widgettype":"Error", + "options":{ + "title":"Authorization Error", + "timeout":3, + "cwidth":16, + "cheight":9, + "message":"Please login" + } + } +ns['org_id'] = userorgid + + + +db = DBPools() +dbname = get_module_dbname('pipeline_core') +async with db.sqlorContext(dbname) as sor: + + ns1 = { + + "org_id": userorgid, + + + "id": params_kw.id + } + recs = await sor.R('llm', ns1) + if len(recs) < 1: + return { + "widgettype":"Error", + "options":{ + "title":"Update Error", + "cwidth":16, + "cheight":9, + "timeout":3, + "message":"Record no exist or with wrong ownership" + } + } + + r = await sor.U('llm', ns) + debug('update success'); + return { + "widgettype":"Message", + "options":{ + "title":"Update Success", + "cwidth":16, + "cheight":9, + "timeout":3, + "message":"ok" + } + } + +return { + "widgettype":"Error", + "options":{ + "title":"Update Error", + "cwidth":16, + "cheight":9, + "timeout":3, + "message":"failed" + } +} \ No newline at end of file