Compare commits
No commits in common. "76622381be636cbaeab8028cf200629655123970" and "70e8fd791fa7b08af080121088d273aa08933d16" have entirely different histories.
76622381be
...
70e8fd791f
@ -48,8 +48,8 @@
|
|||||||
"subtables":[
|
"subtables":[
|
||||||
{
|
{
|
||||||
"field":"llmid",
|
"field":"llmid",
|
||||||
"title":"能力映射",
|
"title":"模型类型",
|
||||||
"subtable":"llm_api_map"
|
"subtable":"llm_catelog_rel"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,27 +1,17 @@
|
|||||||
{
|
{
|
||||||
"tblname": "llm_api_map",
|
"summary": [{"name": "llm_api_map", "title": "模型能力映射表", "primary": ["id"], "catelog": "relation"}],
|
||||||
"title": "模型能力映射",
|
"fields": [
|
||||||
"params": {
|
{"name": "id", "title": "主键", "type": "str", "length": 32, "nullable": "no"},
|
||||||
"browserfields": {
|
{"name": "llmid", "title": "模型ID", "type": "str", "length": 32, "nullable": "no"},
|
||||||
"exclouded": ["id", "llmid"],
|
{"name": "llmcatelogid", "title": "类目ID", "type": "str", "length": 32, "nullable": "no"},
|
||||||
"alters": {
|
{"name": "apiname", "title": "API接口名", "type": "str", "length": 100, "nullable": "no"},
|
||||||
"llmcatelogid": {
|
{"name": "query_apiname", "title": "结果查询API名", "type": "str", "length": 100},
|
||||||
"dataurl": "{{entire_url('../api/llm_catelog_options.dspy')}}",
|
{"name": "query_period", "title": "查询间隔(秒)", "type": "int"},
|
||||||
"textField": "name",
|
{"name": "ppid", "title": "定价项目ID", "type": "str", "length": 32}
|
||||||
"valueField": "id"
|
],
|
||||||
},
|
"indexes": [
|
||||||
"apiname": {
|
{"name": "idx_llm_api_llm", "idxtype": "index", "idxfields": ["llmid"]},
|
||||||
"dataurl": "{{entire_url('../api/uapi_options.dspy')}}",
|
{"name": "idx_llm_api_catelog", "idxtype": "unique", "idxfields": ["llmid", "llmcatelogid"]}
|
||||||
"textField": "name",
|
],
|
||||||
"valueField": "name"
|
"codes": []
|
||||||
},
|
|
||||||
"ppid": {
|
|
||||||
"dataurl": "{{entire_url('/pricing/get_all_pricing_programs.dspy')}}",
|
|
||||||
"textField": "name",
|
|
||||||
"valueField": "id"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"editexclouded": ["id", "llmid"]
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -36,7 +36,7 @@ async def llm_charging(ppid, llmusage):
|
|||||||
'cost': cost
|
'cost': cost
|
||||||
})
|
})
|
||||||
|
|
||||||
async def checkCustomerBalance(llmid, userid, userorgid):
|
async def checkCustomerBalance(llmid, userorgid):
|
||||||
if llmid is None:
|
if llmid is None:
|
||||||
debug(f'checkCustomerBalance(): llmid is None')
|
debug(f'checkCustomerBalance(): llmid is None')
|
||||||
return False
|
return False
|
||||||
@ -51,12 +51,7 @@ async def checkCustomerBalance(llmid, userid, userorgid):
|
|||||||
if llm.ownerid == userorgid:
|
if llm.ownerid == userorgid:
|
||||||
debug(f'self orgid user')
|
debug(f'self orgid user')
|
||||||
return True
|
return True
|
||||||
apikey = await get_user_tpac_apikey(userid)
|
balance = await getCustomerBalance(sor, userorgid)
|
||||||
balance = 0.00
|
|
||||||
if apikey:
|
|
||||||
balance = await get_tpac_balance(apikey, userid)
|
|
||||||
else:
|
|
||||||
balance = await getCustomerBalance(sor, userorgid)
|
|
||||||
bal = 0 if balance is None else balance
|
bal = 0 if balance is None else balance
|
||||||
if llm.min_balance is None:
|
if llm.min_balance is None:
|
||||||
llm.min_balance = 0.00
|
llm.min_balance = 0.00
|
||||||
@ -234,11 +229,7 @@ async def backend_accounting():
|
|||||||
for lu in lus:
|
for lu in lus:
|
||||||
try:
|
try:
|
||||||
debug(f'backend_accounting(): {lu.id=} handleing...')
|
debug(f'backend_accounting(): {lu.id=} handleing...')
|
||||||
apikey = await get_user_tpac_apikey(lu.userid)
|
await llm_accounting(lu)
|
||||||
if apikey:
|
|
||||||
await tpac_accounting(apikey, lu.userid, lu.llmid, lu.amount, lu.usages)
|
|
||||||
else:
|
|
||||||
await llm_accounting(lu)
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
exception(f'{e}, {lu.id=}')
|
exception(f'{e}, {lu.id=}')
|
||||||
await llm_accoung_failed(lu.id)
|
await llm_accoung_failed(lu.id)
|
||||||
|
|||||||
@ -15,9 +15,6 @@ from .utils import (
|
|||||||
get_llmcatelogs,
|
get_llmcatelogs,
|
||||||
get_llms_by_catelog_to_customer,
|
get_llms_by_catelog_to_customer,
|
||||||
get_llmproviders,
|
get_llmproviders,
|
||||||
tpac_accounting,
|
|
||||||
get_tpac_balance,
|
|
||||||
get_user_tpac_apikey,
|
|
||||||
get_llm,
|
get_llm,
|
||||||
BufferedLLMs
|
BufferedLLMs
|
||||||
)
|
)
|
||||||
@ -78,9 +75,6 @@ def load_llmage():
|
|||||||
env.get_llmcatelogs = get_llmcatelogs
|
env.get_llmcatelogs = get_llmcatelogs
|
||||||
env.checkCustomerBalance = checkCustomerBalance
|
env.checkCustomerBalance = checkCustomerBalance
|
||||||
env.get_llmproviders = get_llmproviders
|
env.get_llmproviders = get_llmproviders
|
||||||
env.get_user_tpac_apikey = get_user_tpac_apikey
|
|
||||||
env.get_tpac_balance = get_tpac_balance
|
|
||||||
env.tpac_accounting = tpac_accounting
|
|
||||||
env.get_llms_sort_by_provider = get_llms_sort_by_provider
|
env.get_llms_sort_by_provider = get_llms_sort_by_provider
|
||||||
env.keling_token = keling_token
|
env.keling_token = keling_token
|
||||||
env.llm_query_price = llm_query_price
|
env.llm_query_price = llm_query_price
|
||||||
|
|||||||
@ -13,65 +13,6 @@ from appPublic.timeUtils import curDateString, timestampstr
|
|||||||
from uapi.appapi import UAPI, sor_get_callerid, sor_get_uapi
|
from uapi.appapi import UAPI, sor_get_callerid, sor_get_uapi
|
||||||
from ahserver.serverenv import get_serverenv, ServerEnv
|
from ahserver.serverenv import get_serverenv, ServerEnv
|
||||||
from ahserver.filestorage import FileStorage
|
from ahserver.filestorage import FileStorage
|
||||||
from appPublic.jsonConfig import getConfig
|
|
||||||
from appPublic.streamhttpclient import StreamHttpClient
|
|
||||||
|
|
||||||
async def get_user_tpac_apikey(userid):
|
|
||||||
env = ServerEnv()
|
|
||||||
config = getConfig()
|
|
||||||
if not config.tpac:
|
|
||||||
return None
|
|
||||||
apikey = await env.get_user_dapp_apikey(config.tpac.dappid, userid)
|
|
||||||
if apikey is None:
|
|
||||||
return None
|
|
||||||
return apikey
|
|
||||||
|
|
||||||
async def get_tpac_balance(apikey, userid):
|
|
||||||
config = getConfig()
|
|
||||||
if apikey is None:
|
|
||||||
return None
|
|
||||||
|
|
||||||
url = config.tpac.get_user_balance_url
|
|
||||||
hc = StreamHttpClient()
|
|
||||||
try:
|
|
||||||
b = hc.request('GET', url, params={"apikey": apikey, 'userid': userid})
|
|
||||||
if b:
|
|
||||||
d = json.loads(b.decode('utf-8'))
|
|
||||||
if d['status'] == 'ok':
|
|
||||||
return d['balance']
|
|
||||||
exception(f'{url=}, {userid=}, {apikey=}, error')
|
|
||||||
return None
|
|
||||||
except Exception as e:
|
|
||||||
exception(f'{url=}, {userid=}, {apikey=}, error:{e}')
|
|
||||||
return None
|
|
||||||
|
|
||||||
async def tpac_accounting(apikey, userid, llmid, amount, usage):
|
|
||||||
if apikey is None:
|
|
||||||
return
|
|
||||||
config = getConfig()
|
|
||||||
url = config.tpac.accounting_url
|
|
||||||
d = {
|
|
||||||
'apikey': apikey,
|
|
||||||
'userid': userid,
|
|
||||||
'llmid': llmid,
|
|
||||||
'amount': amount,
|
|
||||||
'usage': usage
|
|
||||||
}
|
|
||||||
url = config.thirdparty_accounting_center.get_user_balance_url
|
|
||||||
hc = StreamHttpClient()
|
|
||||||
try:
|
|
||||||
b = hc.request('POST', url, data=d):
|
|
||||||
d = json.loads(b.decode('utf-8'))
|
|
||||||
if d['status'] == 'ok':
|
|
||||||
env = ServerEnv()
|
|
||||||
async with get_sor_context(env, 'llmage') as sor:
|
|
||||||
await sor.U('llmusage', {'id': llmid, 'tpac_accounting_status': 'accounted')
|
|
||||||
return
|
|
||||||
exception(f'{apikey=}, {userid=}, {llmid=}, {amount=}, {usage=} tpac accounting error')
|
|
||||||
return
|
|
||||||
except Exception as e:
|
|
||||||
exception(f'{apikey=}, {userid=}, {llmid=}, {amount=}, {usage=} tpac accounting error:{e}')
|
|
||||||
return
|
|
||||||
|
|
||||||
async def append_new_llmoutput(webpath, output):
|
async def append_new_llmoutput(webpath, output):
|
||||||
fs = FileStorage()
|
fs = FileStorage()
|
||||||
|
|||||||
135
models/llm.json
135
models/llm.json
@ -1,135 +0,0 @@
|
|||||||
{
|
|
||||||
"summary": [
|
|
||||||
{
|
|
||||||
"name": "llm",
|
|
||||||
"title": "大语言模型",
|
|
||||||
"primary": [
|
|
||||||
"id"
|
|
||||||
]
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"fields": [
|
|
||||||
{
|
|
||||||
"name": "id",
|
|
||||||
"title": "id",
|
|
||||||
"type": "str",
|
|
||||||
"length": 32
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "name",
|
|
||||||
"title": "名称",
|
|
||||||
"type": "str",
|
|
||||||
"length": 100
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "model",
|
|
||||||
"title": "识别名",
|
|
||||||
"type": "str",
|
|
||||||
"length": 100
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "description",
|
|
||||||
"title": "说明",
|
|
||||||
"type": "text",
|
|
||||||
"length": 500
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "iconid",
|
|
||||||
"title": "图标id",
|
|
||||||
"type": "str",
|
|
||||||
"length": 32
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "upappid",
|
|
||||||
"title": "上位系统id",
|
|
||||||
"type": "str",
|
|
||||||
"length": 32
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "apiname",
|
|
||||||
"title": "接口名称",
|
|
||||||
"type": "str",
|
|
||||||
"length": 100
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "providerid",
|
|
||||||
"title": "供应商id",
|
|
||||||
"type": "str",
|
|
||||||
"length": 32
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "ownerid",
|
|
||||||
"title": "所属机构id",
|
|
||||||
"type": "str",
|
|
||||||
"length": 32
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "enabled_date",
|
|
||||||
"title": "启用日期",
|
|
||||||
"type": "date"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "expired_date",
|
|
||||||
"title": "失效日期",
|
|
||||||
"type": "date"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "query_apiname",
|
|
||||||
"title": "结果查询接口名称",
|
|
||||||
"type": "str",
|
|
||||||
"length": 100
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "query_period",
|
|
||||||
"title": "查询间隔",
|
|
||||||
"type": "long",
|
|
||||||
"default": 30
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "ppid",
|
|
||||||
"title": "计费项目id",
|
|
||||||
"type": "str",
|
|
||||||
"length": 32,
|
|
||||||
"nullable": "yes"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "min_balance",
|
|
||||||
"title": "最低余额",
|
|
||||||
"type": "float",
|
|
||||||
"length": 20,
|
|
||||||
"default": 10
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"codes": [
|
|
||||||
{
|
|
||||||
"field": "providerid",
|
|
||||||
"table": "organization",
|
|
||||||
"valuefield": "id",
|
|
||||||
"textfield": "orgname"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"field": "iconid",
|
|
||||||
"table": "svgicon",
|
|
||||||
"valuefield": "id",
|
|
||||||
"textfield": "id"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"field": "upappid",
|
|
||||||
"table": "upapp",
|
|
||||||
"valuefield": "id",
|
|
||||||
"textfield": "name"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"field": "ownerid",
|
|
||||||
"table": "organization",
|
|
||||||
"valuefield": "id",
|
|
||||||
"textfield": "orgname"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"field": "ppid",
|
|
||||||
"table": "pricing_program",
|
|
||||||
"valuefield": "id",
|
|
||||||
"textfield": "name"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
@ -1,99 +0,0 @@
|
|||||||
{
|
|
||||||
"table_name": "llm_api_map",
|
|
||||||
"summary": [
|
|
||||||
{
|
|
||||||
"name": "llm_api_map",
|
|
||||||
"title": "模型API映射表",
|
|
||||||
"primary": "id",
|
|
||||||
"catelog": "relation"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"fields": [
|
|
||||||
{
|
|
||||||
"name": "id",
|
|
||||||
"type": "varchar(32)",
|
|
||||||
"not_null": true,
|
|
||||||
"comment": "主键ID"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "llmid",
|
|
||||||
"type": "varchar(32)",
|
|
||||||
"not_null": true,
|
|
||||||
"comment": "模型ID,关联llm表"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "llmcatelogid",
|
|
||||||
"type": "varchar(32)",
|
|
||||||
"not_null": true,
|
|
||||||
"comment": "模型分类ID,关联llmcatelog表"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "apiname",
|
|
||||||
"type": "varchar(100)",
|
|
||||||
"not_null": true,
|
|
||||||
"comment": "推理接口名称,关联uapi表name字段"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "query_apiname",
|
|
||||||
"type": "varchar(100)",
|
|
||||||
"comment": "异步任务结果查询接口名称,可逗号分隔多个"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "query_period",
|
|
||||||
"type": "bigint",
|
|
||||||
"default": 30,
|
|
||||||
"comment": "异步任务查询轮询间隔(秒),默认30"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "ppid",
|
|
||||||
"type": "varchar(32)",
|
|
||||||
"comment": "计费程序ID,关联pricing_program表"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"indexes": [
|
|
||||||
{
|
|
||||||
"name": "idx_api_map_llmid",
|
|
||||||
"type": "normal",
|
|
||||||
"idxfields": ["llmid"],
|
|
||||||
"idxtype": "index"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "idx_api_map_catelog",
|
|
||||||
"type": "normal",
|
|
||||||
"idxfields": ["llmcatelogid"],
|
|
||||||
"idxtype": "index"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "idx_api_map_apiname",
|
|
||||||
"type": "normal",
|
|
||||||
"idxfields": ["apiname"],
|
|
||||||
"idxtype": "index"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "uk_llmid_apiname",
|
|
||||||
"type": "unique",
|
|
||||||
"idxfields": ["llmid", "apiname"],
|
|
||||||
"idxtype": "unique"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"codes": [
|
|
||||||
{
|
|
||||||
"field": "llmid",
|
|
||||||
"table": "llm",
|
|
||||||
"valuefield": "id",
|
|
||||||
"textfield": "name"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"field": "llmcatelogid",
|
|
||||||
"table": "llmcatelog",
|
|
||||||
"valuefield": "id",
|
|
||||||
"textfield": "name"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"field": "ppid",
|
|
||||||
"table": "pricing_program",
|
|
||||||
"valuefield": "id",
|
|
||||||
"textfield": "name"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
@ -1,56 +0,0 @@
|
|||||||
{
|
|
||||||
"summary": [
|
|
||||||
{
|
|
||||||
"name": "llm_catelog_rel",
|
|
||||||
"title": "模型分类对照表",
|
|
||||||
"primary": [
|
|
||||||
"id"
|
|
||||||
],
|
|
||||||
"catelog": "relation"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"fields": [
|
|
||||||
{
|
|
||||||
"name": "id",
|
|
||||||
"title": "id",
|
|
||||||
"type": "str",
|
|
||||||
"length": 32
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "llmid",
|
|
||||||
"title": "模型id",
|
|
||||||
"type": "str",
|
|
||||||
"length": 32
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "llmcatelogid",
|
|
||||||
"title": "模型分类id",
|
|
||||||
"type": "str",
|
|
||||||
"length": 32
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"indexes": [
|
|
||||||
{
|
|
||||||
"name": "idx_uniue_llm_catelogid",
|
|
||||||
"idxtype": "unique",
|
|
||||||
"idxfields": [
|
|
||||||
"llmid",
|
|
||||||
"llmcatelogid"
|
|
||||||
]
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"codes": [
|
|
||||||
{
|
|
||||||
"field": "llmid",
|
|
||||||
"table": "llm",
|
|
||||||
"valuefield": "id",
|
|
||||||
"textfield": "name"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"field": "llmcatelogid",
|
|
||||||
"table": "llmcatelog",
|
|
||||||
"valuefield": "id",
|
|
||||||
"textfield": "name"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
@ -1,30 +0,0 @@
|
|||||||
{
|
|
||||||
"summary": [
|
|
||||||
{
|
|
||||||
"name": "llmcatelog",
|
|
||||||
"title": "模型类目",
|
|
||||||
"primary": [
|
|
||||||
"id"
|
|
||||||
]
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"fields": [
|
|
||||||
{
|
|
||||||
"name": "id",
|
|
||||||
"title": "id",
|
|
||||||
"type": "str",
|
|
||||||
"length": 32
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "name",
|
|
||||||
"title": "类型名",
|
|
||||||
"type": "str",
|
|
||||||
"length": 100
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "description",
|
|
||||||
"title": "类型说明",
|
|
||||||
"type": "text"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
@ -1,128 +0,0 @@
|
|||||||
{
|
|
||||||
"summary": [
|
|
||||||
{
|
|
||||||
"name": "llmusage",
|
|
||||||
"title": "模型使用",
|
|
||||||
"primary": [
|
|
||||||
"id"
|
|
||||||
],
|
|
||||||
"catelog": "entity"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"fields": [
|
|
||||||
{
|
|
||||||
"name": "id",
|
|
||||||
"title": "id",
|
|
||||||
"type": "str",
|
|
||||||
"length": 32
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "llmid",
|
|
||||||
"title": "模型id",
|
|
||||||
"type": "str",
|
|
||||||
"length": 32
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "use_date",
|
|
||||||
"title": "使用日期",
|
|
||||||
"type": "date"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "use_time",
|
|
||||||
"title": "使用时间",
|
|
||||||
"type": "timestamp"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "userid",
|
|
||||||
"title": "用户id",
|
|
||||||
"type": "str",
|
|
||||||
"length": 32
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "usages",
|
|
||||||
"title": "使用信息",
|
|
||||||
"type": "text"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "ioinfo",
|
|
||||||
"title": "交互内容",
|
|
||||||
"type": "text"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "transno",
|
|
||||||
"title": "交易号",
|
|
||||||
"type": "str",
|
|
||||||
"length": 32
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "responsed_seconds",
|
|
||||||
"title": "响应时间",
|
|
||||||
"type": "float",
|
|
||||||
"length": 18
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "finish_seconds",
|
|
||||||
"title": "结束时间",
|
|
||||||
"type": "float",
|
|
||||||
"length": 18
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "status",
|
|
||||||
"title": "状态",
|
|
||||||
"type": "str",
|
|
||||||
"length": 32
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "taskid",
|
|
||||||
"title": "任务号",
|
|
||||||
"type": "str",
|
|
||||||
"length": 256
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "amount",
|
|
||||||
"title": "交易金额",
|
|
||||||
"type": "float",
|
|
||||||
"length": 18
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "cost",
|
|
||||||
"title": "交易成本",
|
|
||||||
"type": "float",
|
|
||||||
"length": 18
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "userorgid",
|
|
||||||
"title": "用户机构id",
|
|
||||||
"type": "str",
|
|
||||||
"length": 32
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "ownerid",
|
|
||||||
"title": "模型机构id",
|
|
||||||
"type": "str",
|
|
||||||
"length": 32
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "accounting_status",
|
|
||||||
"title": "记账状态",
|
|
||||||
"type": "str",
|
|
||||||
"length": 32
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"indexes": [
|
|
||||||
{
|
|
||||||
"name": "idx1",
|
|
||||||
"idxtype": "unique",
|
|
||||||
"idxfields": [
|
|
||||||
"taskid"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "idx2",
|
|
||||||
"idxtype": "index",
|
|
||||||
"idxfields": [
|
|
||||||
"userid"
|
|
||||||
]
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
@ -1,153 +0,0 @@
|
|||||||
#!/bin/bash
|
|
||||||
# deploy_llmage.sh
|
|
||||||
# 一键部署 llmage 模块的 llm_api_map 变更
|
|
||||||
#
|
|
||||||
# 包含:代码拉取 -> 构建 -> 数据库迁移 -> 权限设置 -> 重启
|
|
||||||
#
|
|
||||||
# 用法:
|
|
||||||
# bash deploy_llmage.sh # 完整部署
|
|
||||||
# bash deploy_llmage.sh --dry-run # 预览,不执行变更
|
|
||||||
# bash deploy_llmage.sh --skip-db # 跳过数据库迁移
|
|
||||||
# bash deploy_llmage.sh --skip-perm # 跳过权限设置
|
|
||||||
|
|
||||||
set -e
|
|
||||||
|
|
||||||
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
|
|
||||||
LLMAGE_DIR="$(cd "$SCRIPT_DIR/.." && pwd)"
|
|
||||||
SAGE_DIR="$(cd "$LLMAGE_DIR/../sage" && pwd 2>/dev/null || echo "")"
|
|
||||||
|
|
||||||
# 参数解析
|
|
||||||
DRY_RUN=false
|
|
||||||
SKIP_DB=false
|
|
||||||
SKIP_PERM=false
|
|
||||||
|
|
||||||
for arg in "$@"; do
|
|
||||||
case $arg in
|
|
||||||
--dry-run) DRY_RUN=true ;;
|
|
||||||
--skip-db) SKIP_DB=true ;;
|
|
||||||
--skip-perm) SKIP_PERM=true ;;
|
|
||||||
-h|--help)
|
|
||||||
echo "用法: bash deploy_llmage.sh [options]"
|
|
||||||
echo " --dry-run 预览模式,不执行变更"
|
|
||||||
echo " --skip-db 跳过数据库迁移"
|
|
||||||
echo " --skip-perm 跳过权限设置"
|
|
||||||
echo " -h, --help 显示帮助"
|
|
||||||
exit 0
|
|
||||||
;;
|
|
||||||
esac
|
|
||||||
done
|
|
||||||
|
|
||||||
echo "============================================"
|
|
||||||
echo " llmage 模块部署: llm_api_map"
|
|
||||||
echo "============================================"
|
|
||||||
echo "LLMAGE_DIR: $LLMAGE_DIR"
|
|
||||||
echo "SAGE_DIR: $SAGE_DIR"
|
|
||||||
echo "Dry run: $DRY_RUN"
|
|
||||||
echo "Skip DB: $SKIP_DB"
|
|
||||||
echo "Skip Perm: $SKIP_PERM"
|
|
||||||
echo ""
|
|
||||||
|
|
||||||
# =============================================
|
|
||||||
# Step 1: 拉取最新代码
|
|
||||||
# =============================================
|
|
||||||
echo ">>> [1/5] 拉取最新代码..."
|
|
||||||
cd "$LLMAGE_DIR"
|
|
||||||
|
|
||||||
if $DRY_RUN; then
|
|
||||||
echo " [DRY] git pull origin main"
|
|
||||||
else
|
|
||||||
git pull origin main
|
|
||||||
echo " Done"
|
|
||||||
fi
|
|
||||||
|
|
||||||
# =============================================
|
|
||||||
# Step 2: 构建模块(生成 UI/DDL)
|
|
||||||
# =============================================
|
|
||||||
echo ""
|
|
||||||
echo ">>> [2/5] 构建模块..."
|
|
||||||
cd "$LLMAGE_DIR/json"
|
|
||||||
|
|
||||||
if $DRY_RUN; then
|
|
||||||
echo " [DRY] bash build.sh"
|
|
||||||
else
|
|
||||||
bash build.sh
|
|
||||||
echo " Done"
|
|
||||||
fi
|
|
||||||
|
|
||||||
# =============================================
|
|
||||||
# Step 3: 数据库迁移
|
|
||||||
# =============================================
|
|
||||||
if ! $SKIP_DB; then
|
|
||||||
echo ""
|
|
||||||
echo ">>> [3/5] 数据库迁移..."
|
|
||||||
|
|
||||||
# 先在 Sage 虚拟环境中 dry-run 验证
|
|
||||||
cd "$SAGE_DIR"
|
|
||||||
if $DRY_RUN; then
|
|
||||||
echo " [DRY] python $LLMAGE_DIR/scripts/migrate_llm_api_map_db.py --dry-run"
|
|
||||||
python "$LLMAGE_DIR/scripts/migrate_llm_api_map_db.py" --dry-run
|
|
||||||
else
|
|
||||||
echo " 执行 dry-run 预览..."
|
|
||||||
python "$LLMAGE_DIR/scripts/migrate_llm_api_map_db.py" --dry-run
|
|
||||||
echo ""
|
|
||||||
echo " 确认以上预览无误后,执行实际迁移..."
|
|
||||||
python "$LLMAGE_DIR/scripts/migrate_llm_api_map_db.py"
|
|
||||||
fi
|
|
||||||
echo " Done"
|
|
||||||
else
|
|
||||||
echo ""
|
|
||||||
echo ">>> [3/5] 跳过数据库迁移"
|
|
||||||
fi
|
|
||||||
|
|
||||||
# =============================================
|
|
||||||
# Step 4: 权限设置
|
|
||||||
# =============================================
|
|
||||||
if ! $SKIP_PERM; then
|
|
||||||
echo ""
|
|
||||||
echo ">>> [4/5] 设置 RBAC 权限..."
|
|
||||||
|
|
||||||
cd "$SAGE_DIR"
|
|
||||||
if $DRY_RUN; then
|
|
||||||
echo " [DRY] bash $LLMAGE_DIR/scripts/setup_llmage_perms.sh"
|
|
||||||
else
|
|
||||||
bash "$LLMAGE_DIR/scripts/setup_llmage_perms.sh"
|
|
||||||
fi
|
|
||||||
echo " Done"
|
|
||||||
else
|
|
||||||
echo ""
|
|
||||||
echo ">>> [4/5] 跳过权限设置"
|
|
||||||
fi
|
|
||||||
|
|
||||||
# =============================================
|
|
||||||
# Step 5: 重启服务
|
|
||||||
# =============================================
|
|
||||||
echo ""
|
|
||||||
echo ">>> [5/5] 重启服务..."
|
|
||||||
|
|
||||||
if $DRY_RUN; then
|
|
||||||
echo " [DRY] 请手动重启 Sage 服务以生效新代码"
|
|
||||||
echo " 示例: systemctl restart sage 或 supervisorctl restart sage"
|
|
||||||
else
|
|
||||||
echo " 请手动重启 Sage 服务以使变更生效:"
|
|
||||||
echo " systemctl restart sage"
|
|
||||||
echo " 或:"
|
|
||||||
echo " supervisorctl restart sage"
|
|
||||||
fi
|
|
||||||
|
|
||||||
# =============================================
|
|
||||||
# 完成
|
|
||||||
# =============================================
|
|
||||||
echo ""
|
|
||||||
echo "============================================"
|
|
||||||
echo " 部署完成"
|
|
||||||
echo "============================================"
|
|
||||||
echo ""
|
|
||||||
echo "验证清单:"
|
|
||||||
echo " 1. 检查 llm_api_map 表是否创建成功"
|
|
||||||
echo " 2. 访问 /llmage/llm_api_map_manage.ui 确认页面可访问"
|
|
||||||
echo " 3. 确认有权限的用户可以正常操作"
|
|
||||||
echo " 4. 确认无权限的用户返回 401"
|
|
||||||
echo ""
|
|
||||||
echo "如需回滚旧 llm 表字段(已删除的话):"
|
|
||||||
echo " 请从 git 历史恢复或手动添加 apiname/query_apiname/query_period/ppid 字段"
|
|
||||||
echo ""
|
|
||||||
@ -1,264 +0,0 @@
|
|||||||
#!/usr/bin/env python3
|
|
||||||
"""
|
|
||||||
llm_api_map 数据库迁移脚本
|
|
||||||
直接操作数据库,完成以下任务:
|
|
||||||
1. 创建 llm_api_map 表(如不存在)
|
|
||||||
2. 从 llm 表迁移 apiname/query_apiname/query_period/ppid 到 llm_api_map
|
|
||||||
3. 关联 llm_catalog_rel 获取 llmcatelogid
|
|
||||||
4. 可选:删除 llm 表中的旧字段(需用户确认)
|
|
||||||
|
|
||||||
运行位置:Sage 虚拟环境
|
|
||||||
用法:python scripts/migrate_llm_api_map_db.py [--dry-run] [--drop-old]
|
|
||||||
"""
|
|
||||||
import sys
|
|
||||||
import os
|
|
||||||
import asyncio
|
|
||||||
import argparse
|
|
||||||
|
|
||||||
# 确保 Sage 虚拟环境的包可用
|
|
||||||
sage_root = os.path.expanduser('~/repos/sage')
|
|
||||||
sys.path.insert(0, sage_root)
|
|
||||||
sys.path.insert(0, os.path.join(sage_root, 'py3/lib/python3.10/site-packages'))
|
|
||||||
|
|
||||||
from appPublic.jsonConfig import getConfig
|
|
||||||
from appPublic.uniqueID import getID
|
|
||||||
from sqlor.dbpools import DBPools
|
|
||||||
|
|
||||||
|
|
||||||
def print_sql(sql, label=""):
|
|
||||||
if label:
|
|
||||||
print(f" [{label}] {sql}")
|
|
||||||
else:
|
|
||||||
print(f" {sql}")
|
|
||||||
|
|
||||||
|
|
||||||
async def migrate(dry_run=False, drop_old=False):
|
|
||||||
"""Execute migration."""
|
|
||||||
config = getConfig(sage_root)
|
|
||||||
db = DBPools(config.databases)
|
|
||||||
dbname = list(config.databases.keys())[0]
|
|
||||||
|
|
||||||
print(f"Using database: {dbname}")
|
|
||||||
print(f"Dry run: {dry_run}")
|
|
||||||
print(f"Drop old columns: {drop_old}")
|
|
||||||
print()
|
|
||||||
|
|
||||||
async with db.sqlorContext(dbname) as sor:
|
|
||||||
# =============================================
|
|
||||||
# Step 1: Check if llm_api_map already has data
|
|
||||||
# =============================================
|
|
||||||
existing = await sor.sqlExe("SELECT COUNT(*) as cnt FROM llm_api_map", {})
|
|
||||||
existing_count = existing[0]['cnt'] if existing else 0
|
|
||||||
|
|
||||||
if existing_count > 0:
|
|
||||||
print(f"[WARNING] llm_api_map already has {existing_count} records.")
|
|
||||||
resp = input("Continue anyway? (y/N): ")
|
|
||||||
if resp.lower() != 'y':
|
|
||||||
print("Aborted.")
|
|
||||||
return False
|
|
||||||
|
|
||||||
# =============================================
|
|
||||||
# Step 2: Create llm_api_map table
|
|
||||||
# =============================================
|
|
||||||
print("[Step 1] Creating llm_api_map table...")
|
|
||||||
|
|
||||||
create_sql = """
|
|
||||||
CREATE TABLE IF NOT EXISTS llm_api_map (
|
|
||||||
id VARCHAR(32) NOT NULL PRIMARY KEY,
|
|
||||||
llmid VARCHAR(32) NOT NULL,
|
|
||||||
llmcatelogid VARCHAR(32) NOT NULL,
|
|
||||||
apiname VARCHAR(100) NOT NULL,
|
|
||||||
query_apiname VARCHAR(100),
|
|
||||||
query_period INT,
|
|
||||||
ppid VARCHAR(32)
|
|
||||||
)
|
|
||||||
"""
|
|
||||||
if dry_run:
|
|
||||||
print_sql(create_sql.strip(), "SQL")
|
|
||||||
else:
|
|
||||||
try:
|
|
||||||
await sor.sqlExe(create_sql, {})
|
|
||||||
print(" Table created (or already exists)")
|
|
||||||
except Exception as e:
|
|
||||||
if 'already exists' in str(e).lower() or 'Duplicate' in str(e):
|
|
||||||
print(" Table already exists, continuing")
|
|
||||||
else:
|
|
||||||
print(f" ERROR: {e}")
|
|
||||||
return False
|
|
||||||
|
|
||||||
# Create indexes
|
|
||||||
indexes = [
|
|
||||||
("CREATE INDEX idx_api_map_llm ON llm_api_map (llmid)", "index llmid"),
|
|
||||||
("CREATE INDEX idx_api_map_catelog ON llm_api_map (llmcatelogid)", "index catelog"),
|
|
||||||
("CREATE INDEX idx_api_map_apiname ON llm_api_map (apiname)", "index apiname"),
|
|
||||||
("CREATE UNIQUE INDEX uk_llmid_apiname ON llm_api_map (llmid, apiname)", "unique (llmid, apiname)"),
|
|
||||||
]
|
|
||||||
|
|
||||||
for sql, label in indexes:
|
|
||||||
if dry_run:
|
|
||||||
print_sql(sql, label)
|
|
||||||
else:
|
|
||||||
try:
|
|
||||||
await sor.sqlExe(sql, {})
|
|
||||||
print(f" Index created: {label}")
|
|
||||||
except Exception as e:
|
|
||||||
if 'already exists' in str(e).lower() or 'Duplicate' in str(e):
|
|
||||||
print(f" Index already exists: {label}")
|
|
||||||
else:
|
|
||||||
print(f" WARNING creating {label}: {e}")
|
|
||||||
|
|
||||||
# =============================================
|
|
||||||
# Step 3: Migrate data from llm -> llm_api_map
|
|
||||||
# =============================================
|
|
||||||
print("\n[Step 2] Migrating data...")
|
|
||||||
|
|
||||||
# Get all llm records that have apiname
|
|
||||||
llms = await sor.sqlExe("""
|
|
||||||
SELECT id, name, apiname, query_apiname, query_period, ppid
|
|
||||||
FROM llm
|
|
||||||
WHERE apiname IS NOT NULL AND apiname != ''
|
|
||||||
""", {})
|
|
||||||
|
|
||||||
print(f" Found {len(llms or [])} llm records with apiname")
|
|
||||||
|
|
||||||
if not llms:
|
|
||||||
print(" No data to migrate. Done.")
|
|
||||||
return True
|
|
||||||
|
|
||||||
# Build catalog_rel lookup
|
|
||||||
rels = await sor.sqlExe("SELECT llmid, llmcatelogid FROM llm_catalog_rel", {})
|
|
||||||
catelog_map = {}
|
|
||||||
for r in (rels or []):
|
|
||||||
catelog_map.setdefault(r['llmid'], []).append(r['llmcatelogid'])
|
|
||||||
|
|
||||||
migrated = 0
|
|
||||||
skipped = 0
|
|
||||||
errors = []
|
|
||||||
|
|
||||||
for llm in llms:
|
|
||||||
llmid = llm['id']
|
|
||||||
catelog_ids = catelog_map.get(llmid)
|
|
||||||
|
|
||||||
if not catelog_ids:
|
|
||||||
print(f" [SKIP] llm '{llm.get('name', llmid)}' has no catalog_rel entry")
|
|
||||||
skipped += 1
|
|
||||||
continue
|
|
||||||
|
|
||||||
for catelogid in catelog_ids:
|
|
||||||
if dry_run:
|
|
||||||
print(f" [DRY] Would insert: llm={llm.get('name', llmid)}, catelog={catelogid}, api={llm['apiname']}")
|
|
||||||
migrated += 1
|
|
||||||
continue
|
|
||||||
|
|
||||||
# Check if already exists
|
|
||||||
exists = await sor.sqlExe(
|
|
||||||
"SELECT id FROM llm_api_map WHERE llmid=${llmid}$ AND apiname=${apiname}$",
|
|
||||||
{'llmid': llmid, 'apiname': llm['apiname']}
|
|
||||||
)
|
|
||||||
if exists:
|
|
||||||
print(f" [SKIP] Already exists: {llm.get('name', llmid)} / {llm['apiname']}")
|
|
||||||
skipped += 1
|
|
||||||
continue
|
|
||||||
|
|
||||||
# Insert
|
|
||||||
data = {
|
|
||||||
'id': getID(),
|
|
||||||
'llmid': llmid,
|
|
||||||
'llmcatelogid': catelogid,
|
|
||||||
'apiname': llm['apiname'],
|
|
||||||
}
|
|
||||||
if llm.get('query_apiname'):
|
|
||||||
data['query_apiname'] = llm['query_apiname']
|
|
||||||
if llm.get('query_period') is not None:
|
|
||||||
data['query_period'] = int(llm['query_period']) if llm['query_period'] != '' else None
|
|
||||||
if llm.get('ppid'):
|
|
||||||
data['ppid'] = llm['ppid']
|
|
||||||
|
|
||||||
try:
|
|
||||||
await sor.C('llm_api_map', data)
|
|
||||||
migrated += 1
|
|
||||||
except Exception as e:
|
|
||||||
errors.append(f"{llm.get('name', llmid)}: {e}")
|
|
||||||
|
|
||||||
print(f"\n Migrated: {migrated}, Skipped: {skipped}")
|
|
||||||
if errors:
|
|
||||||
print(f" Errors: {len(errors)}")
|
|
||||||
for e in errors[:5]:
|
|
||||||
print(f" - {e}")
|
|
||||||
|
|
||||||
if dry_run:
|
|
||||||
print("\n (Dry run, no changes made)")
|
|
||||||
return True
|
|
||||||
|
|
||||||
# =============================================
|
|
||||||
# Step 4: Verify migration
|
|
||||||
# =============================================
|
|
||||||
print("\n[Step 3] Verifying migration...")
|
|
||||||
|
|
||||||
llm_api_count = await sor.sqlExe("SELECT COUNT(*) as cnt FROM llm_api_map", {})
|
|
||||||
api_count = llm_api_count[0]['cnt'] if llm_api_count else 0
|
|
||||||
print(f" llm_api_map total records: {api_count}")
|
|
||||||
|
|
||||||
# Check for llm records without corresponding llm_api_map
|
|
||||||
orphan_check = await sor.sqlExe("""
|
|
||||||
SELECT l.id, l.name
|
|
||||||
FROM llm l
|
|
||||||
WHERE l.apiname IS NOT NULL AND l.apiname != ''
|
|
||||||
AND NOT EXISTS (
|
|
||||||
SELECT 1 FROM llm_api_map m WHERE m.llmid = l.id
|
|
||||||
)
|
|
||||||
""", {})
|
|
||||||
|
|
||||||
if orphan_check:
|
|
||||||
print(f" [WARNING] {len(orphan_check)} llm records have no llm_api_map entry:")
|
|
||||||
for o in orphan_check[:5]:
|
|
||||||
print(f" - {o.get('name', o['id'])}")
|
|
||||||
if len(orphan_check) > 5:
|
|
||||||
print(f" ... and {len(orphan_check) - 5} more")
|
|
||||||
else:
|
|
||||||
print(" All llm records with apiname have corresponding llm_api_map entries")
|
|
||||||
|
|
||||||
# =============================================
|
|
||||||
# Step 5: Optional - drop old columns from llm
|
|
||||||
# =============================================
|
|
||||||
if drop_old:
|
|
||||||
print("\n[Step 4] Dropping old columns from llm table...")
|
|
||||||
|
|
||||||
old_columns = ['apiname', 'query_apiname', 'query_period', 'ppid']
|
|
||||||
for col in old_columns:
|
|
||||||
try:
|
|
||||||
await sor.sqlExe(f"ALTER TABLE llm DROP COLUMN {col}", {})
|
|
||||||
print(f" Dropped column: {col}")
|
|
||||||
except Exception as e:
|
|
||||||
if 'column' in str(e).lower() and ('not exist' in str(e).lower() or 'doesn\'t exist' in str(e).lower()):
|
|
||||||
print(f" Column already dropped: {col}")
|
|
||||||
else:
|
|
||||||
print(f" WARNING dropping {col}: {e}")
|
|
||||||
|
|
||||||
print(" Old columns removed from llm table")
|
|
||||||
print(" NOTE: Update code to use llm_api_map instead of llm columns")
|
|
||||||
|
|
||||||
print("\n[Done] Migration complete")
|
|
||||||
return True
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
|
||||||
parser = argparse.ArgumentParser(description='Migrate llm data to llm_api_map table')
|
|
||||||
parser.add_argument('--dry-run', action='store_true', help='Preview changes without executing')
|
|
||||||
parser.add_argument('--drop-old', action='store_true', help='Drop old columns from llm table after migration')
|
|
||||||
args = parser.parse_args()
|
|
||||||
|
|
||||||
if not args.dry_run:
|
|
||||||
print("=" * 60)
|
|
||||||
print(" llm_api_map Database Migration")
|
|
||||||
print("=" * 60)
|
|
||||||
resp = input("\nThis will modify the database. Continue? (y/N): ")
|
|
||||||
if resp.lower() != 'y':
|
|
||||||
print("Aborted.")
|
|
||||||
sys.exit(0)
|
|
||||||
print()
|
|
||||||
|
|
||||||
success = asyncio.get_event_loop().run_until_complete(
|
|
||||||
migrate(dry_run=args.dry_run, drop_old=args.drop_old)
|
|
||||||
)
|
|
||||||
sys.exit(0 if success else 1)
|
|
||||||
@ -1,66 +0,0 @@
|
|||||||
#!/bin/bash
|
|
||||||
# setup_llmage_perms.sh
|
|
||||||
# 为 llmage 模块的 llm_api_map 管理功能配置 RBAC 角色权限
|
|
||||||
#
|
|
||||||
# 授权角色:
|
|
||||||
# owner.superuser — 系统超管:全局所有模型配置
|
|
||||||
# *.admin — 机构管理员:管理本机构模型(通过ownerid隔离数据)
|
|
||||||
# reseller.operator — 运营:产品管理、模型配置
|
|
||||||
#
|
|
||||||
# 运行位置: sage 项目根目录 (包含 set_role_perm.py 的目录)
|
|
||||||
# 用法: bash setup_llmage_perms.sh
|
|
||||||
|
|
||||||
set -e
|
|
||||||
|
|
||||||
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
|
|
||||||
SAGE_DIR="$(cd "$SCRIPT_DIR/../.." && pwd 2>/dev/null || echo "")"
|
|
||||||
if [ ! -f "$SAGE_DIR/set_role_perm.py" ]; then
|
|
||||||
SAGE_DIR="$(cd "$SCRIPT_DIR/.." && pwd 2>/dev/null || echo "")"
|
|
||||||
fi
|
|
||||||
if [ ! -f "$SAGE_DIR/set_role_perm.py" ]; then
|
|
||||||
echo "Error: Cannot find set_role_perm.py"
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
cd "$SAGE_DIR"
|
|
||||||
|
|
||||||
COUNT=0
|
|
||||||
set_perm() {
|
|
||||||
local role="$1"
|
|
||||||
local path="$2"
|
|
||||||
python set_role_perm.py "${role}" "${path}"
|
|
||||||
COUNT=$((COUNT + 1))
|
|
||||||
}
|
|
||||||
|
|
||||||
# 授权角色(超管 + 各机构管理员 + 运营)
|
|
||||||
PERM_ROLES=(
|
|
||||||
"owner.superuser"
|
|
||||||
"owner.admin"
|
|
||||||
"reseller.admin"
|
|
||||||
"provider.admin"
|
|
||||||
"customer.admin"
|
|
||||||
"reseller.operator"
|
|
||||||
)
|
|
||||||
|
|
||||||
echo "============================================"
|
|
||||||
echo " llmage: llm_api_map 权限初始化"
|
|
||||||
echo "============================================"
|
|
||||||
|
|
||||||
LLM_API_MAP_PATHS=(
|
|
||||||
"/llmage/llm_api_map_manage.ui"
|
|
||||||
"/llmage/api/llm_api_map_list.dspy"
|
|
||||||
"/llmage/api/llm_api_map_create.dspy"
|
|
||||||
"/llmage/api/llm_api_map_delete.dspy"
|
|
||||||
"/llmage/api/llm_api_map_options.dspy"
|
|
||||||
"/llmage/api/uapi_options.dspy"
|
|
||||||
)
|
|
||||||
|
|
||||||
for p in "${LLM_API_MAP_PATHS[@]}"; do
|
|
||||||
for role in "${PERM_ROLES[@]}"; do
|
|
||||||
set_perm "${role}" "${p}"
|
|
||||||
done
|
|
||||||
done
|
|
||||||
|
|
||||||
echo ""
|
|
||||||
echo "============================================"
|
|
||||||
echo " 权限配置完成,共设置 ${COUNT} 条权限"
|
|
||||||
echo "============================================"
|
|
||||||
@ -1,63 +0,0 @@
|
|||||||
#!/usr/bin/env python3
|
|
||||||
import json
|
|
||||||
|
|
||||||
result = {'widgettype': 'Message', 'options': {'title': 'Error', 'message': 'Invalid', 'type': 'error'}}
|
|
||||||
|
|
||||||
try:
|
|
||||||
dbname = get_module_dbname('llmage')
|
|
||||||
llmid = params_kw.get('llmid', '')
|
|
||||||
catelogid = params_kw.get('llmcatelogid', '')
|
|
||||||
apiname = params_kw.get('apiname', '')
|
|
||||||
|
|
||||||
if not llmid or not catelogid or not apiname:
|
|
||||||
result['options'] = {'title': 'Error', 'message': '模型、分类和API接口为必填项', 'type': 'error'}
|
|
||||||
else:
|
|
||||||
user_orgid = await get_userorgid()
|
|
||||||
from appPublic.uniqueID import getID
|
|
||||||
new_id = getID()
|
|
||||||
|
|
||||||
async with DBPools().sqlorContext(dbname) as sor:
|
|
||||||
# 验证模型属于当前用户的机构
|
|
||||||
check_llm = await sor.sqlExe(
|
|
||||||
"select id from llm where id = ${llmid}$ and ownerid = ${ownerid}$",
|
|
||||||
{'llmid': llmid, 'ownerid': user_orgid}
|
|
||||||
)
|
|
||||||
if not check_llm:
|
|
||||||
result['options'] = {'title': 'Error', 'message': '无权操作该模型', 'type': 'error'}
|
|
||||||
else:
|
|
||||||
# 检查是否已存在
|
|
||||||
check_sql = """select id from llm_api_map
|
|
||||||
where llmid = ${llmid}$ and apiname = ${apiname}$"""
|
|
||||||
exists = await sor.sqlExe(check_sql, {'llmid': llmid, 'apiname': apiname})
|
|
||||||
|
|
||||||
if exists:
|
|
||||||
result['options'] = {'title': '提示', 'message': '该模型的此API映射已存在', 'type': 'warning'}
|
|
||||||
else:
|
|
||||||
data = {
|
|
||||||
'id': new_id,
|
|
||||||
'llmid': llmid,
|
|
||||||
'llmcatelogid': catelogid,
|
|
||||||
'apiname': apiname
|
|
||||||
}
|
|
||||||
query_apiname = params_kw.get('query_apiname', '').strip()
|
|
||||||
if query_apiname:
|
|
||||||
data['query_apiname'] = query_apiname
|
|
||||||
query_period = params_kw.get('query_period', '').strip()
|
|
||||||
if query_period:
|
|
||||||
try:
|
|
||||||
data['query_period'] = int(query_period)
|
|
||||||
except ValueError:
|
|
||||||
data['query_period'] = 30
|
|
||||||
else:
|
|
||||||
data['query_period'] = 30
|
|
||||||
ppid = params_kw.get('ppid', '').strip()
|
|
||||||
if ppid:
|
|
||||||
data['ppid'] = ppid
|
|
||||||
|
|
||||||
await sor.C('llm_api_map', data)
|
|
||||||
result['options'] = {'title': 'Success', 'message': '添加成功', 'type': 'success'}
|
|
||||||
|
|
||||||
except Exception as e:
|
|
||||||
result['options'] = {'title': 'Error', 'message': f'添加失败: {str(e)}', 'type': 'error'}
|
|
||||||
|
|
||||||
return json.dumps(result, ensure_ascii=False)
|
|
||||||
@ -1,31 +0,0 @@
|
|||||||
#!/usr/bin/env python3
|
|
||||||
import json
|
|
||||||
|
|
||||||
result = {'widgettype': 'Message', 'options': {'title': 'Error', 'message': 'Invalid', 'type': 'error'}}
|
|
||||||
|
|
||||||
try:
|
|
||||||
dbname = get_module_dbname('llmage')
|
|
||||||
map_id = params_kw.get('id', '')
|
|
||||||
|
|
||||||
if not map_id:
|
|
||||||
result['options'] = {'title': 'Error', 'message': '缺少ID参数', 'type': 'error'}
|
|
||||||
else:
|
|
||||||
user_orgid = await get_userorgid()
|
|
||||||
async with DBPools().sqlorContext(dbname) as sor:
|
|
||||||
# 验证映射记录对应的模型属于当前用户的机构
|
|
||||||
check = await sor.sqlExe(
|
|
||||||
"""select m.id from llm_api_map m
|
|
||||||
join llm l on m.llmid = l.id
|
|
||||||
where m.id = ${id}$ and l.ownerid = ${ownerid}$""",
|
|
||||||
{'id': map_id, 'ownerid': user_orgid}
|
|
||||||
)
|
|
||||||
if not check:
|
|
||||||
result['options'] = {'title': 'Error', 'message': '无权删除该映射', 'type': 'error'}
|
|
||||||
else:
|
|
||||||
await sor.sqlExe("delete from llm_api_map where id = ${id}$", {'id': map_id})
|
|
||||||
result['options'] = {'title': 'Success', 'message': '删除成功', 'type': 'success'}
|
|
||||||
|
|
||||||
except Exception as e:
|
|
||||||
result['options'] = {'title': 'Error', 'message': f'删除失败: {str(e)}', 'type': 'error'}
|
|
||||||
|
|
||||||
return json.dumps(result, ensure_ascii=False)
|
|
||||||
@ -1,32 +0,0 @@
|
|||||||
#!/usr/bin/env python3
|
|
||||||
import json
|
|
||||||
|
|
||||||
result = {'success': False, 'rows': [], 'total': 0}
|
|
||||||
|
|
||||||
try:
|
|
||||||
dbname = get_module_dbname('llmage')
|
|
||||||
user_orgid = await get_userorgid()
|
|
||||||
|
|
||||||
sql = """
|
|
||||||
select m.id, m.llmid, l.name as llm_name,
|
|
||||||
m.llmcatelogid, c.name as catelog_name,
|
|
||||||
m.apiname, m.query_apiname, m.query_period,
|
|
||||||
m.ppid, p.name as ppid_name
|
|
||||||
from llm_api_map m
|
|
||||||
join llm l on m.llmid = l.id
|
|
||||||
join llmcatelog c on m.llmcatelogid = c.id
|
|
||||||
left join pricing_program p on m.ppid = p.id
|
|
||||||
where l.ownerid = ${ownerid}$
|
|
||||||
order by l.name, c.name, m.apiname
|
|
||||||
"""
|
|
||||||
|
|
||||||
async with DBPools().sqlorContext(dbname) as sor:
|
|
||||||
rows = await sor.sqlExe(sql, {'ownerid': user_orgid})
|
|
||||||
result['rows'] = [dict(r) for r in (rows or [])]
|
|
||||||
result['total'] = len(result['rows'])
|
|
||||||
result['success'] = True
|
|
||||||
|
|
||||||
except Exception as e:
|
|
||||||
result['error'] = str(e)
|
|
||||||
|
|
||||||
return json.dumps(result, ensure_ascii=False, default=str)
|
|
||||||
@ -1,38 +0,0 @@
|
|||||||
#!/usr/bin/env python3
|
|
||||||
import json
|
|
||||||
|
|
||||||
result = {'success': False, 'data': {'llms': [], 'catelogs': [], 'apis': [], 'ppids': []}}
|
|
||||||
|
|
||||||
try:
|
|
||||||
dbname = get_module_dbname('llmage')
|
|
||||||
user_orgid = await get_userorgid()
|
|
||||||
|
|
||||||
async with DBPools().sqlorContext(dbname) as sor:
|
|
||||||
# Active LLMs for user's organization
|
|
||||||
today = await get_business_date(sor)
|
|
||||||
llms_sql = """select id, name from llm
|
|
||||||
where enabled_date <= ${today}$
|
|
||||||
and expired_date > ${today}$
|
|
||||||
and ownerid = ${ownerid}$
|
|
||||||
order by name"""
|
|
||||||
llms = await sor.sqlExe(llms_sql, {'today': today, 'ownerid': user_orgid})
|
|
||||||
result['data']['llms'] = [{'id': r['id'], 'text': r['name']} for r in (llms or [])]
|
|
||||||
|
|
||||||
# All catalogs
|
|
||||||
catelogs = await sor.sqlExe("select id, name from llmcatelog order by name", {})
|
|
||||||
result['data']['catelogs'] = [{'id': r['id'], 'text': r['name']} for r in (catelogs or [])]
|
|
||||||
|
|
||||||
# uapi list (for apiname selection)
|
|
||||||
apis = await sor.sqlExe("select name, path from uapi order by name", {})
|
|
||||||
result['data']['apis'] = [{'id': r['name'], 'text': f"{r['name']} ({r['path']})"} for r in (apis or [])]
|
|
||||||
|
|
||||||
# Pricing programs
|
|
||||||
ppids = await sor.sqlExe("select id, name from pricing_program order by name", {})
|
|
||||||
result['data']['ppids'] = [{'id': r['id'], 'text': r['name']} for r in (ppids or [])]
|
|
||||||
|
|
||||||
result['success'] = True
|
|
||||||
|
|
||||||
except Exception as e:
|
|
||||||
result['error'] = str(e)
|
|
||||||
|
|
||||||
return json.dumps(result, ensure_ascii=False, default=str)
|
|
||||||
@ -1,17 +0,0 @@
|
|||||||
#!/usr/bin/env python3
|
|
||||||
import json
|
|
||||||
|
|
||||||
result = {'success': False, 'data': []}
|
|
||||||
|
|
||||||
try:
|
|
||||||
dbname = get_module_dbname('llmage')
|
|
||||||
|
|
||||||
async with DBPools().sqlorContext(dbname) as sor:
|
|
||||||
rows = await sor.sqlExe("select name, path from uapi order by name", {})
|
|
||||||
result['data'] = [{'id': r['name'], 'text': f"{r['name']} ({r['path']})"} for r in (rows or [])]
|
|
||||||
result['success'] = True
|
|
||||||
|
|
||||||
except Exception as e:
|
|
||||||
result['error'] = str(e)
|
|
||||||
|
|
||||||
return json.dumps(result, ensure_ascii=False, default=str)
|
|
||||||
@ -1,181 +0,0 @@
|
|||||||
{
|
|
||||||
"widgettype": "VBox",
|
|
||||||
"options": {
|
|
||||||
"width": "100%",
|
|
||||||
"height": "100%",
|
|
||||||
"spacing": 16
|
|
||||||
},
|
|
||||||
"subwidgets": [
|
|
||||||
{
|
|
||||||
"widgettype": "Title2",
|
|
||||||
"options": {
|
|
||||||
"text": "模型能力映射管理",
|
|
||||||
"halign": "left"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"widgettype": "VBox",
|
|
||||||
"options": {
|
|
||||||
"width": "calc(100% - 40px)",
|
|
||||||
"margin": "0 20px",
|
|
||||||
"padding": "16px",
|
|
||||||
"bgcolor": "#f5f5f5",
|
|
||||||
"spacing": 12
|
|
||||||
},
|
|
||||||
"subwidgets": [
|
|
||||||
{
|
|
||||||
"widgettype": "Text",
|
|
||||||
"options": {
|
|
||||||
"text": "添加新映射",
|
|
||||||
"fontSize": "16px",
|
|
||||||
"fontWeight": "bold"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"widgettype": "Form",
|
|
||||||
"id": "add_form",
|
|
||||||
"options": {
|
|
||||||
"layout": "horizontal",
|
|
||||||
"cols": 3,
|
|
||||||
"fields": [
|
|
||||||
{
|
|
||||||
"name": "llmid",
|
|
||||||
"label": "选择模型",
|
|
||||||
"uitype": "select",
|
|
||||||
"dataurl": "{{entire_url('./api/llm_api_map_options.dspy')}}",
|
|
||||||
"data_field": "llms",
|
|
||||||
"placeholder": "请选择模型"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "llmcatelogid",
|
|
||||||
"label": "选择分类",
|
|
||||||
"uitype": "select",
|
|
||||||
"dataurl": "{{entire_url('./api/llm_api_map_options.dspy')}}",
|
|
||||||
"data_field": "catelogs",
|
|
||||||
"placeholder": "请选择分类"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "apiname",
|
|
||||||
"label": "API接口",
|
|
||||||
"uitype": "select",
|
|
||||||
"dataurl": "{{entire_url('./api/llm_api_map_options.dspy')}}",
|
|
||||||
"data_field": "apis",
|
|
||||||
"placeholder": "请选择API接口"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "query_apiname",
|
|
||||||
"label": "查询API",
|
|
||||||
"uitype": "text",
|
|
||||||
"placeholder": "异步查询API名,多个用逗号分隔"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "query_period",
|
|
||||||
"label": "查询间隔(秒)",
|
|
||||||
"uitype": "number",
|
|
||||||
"placeholder": "默认30"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "ppid",
|
|
||||||
"label": "计费项目",
|
|
||||||
"uitype": "select",
|
|
||||||
"dataurl": "{{entire_url('./api/llm_api_map_options.dspy')}}",
|
|
||||||
"data_field": "ppids",
|
|
||||||
"placeholder": "请选择计费项目"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"buttons": [
|
|
||||||
{
|
|
||||||
"name": "add_btn",
|
|
||||||
"label": "添加映射",
|
|
||||||
"variant": "primary"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"binds": [
|
|
||||||
{
|
|
||||||
"wid": "add_btn",
|
|
||||||
"event": "click",
|
|
||||||
"actiontype": "urlwidget",
|
|
||||||
"target": "PopupWindow",
|
|
||||||
"popup_options": {"archor": "cc", "width": "30%", "height": "20%"},
|
|
||||||
"options": {
|
|
||||||
"url": "{{entire_url('./api/llm_api_map_create.dspy')}}",
|
|
||||||
"params": {
|
|
||||||
"llmid": "$[add_form.llmid]$",
|
|
||||||
"llmcatelogid": "$[add_form.llmcatelogid]$",
|
|
||||||
"apiname": "$[add_form.apiname]$",
|
|
||||||
"query_apiname": "$[add_form.query_apiname]$",
|
|
||||||
"query_period": "$[add_form.query_period]$",
|
|
||||||
"ppid": "$[add_form.ppid]$"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"widgettype": "VBox",
|
|
||||||
"options": {
|
|
||||||
"width": "calc(100% - 40px)",
|
|
||||||
"margin": "0 20px",
|
|
||||||
"spacing": 12
|
|
||||||
},
|
|
||||||
"subwidgets": [
|
|
||||||
{
|
|
||||||
"widgettype": "Text",
|
|
||||||
"options": {
|
|
||||||
"text": "当前映射列表",
|
|
||||||
"fontSize": "16px",
|
|
||||||
"fontWeight": "bold"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"widgettype": "Tabular",
|
|
||||||
"id": "map_table",
|
|
||||||
"options": {
|
|
||||||
"width": "100%",
|
|
||||||
"height": "400px",
|
|
||||||
"data_url": "{{entire_url('./api/llm_api_map_list.dspy')}}",
|
|
||||||
"data_method": "GET",
|
|
||||||
"page_rows": 20,
|
|
||||||
"row_options": {
|
|
||||||
"fields": [
|
|
||||||
{"name": "llm_name", "title": "模型名称", "width": 180},
|
|
||||||
{"name": "catelog_name", "title": "分类", "width": 120},
|
|
||||||
{"name": "apiname", "title": "API接口", "width": 150},
|
|
||||||
{"name": "query_apiname", "title": "查询API", "width": 180},
|
|
||||||
{"name": "query_period", "title": "间隔(秒)", "width": 80},
|
|
||||||
{"name": "ppid_name", "title": "计费项目", "width": 150},
|
|
||||||
{
|
|
||||||
"name": "actions",
|
|
||||||
"title": "操作",
|
|
||||||
"width": 100,
|
|
||||||
"uitype": "button",
|
|
||||||
"data": [
|
|
||||||
{"text": "删除", "event": "delete_map"}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"binds": [
|
|
||||||
{
|
|
||||||
"wid": "self",
|
|
||||||
"event": "delete_map",
|
|
||||||
"actiontype": "urlwidget",
|
|
||||||
"target": "PopupWindow",
|
|
||||||
"popup_options": {"archor": "cc", "width": "30%", "height": "20%"},
|
|
||||||
"options": {
|
|
||||||
"url": "{{entire_url('./api/llm_api_map_delete.dspy')}}",
|
|
||||||
"params": {
|
|
||||||
"id": "$[event.params.id]$"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
@ -1,8 +1,7 @@
|
|||||||
{% if get_user() %}
|
{% if get_user() %}
|
||||||
{% set userid = get_user() %}
|
|
||||||
{% set userorgid = get_userorgid() %}
|
{% set userorgid = get_userorgid() %}
|
||||||
{% if params_kw.id %}
|
{% if params_kw.id %}
|
||||||
{% if checkCustomerBalance(params_kw.id, userid, userorgid) %}
|
{% if checkCustomerBalance(params_kw.id, userorgid) %}
|
||||||
{% set llm = get_llm(params_kw.id) %}
|
{% set llm = get_llm(params_kw.id) %}
|
||||||
{% set kdbs = get_user_kdbs(request) %}
|
{% set kdbs = get_user_kdbs(request) %}
|
||||||
{% if llm %}
|
{% if llm %}
|
||||||
|
|||||||
@ -10,7 +10,7 @@ userid = await get_user()
|
|||||||
userorgid = await get_userorgid()
|
userorgid = await get_userorgid()
|
||||||
if userid is None:
|
if userid is None:
|
||||||
return UiError(title='llm inference', message='Please login first')
|
return UiError(title='llm inference', message='Please login first')
|
||||||
f = await checkCustomerBalance(params_kw.llmid, userid, userorgid)
|
f = await checkCustomerBalance(params_kw.llmid, userorgid)
|
||||||
kdbids = params_kw.kdbids
|
kdbids = params_kw.kdbids
|
||||||
if kdbids:
|
if kdbids:
|
||||||
data = {
|
data = {
|
||||||
|
|||||||
@ -44,7 +44,7 @@ where b.name = ${lctype}$
|
|||||||
return openai_400()
|
return openai_400()
|
||||||
params_kw.llmid = recs[0].id
|
params_kw.llmid = recs[0].id
|
||||||
|
|
||||||
f = await checkCustomerBalance(params_kw.llmid, userid, userorgid)
|
f = await checkCustomerBalance(params_kw.llmid, userorgid)
|
||||||
if not f:
|
if not f:
|
||||||
debug(f'{userid=} balance not enough')
|
debug(f'{userid=} balance not enough')
|
||||||
return openai_429()
|
return openai_429()
|
||||||
|
|||||||
@ -10,7 +10,7 @@ userid = await get_user()
|
|||||||
userorgid = await get_userorgid()
|
userorgid = await get_userorgid()
|
||||||
if userid is None:
|
if userid is None:
|
||||||
return UiError(title='llm inference', message='Please login first')
|
return UiError(title='llm inference', message='Please login first')
|
||||||
f = await checkCustomerBalance(params_kw.llmid, userid, userorgid)
|
f = await checkCustomerBalance(params_kw.llmid, userorgid)
|
||||||
if not f:
|
if not f:
|
||||||
return {
|
return {
|
||||||
"status": "error",
|
"status": "error",
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user