From ad7d9086ceb7fe61bd3c8296afee8679241c8d67 Mon Sep 17 00:00:00 2001 From: yumoqing Date: Fri, 3 Jul 2026 14:50:40 +0800 Subject: [PATCH] fix: CRUD endpoint return format - remove json.dumps, use widgettype format --- json/.llm.json.swp | Bin 0 -> 12288 bytes wwwroot/api/llm_api_map_create.dspy | 2 +- wwwroot/api/llm_api_map_delete.dspy | 2 +- wwwroot/api/llm_create.dspy | 9 +- wwwroot/api/llm_delete.dspy | 9 +- wwwroot/api/llm_update.dspy | 6 +- wwwroot/api/llmcatelog_create.dspy | 2 +- wwwroot/api/llmcatelog_delete.dspy | 2 +- wwwroot/api/llmcatelog_update.dspy | 2 +- .../llmusage_accounting_failed_create.dspy | 9 +- .../llmusage_accounting_failed_delete.dspy | 9 +- .../llmusage_accounting_failed_update.dspy | 9 +- wwwroot/api/llmusage_create.dspy | 9 +- wwwroot/api/llmusage_delete.dspy | 9 +- wwwroot/api/llmusage_history_create.dspy | 3 +- wwwroot/api/llmusage_history_delete.dspy | 3 +- wwwroot/api/llmusage_history_update.dspy | 3 +- wwwroot/api/llmusage_update.dspy | 9 +- wwwroot/llm_metrics/add_llm_metrics.dspy | 55 ++++ wwwroot/llm_metrics/delete_llm_metrics.dspy | 47 +++ wwwroot/llm_metrics/get_llm_metrics.dspy | 76 +++++ wwwroot/llm_metrics/index.ui | 301 ++++++++++++++++++ wwwroot/llm_metrics/update_llm_metrics.dspy | 78 +++++ .../user_llm_policy/add_user_llm_policy.dspy | 55 ++++ .../delete_user_llm_policy.dspy | 47 +++ .../user_llm_policy/get_user_llm_policy.dspy | 76 +++++ wwwroot/user_llm_policy/index.ui | 292 +++++++++++++++++ .../update_user_llm_policy.dspy | 78 +++++ 28 files changed, 1148 insertions(+), 54 deletions(-) create mode 100644 json/.llm.json.swp create mode 100644 wwwroot/llm_metrics/add_llm_metrics.dspy create mode 100644 wwwroot/llm_metrics/delete_llm_metrics.dspy create mode 100644 wwwroot/llm_metrics/get_llm_metrics.dspy create mode 100644 wwwroot/llm_metrics/index.ui create mode 100644 wwwroot/llm_metrics/update_llm_metrics.dspy create mode 100644 wwwroot/user_llm_policy/add_user_llm_policy.dspy create mode 100644 wwwroot/user_llm_policy/delete_user_llm_policy.dspy create mode 100644 wwwroot/user_llm_policy/get_user_llm_policy.dspy create mode 100644 wwwroot/user_llm_policy/index.ui create mode 100644 wwwroot/user_llm_policy/update_user_llm_policy.dspy diff --git a/json/.llm.json.swp b/json/.llm.json.swp new file mode 100644 index 0000000000000000000000000000000000000000..fbbb6b2a3f29e11b272c15128adfff0933ceb72c GIT binary patch literal 12288 zcmeI2ZEO@p7{>?k?NHw82NDd)F_3@?ZLQJNOB!Q>FGL~wK}1R2?A=bUTlQ|(z1^02 z9-<&%36TUaAPFx))A|-jFoYCAiAIC)fyDSpV`9SHmBcUl#l#Qje`a5Ad#x3skz_Xf zvU{`7JTvp3XP%iWmu+9Oa#gyswS(ZAL&!VlR`ku;HGlf(`n$x0hZlIchP- zmg`1O&)~zVK0%F*aM)%u%Ph4SMnTQ9w!V^CBDyerSIcwP?N&OF3P=T{0#X5~fK)&#AQg}bNCj>j1srP{c@5pY$?x(u|2{2o z_dm*;R6r^q6_5%@1*8H}0jYpgKq?>=kP1izqyjgf0;)#H+vxW<{0{*5|Nr3o|JB)q zjDd^bGcW?Sf&y3zTEMrn2q}XO@XJg>egfZtQ(zA;z$2gq)bAwZM{o}81r?xy8Q|wT z2pI*#U>nfDTyS{?A)kUHU<**eTu@KJ9*lsaU>|r3ybAiki=YcE1`EJEK){vT3AqH$ zgD=5{U^jRLIKTvb;3e<^cmmu9W`bXDBjg8g7JLle1-rp!unKg6d%+^G0L%j|fPgEv z5^@0?1N*@nzyb{UwGD0aU%4or zZL3zY2AAAZsffN%*FxPXI`C{G2VNJ7Y+!2b=WK?i8mm*L>Ap_sGD-Va_pmTQ*DYFZ*3e76sOH(Ecq19Q zxU6dxe=1VcQf;*W!TAK>qC(+`1;+1?9Ji!2l_^C{i#Hy6^Cl2ZbG=N@_c}Dw>9>*_ z&^4!*W;)uvkci^t@~IuQgC~PXu}sUgl!ROAsCJ&g8-0eqeqJwX=0H%x8^1GX@vy{T z&=9xvnxU*8ffE-ecmXpMmzdGxl?XZf{!b|J4Jz=^e;}|g&9%rI3sOH?rWB)M{Tr%V z%=IdWx7_+xR~I?JpZY`)jgQTa_GEBAI6gRs>Z706j&7{psbiXkFOPW6sy5PSl!(0{Rl84bJ1<=9R{_ zqFR=&DZDUB*k0vyIz{_c!(~Zps2NxU_Y~z@+hjW%5|~nAs*T9@8;$QUfuG=x9Iku$ zFHLi%#+o!4^u0ldpgsf%9)o*gl)vvra1VhLX*z?qwzks=_dw`^28xX4!g9FXr3$u7PS!TP-ndkP zNL1psm(T31pEzAVeJBwvG@7%R{n-Q6(LL3@Ta(5G#J&;PjUC%s-TqN^*UqFJpPk4I z2KC|NF&o;WbKMk?E+-v!cZ1wq+A=DOqi))lM4a&~E=d=E>=(JHQLgcMC+5Xp{>|M4 z3CWW(F(--cL`pXI6}|VRncSVwI`Z`N%BLQSjW0eEll$>j3eLeoqlBB-L?yh?M^~?a zsO!QB5GhshW)3g-PCyMauNScox`<)Z&hrHl9k}^UPfMolWCyV&`oj_K!V@z05`L|z izBfE}?31yPO|_9D)q^|yc%e?#5JP}xd>uh)nfwLjSO8D} literal 0 HcmV?d00001 diff --git a/wwwroot/api/llm_api_map_create.dspy b/wwwroot/api/llm_api_map_create.dspy index 29d168e..4fb86c4 100644 --- a/wwwroot/api/llm_api_map_create.dspy +++ b/wwwroot/api/llm_api_map_create.dspy @@ -59,4 +59,4 @@ try: except Exception as e: result['options'] = {'title': 'Error', 'message': f'添加失败: {str(e)}', 'type': 'error'} -return json.dumps(result, ensure_ascii=False) +return result diff --git a/wwwroot/api/llm_api_map_delete.dspy b/wwwroot/api/llm_api_map_delete.dspy index f860cd3..e5281f1 100644 --- a/wwwroot/api/llm_api_map_delete.dspy +++ b/wwwroot/api/llm_api_map_delete.dspy @@ -27,4 +27,4 @@ try: except Exception as e: result['options'] = {'title': 'Error', 'message': f'删除失败: {str(e)}', 'type': 'error'} -return json.dumps(result, ensure_ascii=False) +return result diff --git a/wwwroot/api/llm_create.dspy b/wwwroot/api/llm_create.dspy index aebb723..17b7d4e 100644 --- a/wwwroot/api/llm_create.dspy +++ b/wwwroot/api/llm_create.dspy @@ -1,6 +1,6 @@ #!/usr/bin/env python3 -result = {'success': False, 'message': ''} +result = {"widgettype":"Error","options":{"title":"操作失败","message":"Invalid request","cwidth":16,"cheight":9,"timeout":3}} try: dbname = get_module_dbname('llmage') @@ -11,9 +11,8 @@ try: data.pop('data_filter', None) data['id'] = getID() await sor.C('llm', data) - result['success'] = True - result['message'] = '创建成功' + result = {"widgettype":"Message","options":{"title":"创建成功","message":"ok","cwidth":16,"cheight":9,"timeout":3}} except Exception as e: - result['message'] = str(e) + result["options"] = {"title":"操作失败","message":str(e),"cwidth":16,"cheight":9,"timeout":3} -return json.dumps(result, ensure_ascii=False, default=str) +return result diff --git a/wwwroot/api/llm_delete.dspy b/wwwroot/api/llm_delete.dspy index 1887c58..8edaf91 100644 --- a/wwwroot/api/llm_delete.dspy +++ b/wwwroot/api/llm_delete.dspy @@ -1,6 +1,6 @@ #!/usr/bin/env python3 -result = {'success': False, 'message': ''} +result = {"widgettype":"Error","options":{"title":"操作失败","message":"Invalid request","cwidth":16,"cheight":9,"timeout":3}} try: dbname = get_module_dbname('llmage') @@ -14,9 +14,8 @@ try: result['message'] = '缺少id' else: await sor.D('llm', {'id': record_id}) - result['success'] = True - result['message'] = '删除成功' + result = {"widgettype":"Message","options":{"title":"删除成功","message":"ok","cwidth":16,"cheight":9,"timeout":3}} except Exception as e: - result['message'] = str(e) + result["options"] = {"title":"操作失败","message":str(e),"cwidth":16,"cheight":9,"timeout":3} -return json.dumps(result, ensure_ascii=False, default=str) +return result diff --git a/wwwroot/api/llm_update.dspy b/wwwroot/api/llm_update.dspy index fa64288..9bbf89d 100644 --- a/wwwroot/api/llm_update.dspy +++ b/wwwroot/api/llm_update.dspy @@ -7,13 +7,13 @@ data.pop('rows', None) data.pop('data_filter', None) if not data.get('id'): - return json.dumps({'widgettype':'Error','options':{'title':'Update Error','message':'缺少id'}}, ensure_ascii=False) + return {'widgettype':'Error','options':{'title':'Update Error','message':'缺少id'}} try: db = DBPools() dbname = get_module_dbname('llmage') async with db.sqlorContext(dbname) as sor: await sor.U('llm', data) - return json.dumps({'widgettype':'Message','options':{'title':'Update Success','message':'ok'}}, ensure_ascii=False) + return {'widgettype':'Message','options':{'title':'Update Success','message':'ok'}} except Exception as e: - return json.dumps({'widgettype':'Error','options':{'title':'Update Error','message':str(e)}}, ensure_ascii=False) + return {'widgettype':'Error','options':{'title':'Update Error','message':str(e)}} diff --git a/wwwroot/api/llmcatelog_create.dspy b/wwwroot/api/llmcatelog_create.dspy index 732c768..0acb861 100644 --- a/wwwroot/api/llmcatelog_create.dspy +++ b/wwwroot/api/llmcatelog_create.dspy @@ -25,4 +25,4 @@ try: except Exception as e: result['options'] = {'title': '错误', 'message': str(e), 'type': 'error'} -return json.dumps(result, ensure_ascii=False) +return result diff --git a/wwwroot/api/llmcatelog_delete.dspy b/wwwroot/api/llmcatelog_delete.dspy index 68b2c93..51edfe7 100644 --- a/wwwroot/api/llmcatelog_delete.dspy +++ b/wwwroot/api/llmcatelog_delete.dspy @@ -29,4 +29,4 @@ try: except Exception as e: result['options'] = {'title': '错误', 'message': str(e), 'type': 'error'} -return json.dumps(result, ensure_ascii=False) +return result diff --git a/wwwroot/api/llmcatelog_update.dspy b/wwwroot/api/llmcatelog_update.dspy index 5e30900..e89b08d 100644 --- a/wwwroot/api/llmcatelog_update.dspy +++ b/wwwroot/api/llmcatelog_update.dspy @@ -27,4 +27,4 @@ try: except Exception as e: result['options'] = {'title': '错误', 'message': str(e), 'type': 'error'} -return json.dumps(result, ensure_ascii=False) +return result diff --git a/wwwroot/api/llmusage_accounting_failed_create.dspy b/wwwroot/api/llmusage_accounting_failed_create.dspy index ca4266b..eda8f7f 100644 --- a/wwwroot/api/llmusage_accounting_failed_create.dspy +++ b/wwwroot/api/llmusage_accounting_failed_create.dspy @@ -1,6 +1,6 @@ #!/usr/bin/env python3 -result = {'success': False, 'message': ''} +result = {"widgettype":"Error","options":{"title":"操作失败","message":"Invalid request","cwidth":16,"cheight":9,"timeout":3}} try: dbname = get_module_dbname('llmage') @@ -8,9 +8,8 @@ try: data = params_kw data['id'] = getID() await sor.C('llmusage_accounting_failed', data) - result['success'] = True - result['message'] = '创建成功' + result = {"widgettype":"Message","options":{"title":"创建成功","message":"ok","cwidth":16,"cheight":9,"timeout":3}} except Exception as e: - result['message'] = str(e) + result["options"] = {"title":"操作失败","message":str(e),"cwidth":16,"cheight":9,"timeout":3} -return json.dumps(result, ensure_ascii=False, default=str) +return result diff --git a/wwwroot/api/llmusage_accounting_failed_delete.dspy b/wwwroot/api/llmusage_accounting_failed_delete.dspy index 0b27c7f..b8bd618 100644 --- a/wwwroot/api/llmusage_accounting_failed_delete.dspy +++ b/wwwroot/api/llmusage_accounting_failed_delete.dspy @@ -1,6 +1,6 @@ #!/usr/bin/env python3 -result = {'success': False, 'message': ''} +result = {"widgettype":"Error","options":{"title":"操作失败","message":"Invalid request","cwidth":16,"cheight":9,"timeout":3}} try: dbname = get_module_dbname('llmage') @@ -10,9 +10,8 @@ try: result['message'] = '缺少id参数' else: await sor.D('llmusage_accounting_failed', {'id': rid}) - result['success'] = True - result['message'] = '删除成功' + result = {"widgettype":"Message","options":{"title":"删除成功","message":"ok","cwidth":16,"cheight":9,"timeout":3}} except Exception as e: - result['message'] = str(e) + result["options"] = {"title":"操作失败","message":str(e),"cwidth":16,"cheight":9,"timeout":3} -return json.dumps(result, ensure_ascii=False, default=str) +return result diff --git a/wwwroot/api/llmusage_accounting_failed_update.dspy b/wwwroot/api/llmusage_accounting_failed_update.dspy index 549a4ba..06544a2 100644 --- a/wwwroot/api/llmusage_accounting_failed_update.dspy +++ b/wwwroot/api/llmusage_accounting_failed_update.dspy @@ -1,6 +1,6 @@ #!/usr/bin/env python3 -result = {'success': False, 'message': ''} +result = {"widgettype":"Error","options":{"title":"操作失败","message":"Invalid request","cwidth":16,"cheight":9,"timeout":3}} try: dbname = get_module_dbname('llmage') @@ -15,9 +15,8 @@ try: if data.get('handled') == '1' and not data.get('handled_time'): data['handled_time'] = datetime.now().strftime('%Y-%m-%d %H:%M:%S') await sor.U('llmusage_accounting_failed', data) - result['success'] = True - result['message'] = '更新成功' + result = {"widgettype":"Message","options":{"title":"更新成功","message":"ok","cwidth":16,"cheight":9,"timeout":3}} except Exception as e: - result['message'] = str(e) + result["options"] = {"title":"操作失败","message":str(e),"cwidth":16,"cheight":9,"timeout":3} -return json.dumps(result, ensure_ascii=False, default=str) +return result diff --git a/wwwroot/api/llmusage_create.dspy b/wwwroot/api/llmusage_create.dspy index b2193b3..83cf158 100644 --- a/wwwroot/api/llmusage_create.dspy +++ b/wwwroot/api/llmusage_create.dspy @@ -1,6 +1,6 @@ #!/usr/bin/env python3 -result = {'success': False, 'message': ''} +result = {"widgettype":"Error","options":{"title":"操作失败","message":"Invalid request","cwidth":16,"cheight":9,"timeout":3}} try: dbname = get_module_dbname('llmage') @@ -8,9 +8,8 @@ try: data = params_kw data['id'] = getID() await sor.C('llmusage', data) - result['success'] = True - result['message'] = '创建成功' + result = {"widgettype":"Message","options":{"title":"创建成功","message":"ok","cwidth":16,"cheight":9,"timeout":3}} except Exception as e: - result['message'] = str(e) + result["options"] = {"title":"操作失败","message":str(e),"cwidth":16,"cheight":9,"timeout":3} -return json.dumps(result, ensure_ascii=False, default=str) +return result diff --git a/wwwroot/api/llmusage_delete.dspy b/wwwroot/api/llmusage_delete.dspy index 11f9459..932c6a6 100644 --- a/wwwroot/api/llmusage_delete.dspy +++ b/wwwroot/api/llmusage_delete.dspy @@ -1,6 +1,6 @@ #!/usr/bin/env python3 -result = {'success': False, 'message': ''} +result = {"widgettype":"Error","options":{"title":"操作失败","message":"Invalid request","cwidth":16,"cheight":9,"timeout":3}} try: dbname = get_module_dbname('llmage') @@ -10,9 +10,8 @@ try: result['message'] = '缺少id参数' else: await sor.D('llmusage', {'id': rid}) - result['success'] = True - result['message'] = '删除成功' + result = {"widgettype":"Message","options":{"title":"删除成功","message":"ok","cwidth":16,"cheight":9,"timeout":3}} except Exception as e: - result['message'] = str(e) + result["options"] = {"title":"操作失败","message":str(e),"cwidth":16,"cheight":9,"timeout":3} -return json.dumps(result, ensure_ascii=False, default=str) +return result diff --git a/wwwroot/api/llmusage_history_create.dspy b/wwwroot/api/llmusage_history_create.dspy index 55543f6..c4cc166 100644 --- a/wwwroot/api/llmusage_history_create.dspy +++ b/wwwroot/api/llmusage_history_create.dspy @@ -1,3 +1,2 @@ #!/usr/bin/env python3 -result = {'success': False, 'message': '历史数据为只读,不可新增'} -return json.dumps(result, ensure_ascii=False, default=str) +return {"widgettype":"Error","options":{"title":"操作失败","message":"历史数据为只读,不可新增","cwidth":16,"cheight":9,"timeout":3}} diff --git a/wwwroot/api/llmusage_history_delete.dspy b/wwwroot/api/llmusage_history_delete.dspy index aef1dc3..f4c0254 100644 --- a/wwwroot/api/llmusage_history_delete.dspy +++ b/wwwroot/api/llmusage_history_delete.dspy @@ -1,3 +1,2 @@ #!/usr/bin/env python3 -result = {'success': False, 'message': '历史数据为只读,不可删除'} -return json.dumps(result, ensure_ascii=False, default=str) +return {"widgettype":"Error","options":{"title":"操作失败","message":"历史数据为只读,不可删除","cwidth":16,"cheight":9,"timeout":3}} diff --git a/wwwroot/api/llmusage_history_update.dspy b/wwwroot/api/llmusage_history_update.dspy index 220a62c..41bf714 100644 --- a/wwwroot/api/llmusage_history_update.dspy +++ b/wwwroot/api/llmusage_history_update.dspy @@ -1,3 +1,2 @@ #!/usr/bin/env python3 -result = {'success': False, 'message': '历史数据为只读,不可修改'} -return json.dumps(result, ensure_ascii=False, default=str) +return {"widgettype":"Error","options":{"title":"操作失败","message":"历史数据为只读,不可修改","cwidth":16,"cheight":9,"timeout":3}} diff --git a/wwwroot/api/llmusage_update.dspy b/wwwroot/api/llmusage_update.dspy index 1adee52..228e706 100644 --- a/wwwroot/api/llmusage_update.dspy +++ b/wwwroot/api/llmusage_update.dspy @@ -1,6 +1,6 @@ #!/usr/bin/env python3 -result = {'success': False, 'message': ''} +result = {"widgettype":"Error","options":{"title":"操作失败","message":"Invalid request","cwidth":16,"cheight":9,"timeout":3}} try: dbname = get_module_dbname('llmage') @@ -12,9 +12,8 @@ try: else: data['id'] = rid await sor.U('llmusage', data) - result['success'] = True - result['message'] = '更新成功' + result = {"widgettype":"Message","options":{"title":"更新成功","message":"ok","cwidth":16,"cheight":9,"timeout":3}} except Exception as e: - result['message'] = str(e) + result["options"] = {"title":"操作失败","message":str(e),"cwidth":16,"cheight":9,"timeout":3} -return json.dumps(result, ensure_ascii=False, default=str) +return result diff --git a/wwwroot/llm_metrics/add_llm_metrics.dspy b/wwwroot/llm_metrics/add_llm_metrics.dspy new file mode 100644 index 0000000..86bf155 --- /dev/null +++ b/wwwroot/llm_metrics/add_llm_metrics.dspy @@ -0,0 +1,55 @@ + +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['userorgid'] = userorgid + + +db = DBPools() +dbname = get_module_dbname('llmage') +async with db.sqlorContext(dbname) as sor: + r = await sor.C('llm_metrics', ns.copy()) + _new_rows = await sor.sqlExe("SELECT * FROM llm_metrics WHERE id = ${id}$", {'id': id}) + _user_data = _new_rows[0] if _new_rows else ns + return { + "widgettype":"Message", + "options":{ + "cwidth":16, + "cheight":9, + "title":"Add Success", + "timeout":3, + "message":"ok", + "user_data": _user_data + } + } + +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_metrics/delete_llm_metrics.dspy b/wwwroot/llm_metrics/delete_llm_metrics.dspy new file mode 100644 index 0000000..faff46f --- /dev/null +++ b/wwwroot/llm_metrics/delete_llm_metrics.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['userorgid'] = userorgid + +db = DBPools() +dbname = get_module_dbname('llmage') +async with db.sqlorContext(dbname) as sor: + r = await sor.D('llm_metrics', 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_metrics/get_llm_metrics.dspy b/wwwroot/llm_metrics/get_llm_metrics.dspy new file mode 100644 index 0000000..d2b023d --- /dev/null +++ b/wwwroot/llm_metrics/get_llm_metrics.dspy @@ -0,0 +1,76 @@ + +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['userorgid'] = userorgid +ns['userorgid'] = userorgid + +debug(f'get_llm_metrics.dspy:{ns=}') +if not ns.get('page'): + ns['page'] = 1 +if not ns.get('sort'): + + + ns['sort'] = 'use_time' + + + +sql = '''select a.*, b.userid_text, c.llmid_text, d.userorgid_text +from (select * from llm_metrics where 1=1 [[filterstr]]) a left join (select id as userid, + username as userid_text from users where 1 = 1) b on a.userid = b.userid left join (select id as llmid, + model as llmid_text from llm where 1 = 1) c on a.llmid = c.llmid left join (select id as userorgid, + orgname as userorgid_text from organization where 1 = 1) d on a.userorgid = d.userorgid''' + + +filterjson = json.loads('{"AND":[{"field":"userorgid","op":"=","var":"userorgid"},{"field":"use_date","op":"between","var":"use_date"}]}') + + +# 确保 logined 过滤条件始终生效 +if filterjson: + if not isinstance(filterjson, dict) or 'AND' not in filterjson: + filterjson = {'AND': [filterjson] if filterjson else []} + + filterjson['AND'].append({'field': 'userorgid', '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('llmage') +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_metrics/index.ui b/wwwroot/llm_metrics/index.ui new file mode 100644 index 0000000..f48439c --- /dev/null +++ b/wwwroot/llm_metrics/index.ui @@ -0,0 +1,301 @@ + +{ + "widgettype":"VBox", + "options":{"height":"100%","width":"100%"}, + "subwidgets":[ +{ + "id":"llm_metrics_tbl", + "widgettype":"Tabular", + "options":{ + "width":"100%", + "height":"100%", + + + "title":"模型使用指标", + + + + + "toolbar":{ + "tools": [ + { + "name": "filter", + "label": "搜索" + } + ] +}, + + "css":"card", + + + "editable":{ + + "new_data_url":"{{entire_url('add_llm_metrics.dspy')}}", + + + "delete_data_url":"{{entire_url('delete_llm_metrics.dspy')}}", + + + "update_data_url":"{{entire_url('update_llm_metrics.dspy')}}" + + + }, + + + "data_url":"{{entire_url('./get_llm_metrics.dspy')}}", + + "data_method":"GET", + "data_params":{{json.dumps(params_kw, indent=4, ensure_ascii=False)}}, + "row_options":{ + + + + "browserfields": { + "exclouded": [ + "id", + "taskid" + ], + "cwidth": {}, + "alters": null +}, + + + "editexclouded":[ + "id", + "amount", + "responsed_seconds", + "finish_seconds", + "status", + "taskid" +], + + "fields":[ + { + "name": "id", + "title": "id", + "type": "str", + "length": 32, + "cwidth": 18, + "uitype": "str", + "datatype": "str", + "label": "id" + }, + { + "name": "userid", + "title": "用户id", + "type": "str", + "length": 32, + "label": "用户id", + "uitype": "code", + "valueField": "userid", + "textField": "userid_text", + "params": { + "dbname": "{{get_module_dbname('llmage')}}", + "table": "users", + "tblvalue": "id", + "tbltext": "username", + "valueField": "userid", + "textField": "userid_text" + }, + "dataurl": "{{entire_url('/appbase/get_code.dspy')}}" + }, + { + "name": "userorgid", + "title": "用户机构", + "type": "str", + "length": 32, + "label": "用户机构", + "uitype": "code", + "valueField": "userorgid", + "textField": "userorgid_text", + "params": { + "dbname": "{{get_module_dbname('llmage')}}", + "table": "organization", + "tblvalue": "id", + "tbltext": "orgname", + "valueField": "userorgid", + "textField": "userorgid_text" + }, + "dataurl": "{{entire_url('/appbase/get_code.dspy')}}" + }, + { + "name": "llmid", + "title": "模型", + "type": "str", + "length": 32, + "label": "模型", + "uitype": "code", + "valueField": "llmid", + "textField": "llmid_text", + "params": { + "dbname": "{{get_module_dbname('llmage')}}", + "table": "llm", + "tblvalue": "id", + "tbltext": "model", + "valueField": "llmid", + "textField": "llmid_text" + }, + "dataurl": "{{entire_url('/appbase/get_code.dspy')}}" + }, + { + "name": "use_date", + "title": "使用日期", + "type": "date", + "length": 0, + "uitype": "date", + "datatype": "date", + "label": "使用日期" + }, + { + "name": "use_time", + "title": "使用时间", + "type": "timestamp", + "length": 0, + "uitype": "str", + "datatype": "timestamp", + "label": "使用时间" + }, + { + "name": "input_tokens", + "title": "输入tokens", + "type": "int", + "default": "0", + "length": 0, + "uitype": "int", + "datatype": "int", + "label": "输入tokens" + }, + { + "name": "output_tokens", + "title": "输出tokens", + "type": "int", + "default": "0", + "length": 0, + "uitype": "int", + "datatype": "int", + "label": "输出tokens" + }, + { + "name": "total_tokens", + "title": "总tokens", + "type": "int", + "default": "0", + "length": 0, + "uitype": "int", + "datatype": "int", + "label": "总tokens" + }, + { + "name": "amount", + "title": "金额", + "type": "float", + "length": 18, + "dec": 4, + "default": "0", + "cwidth": 18, + "uitype": "float", + "datatype": "float", + "label": "金额" + }, + { + "name": "responsed_seconds", + "title": "响应时长(秒)", + "type": "float", + "length": 18, + "dec": 2, + "nullable": true, + "cwidth": 18, + "uitype": "float", + "datatype": "float", + "label": "响应时长(秒)" + }, + { + "name": "finish_seconds", + "title": "完成时长(秒)", + "type": "float", + "length": 18, + "dec": 2, + "nullable": true, + "cwidth": 18, + "uitype": "float", + "datatype": "float", + "label": "完成时长(秒)" + }, + { + "name": "status", + "title": "状态", + "type": "str", + "length": 32, + "nullable": true, + "cwidth": 18, + "uitype": "str", + "datatype": "str", + "label": "状态" + }, + { + "name": "taskid", + "title": "任务号", + "type": "str", + "length": 32, + "nullable": true, + "cwidth": 18, + "uitype": "str", + "datatype": "str", + "label": "任务号" + } +] + }, + + + + "search_form":{ + "css":"card", + "padding":"8px", + "show_label":false, + "submit_label":"搜索", + "submit_css":"primary", + "fields":[ + { + "name": "userorgid", + "uitype": "code", + "placeholder": "用户机构", + "cwidth": 15, + "valueField": "userorgid", + "textField": "userorgid_text", + "params": { + "dbname": "{{get_module_dbname('llmage')}}", + "table": "organization", + "tblvalue": "id", + "tbltext": "orgname", + "valueField": "userorgid", + "textField": "userorgid_text" + }, + "dataurl": "{{entire_url('/appbase/get_code.dspy')}}?prepend_all=1" + }, + { + "name": "use_date", + "uitype": "date", + "placeholder": "使用日期", + "cwidth": 15 + } +] + }, + + + "filter_labels":{ + "use_date": "使用日期" +}, + + + "filter_title":"查询条件", + + + + "page_rows":160, + "cache_limit":5 + } + + ,"binds":[] + +}] +} \ No newline at end of file diff --git a/wwwroot/llm_metrics/update_llm_metrics.dspy b/wwwroot/llm_metrics/update_llm_metrics.dspy new file mode 100644 index 0000000..4e4711b --- /dev/null +++ b/wwwroot/llm_metrics/update_llm_metrics.dspy @@ -0,0 +1,78 @@ + +ns = params_kw.copy() +for k,v in list(ns.items()): + if v == 'NaN' or v == 'null' or v == '' or v is None: + if k != 'id': + del ns[k] + else: + ns[k] = None if v in ('NaN', 'null') else v + + +userorgid = await get_userorgid() +if not userorgid: + return { + "widgettype":"Error", + "options":{ + "title":"Authorization Error", + "timeout":3, + "cwidth":16, + "cheight":9, + "message":"Please login" + } + } +ns['userorgid'] = userorgid + + + + +db = DBPools() +dbname = get_module_dbname('llmage') +async with db.sqlorContext(dbname) as sor: + + ns1 = { + + "userorgid": userorgid, + + + "id": params_kw.id + } + recs = await sor.R('llm_metrics', 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" + } + } + + tblfields = json.loads('[{"name":"id","title":"id","type":"str","length":32},{"name":"userid","title":"用户id","type":"str","length":32},{"name":"userorgid","title":"用户机构","type":"str","length":32},{"name":"llmid","title":"模型","type":"str","length":32},{"name":"use_date","title":"使用日期","type":"date"},{"name":"use_time","title":"使用时间","type":"timestamp"},{"name":"input_tokens","title":"输入tokens","type":"int","default":"0"},{"name":"output_tokens","title":"输出tokens","type":"int","default":"0"},{"name":"total_tokens","title":"总tokens","type":"int","default":"0"},{"name":"amount","title":"金额","type":"float","length":18,"dec":4,"default":"0"},{"name":"responsed_seconds","title":"响应时长(秒)","type":"float","length":18,"dec":2,"nullable":true},{"name":"finish_seconds","title":"完成时长(秒)","type":"float","length":18,"dec":2,"nullable":true},{"name":"status","title":"状态","type":"str","length":32,"nullable":true},{"name":"taskid","title":"任务号","type":"str","length":32,"nullable":true}]') + tblfname = [ f['name'] for f in tblfields ] + other_keys = [ k for k in ns.keys() if k in tblfname and k != 'id' ] + if len(other_keys) > 0: + r = await sor.U('llm_metrics', 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 diff --git a/wwwroot/user_llm_policy/add_user_llm_policy.dspy b/wwwroot/user_llm_policy/add_user_llm_policy.dspy new file mode 100644 index 0000000..30948b1 --- /dev/null +++ b/wwwroot/user_llm_policy/add_user_llm_policy.dspy @@ -0,0 +1,55 @@ + +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['orgid'] = userorgid + + +db = DBPools() +dbname = get_module_dbname('llmage') +async with db.sqlorContext(dbname) as sor: + r = await sor.C('user_llm_policy', ns.copy()) + _new_rows = await sor.sqlExe("SELECT * FROM user_llm_policy WHERE id = ${id}$", {'id': id}) + _user_data = _new_rows[0] if _new_rows else ns + return { + "widgettype":"Message", + "options":{ + "cwidth":16, + "cheight":9, + "title":"Add Success", + "timeout":3, + "message":"ok", + "user_data": _user_data + } + } + +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/user_llm_policy/delete_user_llm_policy.dspy b/wwwroot/user_llm_policy/delete_user_llm_policy.dspy new file mode 100644 index 0000000..936cc61 --- /dev/null +++ b/wwwroot/user_llm_policy/delete_user_llm_policy.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['orgid'] = userorgid + +db = DBPools() +dbname = get_module_dbname('llmage') +async with db.sqlorContext(dbname) as sor: + r = await sor.D('user_llm_policy', 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/user_llm_policy/get_user_llm_policy.dspy b/wwwroot/user_llm_policy/get_user_llm_policy.dspy new file mode 100644 index 0000000..9eaf90e --- /dev/null +++ b/wwwroot/user_llm_policy/get_user_llm_policy.dspy @@ -0,0 +1,76 @@ + +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['orgid'] = userorgid +ns['userorgid'] = userorgid + +debug(f'get_user_llm_policy.dspy:{ns=}') +if not ns.get('page'): + ns['page'] = 1 +if not ns.get('sort'): + + + ns['sort'] = 'userid' + + + +sql = '''select a.*, b.userid_text, c.llmid_text, d.orgid_text +from (select * from user_llm_policy where 1=1 [[filterstr]]) a left join (select id as userid, + username as userid_text from users where 1 = 1) b on a.userid = b.userid left join (select id as llmid, + model as llmid_text from llm where 1 = 1) c on a.llmid = c.llmid left join (select id as orgid, + orgname as orgid_text from organization where 1 = 1) d on a.orgid = d.orgid''' + + +filterjson = json.loads('{"AND":[{"field":"orgid","op":"=","var":"orgid"}]}') + + +# 确保 logined 过滤条件始终生效 +if filterjson: + if not isinstance(filterjson, dict) or 'AND' not in filterjson: + filterjson = {'AND': [filterjson] if filterjson else []} + + filterjson['AND'].append({'field': 'orgid', '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('llmage') +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/user_llm_policy/index.ui b/wwwroot/user_llm_policy/index.ui new file mode 100644 index 0000000..950faaf --- /dev/null +++ b/wwwroot/user_llm_policy/index.ui @@ -0,0 +1,292 @@ + +{ + "widgettype":"VBox", + "options":{"height":"100%","width":"100%"}, + "subwidgets":[ +{ + "id":"user_llm_policy_tbl", + "widgettype":"Tabular", + "options":{ + "width":"100%", + "height":"100%", + + + "title":"用户模型策略", + + + + + "toolbar":{ + "tools": [ + { + "name": "filter", + "label": "搜索" + } + ] +}, + + "css":"card", + + + "editable":{ + + "new_data_url":"{{entire_url('add_user_llm_policy.dspy')}}", + + + "delete_data_url":"{{entire_url('delete_user_llm_policy.dspy')}}", + + + "update_data_url":"{{entire_url('update_user_llm_policy.dspy')}}" + + + }, + + + "data_url":"{{entire_url('./get_user_llm_policy.dspy')}}", + + "data_method":"GET", + "data_params":{{json.dumps(params_kw, indent=4, ensure_ascii=False)}}, + "row_options":{ + + + + "browserfields": { + "exclouded": [ + "id", + "orgid", + "created_at", + "updated_at" + ], + "cwidth": {}, + "alters": null +}, + + + "editexclouded":[ + "id", + "orgid", + "created_at", + "updated_at", + "created_by" +], + + "fields":[ + { + "name": "id", + "title": "id", + "type": "str", + "length": 32, + "cwidth": 18, + "uitype": "str", + "datatype": "str", + "label": "id" + }, + { + "name": "orgid", + "title": "所属机构", + "type": "str", + "length": 32, + "label": "所属机构", + "uitype": "code", + "valueField": "orgid", + "textField": "orgid_text", + "params": { + "dbname": "{{get_module_dbname('llmage')}}", + "table": "organization", + "tblvalue": "id", + "tbltext": "orgname", + "valueField": "orgid", + "textField": "orgid_text" + }, + "dataurl": "{{entire_url('/appbase/get_code.dspy')}}" + }, + { + "name": "userid", + "title": "用户", + "type": "str", + "length": 32, + "label": "用户", + "uitype": "code", + "valueField": "userid", + "textField": "userid_text", + "params": { + "dbname": "{{get_module_dbname('llmage')}}", + "table": "users", + "tblvalue": "id", + "tbltext": "username", + "valueField": "userid", + "textField": "userid_text" + }, + "dataurl": "{{entire_url('/appbase/get_code.dspy')}}" + }, + { + "name": "llmid", + "title": "模型", + "type": "str", + "length": 32, + "nullable": true, + "label": "模型", + "uitype": "code", + "valueField": "llmid", + "textField": "llmid_text", + "params": { + "dbname": "{{get_module_dbname('llmage')}}", + "table": "llm", + "tblvalue": "id", + "tbltext": "model", + "valueField": "llmid", + "textField": "llmid_text" + }, + "dataurl": "{{entire_url('/appbase/get_code.dspy')}}" + }, + { + "name": "daily_call_limit", + "title": "日调用上限", + "type": "int", + "nullable": true, + "length": 0, + "uitype": "int", + "datatype": "int", + "label": "日调用上限" + }, + { + "name": "monthly_call_limit", + "title": "月调用上限", + "type": "int", + "nullable": true, + "length": 0, + "uitype": "int", + "datatype": "int", + "label": "月调用上限" + }, + { + "name": "spending_alert", + "title": "花费预警", + "type": "float", + "length": 18, + "dec": 2, + "nullable": true, + "cwidth": 18, + "uitype": "float", + "datatype": "float", + "label": "花费预警" + }, + { + "name": "spending_ceiling", + "title": "费用上限", + "type": "float", + "length": 18, + "dec": 2, + "nullable": true, + "cwidth": 18, + "uitype": "float", + "datatype": "float", + "label": "费用上限" + }, + { + "name": "max_concurrency", + "title": "并发上限", + "type": "int", + "nullable": true, + "length": 0, + "uitype": "int", + "datatype": "int", + "label": "并发上限" + }, + { + "name": "status", + "title": "状态", + "type": "str", + "length": 10, + "default": "active", + "cwidth": 10, + "uitype": "str", + "datatype": "str", + "label": "状态" + }, + { + "name": "created_at", + "title": "创建时间", + "type": "timestamp", + "nullable": true, + "length": 0, + "uitype": "str", + "datatype": "timestamp", + "label": "创建时间" + }, + { + "name": "updated_at", + "title": "更新时间", + "type": "timestamp", + "nullable": true, + "length": 0, + "uitype": "str", + "datatype": "timestamp", + "label": "更新时间" + }, + { + "name": "created_by", + "title": "创建人", + "type": "str", + "length": 32, + "nullable": true, + "cwidth": 18, + "uitype": "str", + "datatype": "str", + "label": "创建人" + }, + { + "name": "remark", + "title": "备注", + "type": "str", + "length": 500, + "nullable": true, + "cwidth": 18, + "uitype": "str", + "datatype": "str", + "label": "备注" + } +] + }, + + + + "search_form":{ + "css":"card", + "padding":"8px", + "show_label":false, + "submit_label":"搜索", + "submit_css":"primary", + "fields":[ + { + "name": "orgid", + "uitype": "code", + "placeholder": "所属机构", + "cwidth": 15, + "valueField": "orgid", + "textField": "orgid_text", + "params": { + "dbname": "{{get_module_dbname('llmage')}}", + "table": "organization", + "tblvalue": "id", + "tbltext": "orgname", + "valueField": "orgid", + "textField": "orgid_text" + }, + "dataurl": "{{entire_url('/appbase/get_code.dspy')}}?prepend_all=1" + } +] + }, + + + + + + "page_rows":160, + "cache_limit":5 + } + + ,"binds":[] + +}] +} \ No newline at end of file diff --git a/wwwroot/user_llm_policy/update_user_llm_policy.dspy b/wwwroot/user_llm_policy/update_user_llm_policy.dspy new file mode 100644 index 0000000..8dc9eab --- /dev/null +++ b/wwwroot/user_llm_policy/update_user_llm_policy.dspy @@ -0,0 +1,78 @@ + +ns = params_kw.copy() +for k,v in list(ns.items()): + if v == 'NaN' or v == 'null' or v == '' or v is None: + if k != 'id': + del ns[k] + else: + ns[k] = None if v in ('NaN', 'null') else v + + +userorgid = await get_userorgid() +if not userorgid: + return { + "widgettype":"Error", + "options":{ + "title":"Authorization Error", + "timeout":3, + "cwidth":16, + "cheight":9, + "message":"Please login" + } + } +ns['orgid'] = userorgid + + + + +db = DBPools() +dbname = get_module_dbname('llmage') +async with db.sqlorContext(dbname) as sor: + + ns1 = { + + "orgid": userorgid, + + + "id": params_kw.id + } + recs = await sor.R('user_llm_policy', 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" + } + } + + tblfields = json.loads('[{"name":"id","title":"id","type":"str","length":32},{"name":"orgid","title":"所属机构","type":"str","length":32},{"name":"userid","title":"用户","type":"str","length":32},{"name":"llmid","title":"模型","type":"str","length":32,"nullable":true},{"name":"daily_call_limit","title":"日调用上限","type":"int","nullable":true},{"name":"monthly_call_limit","title":"月调用上限","type":"int","nullable":true},{"name":"spending_alert","title":"花费预警","type":"float","length":18,"dec":2,"nullable":true},{"name":"spending_ceiling","title":"费用上限","type":"float","length":18,"dec":2,"nullable":true},{"name":"max_concurrency","title":"并发上限","type":"int","nullable":true},{"name":"status","title":"状态","type":"str","length":10,"default":"active"},{"name":"created_at","title":"创建时间","type":"timestamp","nullable":true},{"name":"updated_at","title":"更新时间","type":"timestamp","nullable":true},{"name":"created_by","title":"创建人","type":"str","length":32,"nullable":true},{"name":"remark","title":"备注","type":"str","length":500,"nullable":true}]') + tblfname = [ f['name'] for f in tblfields ] + other_keys = [ k for k in ns.keys() if k in tblfname and k != 'id' ] + if len(other_keys) > 0: + r = await sor.U('user_llm_policy', 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