chore: remove build/ from git tracking, add to .gitignore
This commit is contained in:
parent
5ec5946a90
commit
62dce1d3d7
1
.gitignore
vendored
1
.gitignore
vendored
@ -6,3 +6,4 @@ wwwroot/llmcatelog_list/
|
||||
wwwroot/llmusage/
|
||||
wwwroot/llmusage_accounting_failed/
|
||||
wwwroot/llmusage_history/
|
||||
build/
|
||||
|
||||
@ -1,369 +0,0 @@
|
||||
import asyncio
|
||||
import json
|
||||
import time
|
||||
from datetime import datetime
|
||||
from appPublic.log import exception, debug
|
||||
from appPublic.uniqueID import getID
|
||||
from appPublic.dictObject import DictObject
|
||||
from sqlor.dbpools import get_sor_context
|
||||
from ahserver.serverenv import ServerEnv
|
||||
from accounting.consume import consume_accounting
|
||||
from accounting.getaccount import getCustomerBalance
|
||||
from .utils import *
|
||||
|
||||
async def llm_charging(ppid, llmusage):
|
||||
env = ServerEnv()
|
||||
usages = llmusage.usages
|
||||
if isinstance(usages, str):
|
||||
usages = json.loads(usages)
|
||||
prices = await env.buffered_charging(ppid, usages)
|
||||
if prices is None:
|
||||
e = Exception(f'{ppid=}, {usages=}{llmusage.id=} env.buffered_charging() return None')
|
||||
exception(f'{e}')
|
||||
raise e
|
||||
return None
|
||||
amount = 0
|
||||
cost = 0
|
||||
for p in prices:
|
||||
amount += p.amount
|
||||
if p.cost:
|
||||
cost += p.cost
|
||||
discount = await env.get_customer_discount(llmusage.ownerid,
|
||||
llmusage.userorgid)
|
||||
return DictObject(**{
|
||||
'original_amount': amount,
|
||||
'amount': amount * discount,
|
||||
'cost': cost
|
||||
})
|
||||
|
||||
async def checkCustomerBalance(llmid, userid, userorgid, catelogid=None):
|
||||
if llmid is None:
|
||||
debug(f'checkCustomerBalance(): llmid is None')
|
||||
return False
|
||||
env = ServerEnv()
|
||||
llm = await get_llm(llmid)
|
||||
if llm.ownerid == userorgid:
|
||||
debug(f'self orgid user')
|
||||
return True
|
||||
balance = 0.00
|
||||
tpac = await get_user_tpac(userid)
|
||||
if tpac:
|
||||
debug(f'{tpac=}, get tpac balance')
|
||||
balance = await get_tpac_balance(tpac, userid)
|
||||
else:
|
||||
debug(f'{tpac=}, get local balance')
|
||||
async with get_sor_context(env, 'accounting') as sor:
|
||||
balance = await getCustomerBalance(sor, userorgid)
|
||||
bal = 0 if balance is None else balance
|
||||
if llm.min_balance is None:
|
||||
llm.min_balance = 0.00
|
||||
ret = llm.ppid and llm.min_balance < bal
|
||||
debug(f'{llm.ppid=}, {llm.min_balance=}, {bal=}')
|
||||
return ret
|
||||
|
||||
async def llm_accounting(llmusage):
|
||||
env = ServerEnv()
|
||||
llmid = llmusage.llmid
|
||||
async with get_sor_context(env, 'llmage') as sor:
|
||||
sql = """select a.*, b.ppid from llm a, llm_api_map b
|
||||
where a.id=${llmid}$
|
||||
and a.id = b.llmid
|
||||
and b.isdefaultcatelog = '1'
|
||||
"""
|
||||
recs = await sor.sqlExe(sql, {'llmid': llmusage.llmid})
|
||||
if len(recs) == 0:
|
||||
ns = {
|
||||
'id': llmusage.id,
|
||||
'accounting_status': 'failed'
|
||||
}
|
||||
await sor.U('llmusage', ns)
|
||||
e = Exception(f'llm not found({llmid})')
|
||||
exception(f'{e}')
|
||||
raise e
|
||||
if recs[0].ppid is None:
|
||||
ns = {
|
||||
'id': llmusage.id,
|
||||
'accounting_status': 'failed'
|
||||
}
|
||||
await sor.U('llmusage', ns)
|
||||
e = Exception(f'llm ({llmid}) donot has a pricing_program')
|
||||
exception(f'{e}')
|
||||
raise e
|
||||
customerid = llmusage.userorgid
|
||||
userid = llmusage.userid
|
||||
resellerid = recs[0].ownerid
|
||||
providerid = recs[0].providerid
|
||||
trans_amount = llmusage.amount
|
||||
trans_cost = llmusage.cost
|
||||
biz_date = await env.get_business_date(sor)
|
||||
timestamp = env.timestampstr()
|
||||
orderid = getID()
|
||||
order = {
|
||||
"id": orderid,
|
||||
"customerid": customerid,
|
||||
"resellerid": resellerid,
|
||||
"order_date": biz_date,
|
||||
"order_status": "1", # accounted
|
||||
"business_op": "PAY",
|
||||
"amount": trans_amount,
|
||||
"userid": userid,
|
||||
"productid": llmid
|
||||
}
|
||||
await sor.C('biz_order', order)
|
||||
orderdetail = {
|
||||
"id": getID(),
|
||||
"orderid": orderid,
|
||||
"productid": llmid,
|
||||
"product_cnt": 1,
|
||||
"trans_amount": trans_amount
|
||||
}
|
||||
await sor.C('biz_orderdetail', orderdetail)
|
||||
ais = []
|
||||
if customerid != resellerid:
|
||||
ai0 = DictObject()
|
||||
ai0.action = 'PAY'
|
||||
ai0.customerid = customerid
|
||||
ai0.resellerid = resellerid
|
||||
ai0.providerid = providerid
|
||||
ai0.biz_date = biz_date
|
||||
ai0.timestamp = timestamp
|
||||
ai0.productid = llmid
|
||||
ai0.transamt = trans_amount
|
||||
ai0.variable = {
|
||||
"交易金额": trans_amount,
|
||||
"交易手续费": 0
|
||||
}
|
||||
ais.append(ai0)
|
||||
ai1 = DictObject()
|
||||
ai1.action = 'PAY*'
|
||||
ai1.customerid = customerid
|
||||
ai1.resellerid = resellerid
|
||||
ai1.providerid = providerid
|
||||
ai1.biz_date = biz_date
|
||||
ai1.timestamp = timestamp
|
||||
ai1.providerid = providerid
|
||||
ai1.productid = llmid
|
||||
ai1.transamt = trans_cost
|
||||
ai1.variable = {
|
||||
"采购成本": trans_cost
|
||||
}
|
||||
ais.append(ai1)
|
||||
await consume_accounting(sor, orderid, ais)
|
||||
llmusage.accounting_status = 'accounted'
|
||||
ns = {
|
||||
'id': llmusage.id,
|
||||
'accounting_status': 'accounted'
|
||||
}
|
||||
await sor.U('llmusage', ns)
|
||||
|
||||
async def get_accounting_llmusages(luid=None):
|
||||
env = ServerEnv()
|
||||
lus = []
|
||||
t = time.time()
|
||||
dt = datetime.fromtimestamp(t)
|
||||
tsstr = dt.strftime('%Y-%m-%d %H:%M:%S.') + f'{dt.microsecond // 1000:03d}'
|
||||
async with get_sor_context(env, 'llmage') as sor:
|
||||
sql = """select a.*, c.ppid
|
||||
from llmusage a, llm b, llm_api_map c
|
||||
where a.llmid = b.id
|
||||
and a.llmid = c.llmid
|
||||
and c.isdefaultcatelog = '1'
|
||||
and a.status = 'SUCCEEDED'
|
||||
and a.use_time < ${tsstr}$
|
||||
and a.accounting_status='created'"""
|
||||
ns = {'tsstr': tsstr}
|
||||
if luid:
|
||||
sql += " and a.id=${luid}$"
|
||||
ns['luid'] = luid
|
||||
recs = await sor.sqlExe(sql, ns)
|
||||
# debug(f'{sql=}, {ns=}, {len(recs)=}')
|
||||
for r in recs:
|
||||
if r.usages is None:
|
||||
try:
|
||||
output = await get_lastoutput(r.ioinfo)
|
||||
except Exception as e:
|
||||
continue
|
||||
r.usages = output.get('usage')
|
||||
if r.usages is None:
|
||||
debug(f'{r.usages=} is None, accoiunting failed')
|
||||
await llm_accoung_failed(r.id, reason='usages is None')
|
||||
continue
|
||||
d = None
|
||||
try:
|
||||
debug(f'{r.ppid=}, {r.usages=} {r.id=}')
|
||||
d = await llm_charging(r.ppid, r)
|
||||
|
||||
except Exception as e:
|
||||
exception(f'{r.ppid=}, {r.usages=} llm_charging() failed,{e}')
|
||||
await llm_accoung_failed(r.id, reason=f'llm_charging failed: {e}')
|
||||
continue
|
||||
r.amount = d.amount
|
||||
r.cost = d.cost
|
||||
ns = {
|
||||
'id': r.id,
|
||||
'amount': r.amount,
|
||||
'cost': r.cost,
|
||||
'usage': json.dumps(r.usage, ensure_ascii=False, indent=4)
|
||||
}
|
||||
await sor.U('llmusage', ns)
|
||||
lus.append(r)
|
||||
return lus
|
||||
|
||||
async def llm_accoung_failed(luid, reason=None):
|
||||
env = ServerEnv()
|
||||
async with get_sor_context(env, 'llmage') as sor:
|
||||
await sor.U('llmusage', {
|
||||
'id': luid,
|
||||
'accounting_status': 'failed'
|
||||
})
|
||||
# Also record in the failed accounting table for tracking
|
||||
recs = await sor.R('llmusage', {'id': luid})
|
||||
if recs:
|
||||
r = recs[0]
|
||||
failed_id = getID()
|
||||
failed_rec = {
|
||||
'id': failed_id,
|
||||
'llmusageid': luid,
|
||||
'llmid': r.llmid,
|
||||
'userid': r.userid,
|
||||
'userorgid': r.userorgid,
|
||||
'use_date': r.use_date,
|
||||
'use_time': r.use_time,
|
||||
'amount': r.amount,
|
||||
'cost': r.cost,
|
||||
'failed_reason': reason or 'accounting failed',
|
||||
'failed_time': env.timestampstr(),
|
||||
'retry_count': 0,
|
||||
'handled': '0'
|
||||
}
|
||||
await sor.C('llmusage_accounting_failed', failed_rec)
|
||||
|
||||
|
||||
async def backup_accounted_llmusage():
|
||||
"""Backup yesterday's accounted records to history table and remove from llmusage."""
|
||||
env = ServerEnv()
|
||||
yesterday = (datetime.now() - timedelta(days=1)).strftime('%Y-%m-%d')
|
||||
ts = env.timestampstr()
|
||||
batched = 0
|
||||
async with get_sor_context(env, 'llmage') as sor:
|
||||
# Select yesterday's accounted records
|
||||
sql = """select * from llmusage
|
||||
where accounting_status='accounted'
|
||||
and use_date < ${yesterday}$"""
|
||||
recs = await sor.sqlExe(sql, {'yesterday': yesterday})
|
||||
if not recs:
|
||||
debug(f'backup_accounted_llmusage: no records to backup for use_date < {yesterday}')
|
||||
return 0
|
||||
debug(f'backup_accounted_llmusage: {len(recs)} records to backup')
|
||||
for r in recs:
|
||||
history_rec = {
|
||||
'id': r.id,
|
||||
'llmid': r.llmid,
|
||||
'use_date': r.use_date,
|
||||
'use_time': r.use_time,
|
||||
'userid': r.userid,
|
||||
'usages': r.usages,
|
||||
'ioinfo': r.ioinfo,
|
||||
'transno': r.transno,
|
||||
'responsed_seconds': r.responsed_seconds,
|
||||
'finish_seconds': r.finish_seconds,
|
||||
'status': r.status,
|
||||
'taskid': r.taskid,
|
||||
'amount': r.amount,
|
||||
'cost': r.cost,
|
||||
'userorgid': r.userorgid,
|
||||
'ownerid': r.ownerid,
|
||||
'accounting_status': r.accounting_status,
|
||||
'backup_time': ts
|
||||
}
|
||||
await sor.C('llmusage_history', history_rec)
|
||||
# Delete from main table
|
||||
await sor.D('llmusage', {'id': r.id})
|
||||
batched += 1
|
||||
debug(f'backup_accounted_llmusage: backed up {batched} records')
|
||||
return batched
|
||||
|
||||
|
||||
async def get_failed_accounting_records(filters=None, page=1, page_size=50):
|
||||
"""Search failed accounting records with optional filters.
|
||||
|
||||
filters: dict with optional keys:
|
||||
- userorgid: filter by user organization
|
||||
- llmid: filter by model ID
|
||||
- handled: '0' or '1'
|
||||
- start_date: filter use_date >= start_date
|
||||
- end_date: filter use_date <= end_date
|
||||
"""
|
||||
async with get_sor_context(ServerEnv(), 'llmage') as sor:
|
||||
conditions = []
|
||||
ns = {}
|
||||
if filters:
|
||||
if filters.get('userorgid'):
|
||||
conditions.append("userorgid=${userorgid}$")
|
||||
ns['userorgid'] = filters['userorgid']
|
||||
if filters.get('llmid'):
|
||||
conditions.append("llmid=${llmid}$")
|
||||
ns['llmid'] = filters['llmid']
|
||||
if filters.get('handled') is not None:
|
||||
conditions.append("handled=${handled}$")
|
||||
ns['handled'] = filters['handled']
|
||||
if filters.get('start_date'):
|
||||
conditions.append("use_date>=${start_date}$")
|
||||
ns['start_date'] = filters['start_date']
|
||||
if filters.get('end_date'):
|
||||
conditions.append("use_date<=${end_date}$")
|
||||
ns['end_date'] = filters['end_date']
|
||||
where = ""
|
||||
if conditions:
|
||||
where = "where " + " and ".join(conditions)
|
||||
# Count total
|
||||
count_sql = f"select count(*) as cnt from llmusage_accounting_failed {where}"
|
||||
count_recs = await sor.sqlExe(count_sql, ns)
|
||||
total = count_recs[0].cnt if count_recs else 0
|
||||
# Query with pagination
|
||||
offset = (page - 1) * page_size
|
||||
query_sql = f"""select * from llmusage_accounting_failed {where}
|
||||
order by failed_time desc limit {page_size} offset {offset}"""
|
||||
recs = await sor.sqlExe(query_sql, ns)
|
||||
return {
|
||||
'total': total,
|
||||
'page': page,
|
||||
'page_size': page_size,
|
||||
'records': recs
|
||||
}
|
||||
|
||||
async def backend_accounting():
|
||||
env = ServerEnv()
|
||||
debug(f'backend accounting started ...')
|
||||
backup_counter = 0
|
||||
while True:
|
||||
try:
|
||||
lus = await get_accounting_llmusages()
|
||||
except Exception as e:
|
||||
exception(f'{e}')
|
||||
lus = []
|
||||
# debug(f'{len(lus)=} need to accounting........')
|
||||
for lu in lus:
|
||||
try:
|
||||
tpac = await get_user_tpac(lu.userid)
|
||||
if tpac:
|
||||
debug(f'{lu.id=},{lu.userid=}, {tpac=}, go tpac')
|
||||
await tpac_accounting(tpac, lu.userid, lu.llmid, lu.amount, lu.usages, lu.id)
|
||||
else:
|
||||
debug(f'{lu.id=},{lu.userid=}, {tpac=}, go local')
|
||||
await llm_accounting(lu)
|
||||
except Exception as e:
|
||||
exception(f'{e}, {lu.id=}')
|
||||
await llm_accoung_failed(lu.id, reason=str(e))
|
||||
|
||||
# Run backup every 100 iterations (roughly every ~1000 seconds)
|
||||
backup_counter += 1
|
||||
if backup_counter >= 100:
|
||||
backup_counter = 0
|
||||
try:
|
||||
await backup_accounted_llmusage()
|
||||
except Exception as e:
|
||||
exception(f'backup_accounted_llmusage failed: {e}')
|
||||
|
||||
await asyncio.sleep(10)
|
||||
|
||||
@ -1,203 +0,0 @@
|
||||
import json
|
||||
import time
|
||||
import asyncio
|
||||
from random import randint
|
||||
from functools import partial
|
||||
from traceback import format_exc
|
||||
from sqlor.dbpools import DBPools, get_sor_context
|
||||
from appPublic.log import debug, exception, error, critical
|
||||
from appPublic.uniqueID import getID
|
||||
from appPublic.dictObject import DictObject
|
||||
from appPublic.timeUtils import curDateString, timestampstr, timestampAdd
|
||||
from appPublic.base64_to_file import base64_to_file, getFilenameFromBase64
|
||||
from ahserver.serverenv import get_serverenv, ServerEnv
|
||||
from ahserver.filestorage import FileStorage
|
||||
from .accounting import llm_accounting, llm_charging
|
||||
from .utils import *
|
||||
|
||||
async def get_today_asynctask_list(userid):
|
||||
env = ServerEnv()
|
||||
async with get_sor_context(env, 'llmage') as sor:
|
||||
today = await env.get_business_date(sor)
|
||||
sql = '''select * from llmusage
|
||||
where userid=${userid}$
|
||||
and use_date = ${date}$'''
|
||||
recs = await sor.sqlExe(sql, {
|
||||
'date': today,
|
||||
'userid': userid
|
||||
})
|
||||
return recs
|
||||
return []
|
||||
|
||||
async def get_asynctask_status(request, taskid):
|
||||
env = ServerEnv()
|
||||
async with get_sor_context(env, 'llmage') as sor:
|
||||
recs = await sor.R('llmusage', {'taskid': taskid})
|
||||
if recs:
|
||||
r = recs[0]
|
||||
output = await get_lastoutput(r.ioinfo)
|
||||
t = timestampAdd(r.use_time, 600)
|
||||
now = time.time()
|
||||
if r.status not in ['UNKNOWN', 'FAILED', 'SUCCEEDED'] and now > t:
|
||||
asyncio.create_task(query_task_status(request, r.id))
|
||||
return output
|
||||
return {
|
||||
'taskid': taskid,
|
||||
'status': 'FAILED',
|
||||
'error': f'taskid={taskid} not exist'
|
||||
}
|
||||
return {
|
||||
'taskid': taskid,
|
||||
'status': 'FAILED',
|
||||
'error': f'system error'
|
||||
}
|
||||
|
||||
async def async_uapi_request(request, llm,
|
||||
callerid, callerorgid, params_kw=None):
|
||||
env = request._run_ns.copy()
|
||||
if not params_kw:
|
||||
params_kw = env.params_kw
|
||||
# callerorgid = await env.get_userorgid()
|
||||
# callerid = await env.get_user()
|
||||
uapi = env.UpAppApi(request)
|
||||
userid = await env.uapi_data.get_calluserid(llm.upappid, orgid=llm.ownerid)
|
||||
b = None
|
||||
luid = getID()
|
||||
try:
|
||||
start_timestamp = time.time()
|
||||
if llm.callbackurl:
|
||||
params_kw.callbackurl = llm.callbackurl
|
||||
|
||||
b = None
|
||||
try:
|
||||
b = await uapi.call(llm.upappid, llm.apiname, userid, params=params_kw)
|
||||
except Exception as e:
|
||||
estr = erase_apikey(e)
|
||||
ed = {"error": f"ERROR:{estr}", "status": "FAILED"}
|
||||
exception(f'{ed}')
|
||||
yield f'{ed}\n'
|
||||
return
|
||||
if isinstance(b, bytes):
|
||||
b = b.decode('utf-8')
|
||||
debug(f'task submited:{b}')
|
||||
d = DictObject(**json.loads(b))
|
||||
responsed_seconds = time.time() - start_timestamp
|
||||
finish_seconds = responsed_seconds
|
||||
llmusage = DictObject()
|
||||
llmusage.id = luid
|
||||
llmusage.llmid = llm.id
|
||||
llmusage.use_date = curDateString()
|
||||
llmusage.use_time = timestampstr()
|
||||
llmusage.userid = callerid
|
||||
ioinfo = {
|
||||
"input": params_kw,
|
||||
'output': [d]
|
||||
}
|
||||
webpath = await write_llmio(llmusage.id, ioinfo)
|
||||
llmusage.ioinfo = webpath
|
||||
llmusage.taskid = d.taskid
|
||||
llmusage.transno = params_kw.transno
|
||||
llmusage.responsed_seconds = responsed_seconds
|
||||
llmusage.finish_seconds = finish_seconds
|
||||
llmusage.status = d.status
|
||||
llmusage.userorgid = callerorgid
|
||||
llmusage.ownerid = llm.ownerid
|
||||
llmusage.accounting_status = 'created'
|
||||
b = json.dumps(d, ensure_ascii=False)
|
||||
yield b
|
||||
await write_llmusage(llmusage)
|
||||
# if llm.callbackurl:
|
||||
# return
|
||||
if d.status == 'FAILED':
|
||||
e = Exception(f'resp={d} FFAILED')
|
||||
return
|
||||
asyncio.create_task(query_task_status(request, luid))
|
||||
|
||||
except Exception as e:
|
||||
ed = {"error": f"ERROR:{e}", "status": "FAILED"}
|
||||
s = json.dumps(ed, ensure_ascii=False)
|
||||
s = ''.join(s.split('\n'))
|
||||
exception(s)
|
||||
yield f'{s}\n'
|
||||
return
|
||||
|
||||
async def modify_llmusage(ns):
|
||||
env = ServerEnv()
|
||||
async with get_sor_context(env, 'llmage') as sor:
|
||||
await sor.U('llmusage', ns.copy())
|
||||
|
||||
async def get_llm_llmusage(luid):
|
||||
env = ServerEnv()
|
||||
async with get_sor_context(env, 'llmage') as sor:
|
||||
recs = await sor.R('llmusage', {'id': luid})
|
||||
if len(recs) == 0:
|
||||
e = Exception(f'{luid=} is not found in llmusage')
|
||||
exception(f'{e}')
|
||||
raise e
|
||||
llmusage = recs[0]
|
||||
if llmusage.status == 'UNKNOWN':
|
||||
return
|
||||
if llmusage.status == 'SUCCEEDED':
|
||||
return
|
||||
if llmusage.status == 'FAILED':
|
||||
return
|
||||
llms = await sor.R('llm', {'id': llmusage.llmid})
|
||||
if len(llms) == 0:
|
||||
e = Exception(f'{llmusage.llmid=} not found in llm')
|
||||
exception(f'{e}')
|
||||
raise e
|
||||
llm = llms[0]
|
||||
return llm, llmusage
|
||||
|
||||
async def query_task_status(request, luid, onetime=False):
|
||||
env = ServerEnv()
|
||||
uapi = env.UpAppApi(request)
|
||||
llm, llmusage = await get_llm_llmusage(luid)
|
||||
userid = await env.uapi_data.get_calluserid(llm.upappid, orgid=llm.ownerid)
|
||||
taskid = llmusage.taskid
|
||||
upappid = llm.upappid
|
||||
apinames = llm.query_apiname.split(',')
|
||||
|
||||
for apiname in apinames:
|
||||
while True:
|
||||
lastoutout = await get_lastoutput(llmusage.ioinfo)
|
||||
if lastoutout['status'] in ['UNKNOWN', 'FAILED', 'SUCCEEDED']:
|
||||
critical(f"{lastoutout['status']=}")
|
||||
return
|
||||
ns = {'taskid': taskid}
|
||||
new_output = b = d = None
|
||||
try:
|
||||
b = await uapi.call(upappid, apiname, userid, params=ns)
|
||||
if isinstance(b, bytes):
|
||||
b = b.decode('utf-8')
|
||||
new_output = json.loads(b)
|
||||
except Exception as e:
|
||||
exception(f'{e}, {b=}')
|
||||
new_output = {
|
||||
'status': 'FAILED',
|
||||
'error': f'{b},{e}'
|
||||
}
|
||||
if not new_output.get('status'):
|
||||
e = Exception(f"{new_output=} {upappid=}, {apiname=} has not status field")
|
||||
critical(f'{e}')
|
||||
raise e
|
||||
if lastoutout['status'] != new_output.get('status'):
|
||||
llmusage.status = new_output['status']
|
||||
ns = {
|
||||
'id': llmusage.id,
|
||||
'status': llmusage.status
|
||||
}
|
||||
if 'usage' in new_output.keys():
|
||||
ns['usages'] = json.dumps(new_output['usage'])
|
||||
await append_new_llmoutput(llmusage.ioinfo, new_output)
|
||||
await modify_llmusage(ns)
|
||||
if llmusage.status in ['UNKNOWN', 'FAILED', 'SUCCEEDED']:
|
||||
critical(f'finished .. {llmusage.status=}')
|
||||
return
|
||||
|
||||
if onetime:
|
||||
critical(f'onetime is true, returned')
|
||||
return
|
||||
await asyncio.sleep(llm.query_period or 30)
|
||||
critical(f'{llm.query_period=} seconds will retry, {new_output["status"]=}')
|
||||
|
||||
@ -1,33 +0,0 @@
|
||||
from ahserver.serverenv import ServerEnv
|
||||
from appPublic.dictObject import DictObject
|
||||
from sqlor.dbpools import get_sor_context
|
||||
|
||||
async def asynctask_callback(appname, apiname, params_kw)
|
||||
env = ServerEnv()
|
||||
llmusage = None
|
||||
async with get_sor_context(env, 'llmage') as sor:
|
||||
uapi = await env.sor_get_uapi_by_appname_apiname(appname, apiname)
|
||||
try:
|
||||
dstr = await env.tmpl_engine.renders(uapi.response, params_kw)
|
||||
d = DictObject(**json.loads(dstr))
|
||||
llmus = await sor.R('llmusage', {'taskid': d.taskid})
|
||||
if len(llmus) == 0:
|
||||
e = Exception(f'{d=}, {taskid=} not found')
|
||||
exception(f'{e}')
|
||||
raise e
|
||||
llmusage = llmus[0]
|
||||
io = json.loads(llmusage.ioinfo)
|
||||
out = io.get('output')
|
||||
out.append(d)
|
||||
llmusage.ioinfo = json.dumps(io, ensure_ascii=False)
|
||||
llmusage.status = d.status
|
||||
await sor.U('llmusage', llmusage)
|
||||
|
||||
except Exception as e:
|
||||
e = Exception(f'{uapi.response=}, {params_kw=} render error')
|
||||
exception(f'{e}')
|
||||
raise e
|
||||
|
||||
if llmusage:
|
||||
await llm_accounting(llmusage)
|
||||
|
||||
@ -1,68 +0,0 @@
|
||||
import asyncio
|
||||
from appPublic.registerfunction import RegisterFunction
|
||||
from sqlor.dbpools import DBPools
|
||||
from ahserver.serverenv import ServerEnv
|
||||
from appPublic.log import debug
|
||||
from .keling import keling_token
|
||||
from .jimeng import jimeng_auth_headers
|
||||
from .utils import (
|
||||
llm_query_orders,
|
||||
read_webpath,
|
||||
llm_query_price,
|
||||
get_llm_by_model,
|
||||
get_llms_by_catelog,
|
||||
get_llms_sort_by_provider,
|
||||
get_llmcatelogs,
|
||||
get_llms_by_catelog_to_customer,
|
||||
get_llmproviders,
|
||||
get_llm,
|
||||
)
|
||||
|
||||
from .llmclient import (
|
||||
inference_generator,
|
||||
inference
|
||||
)
|
||||
from .accounting import (
|
||||
checkCustomerBalance,
|
||||
llm_charging,
|
||||
get_accounting_llmusages,
|
||||
backend_accounting,
|
||||
llm_accounting,
|
||||
backup_accounted_llmusage,
|
||||
get_failed_accounting_records,
|
||||
llm_accoung_failed
|
||||
)
|
||||
|
||||
from .asyncinference import (
|
||||
get_asynctask_status,
|
||||
query_task_status,
|
||||
get_today_asynctask_list
|
||||
)
|
||||
|
||||
def load_llmage():
|
||||
env = ServerEnv()
|
||||
env.llm_query_orders = llm_query_orders
|
||||
env.read_webpath = read_webpath
|
||||
env.get_llm_by_model = get_llm_by_model
|
||||
env.llm_charging = llm_charging
|
||||
env.get_accounting_llmusages = get_accounting_llmusages
|
||||
env.llm_accounting = llm_accounting
|
||||
env.get_today_asynctask_list = get_today_asynctask_list
|
||||
env.get_asynctask_status = get_asynctask_status
|
||||
env.query_task_status = query_task_status
|
||||
env.get_llm = get_llm
|
||||
env.inference = inference
|
||||
env.inference_generator = inference_generator
|
||||
env.get_llms_by_catelog = get_llms_by_catelog
|
||||
env.get_llmcatelogs = get_llmcatelogs
|
||||
env.checkCustomerBalance = checkCustomerBalance
|
||||
env.get_llmproviders = get_llmproviders
|
||||
env.get_llms_sort_by_provider = get_llms_sort_by_provider
|
||||
env.keling_token = keling_token
|
||||
env.llm_query_price = llm_query_price
|
||||
env.get_llms_by_catelog_to_customer = get_llms_by_catelog_to_customer
|
||||
env.backup_accounted_llmusage = backup_accounted_llmusage
|
||||
env.get_failed_accounting_records = get_failed_accounting_records
|
||||
rf = RegisterFunction()
|
||||
rf.register('jimeng_auth_headers', jimeng_auth_headers)
|
||||
|
||||
@ -1,98 +0,0 @@
|
||||
import hashlib
|
||||
import datetime
|
||||
from datetime import timezone
|
||||
import hmac
|
||||
import json
|
||||
from urllib.parse import quote
|
||||
|
||||
Service = "visual"
|
||||
Version = "2022-08-31"
|
||||
Region = "cn-north-1"
|
||||
Host = "visual.volcengineapi.com"
|
||||
ContentType = "application/json"
|
||||
|
||||
def utc_now():
|
||||
try:
|
||||
from datetime import timezone
|
||||
return datetime.datetime.now(timezone.utc)
|
||||
except ImportError:
|
||||
class UTC(datetime.tzinfo):
|
||||
def utcoffset(self, dt):
|
||||
return datetime.timedelta(0)
|
||||
def tzname(self, dt):
|
||||
return "UTC"
|
||||
def dst(self, dt):
|
||||
return datetime.timedelta(0)
|
||||
return datetime.datetime.now(UTC())
|
||||
|
||||
def jm_timestamp():
|
||||
dt = utc_new()
|
||||
return dt.strftime("%Y%m%dT%H%M%SZ")
|
||||
|
||||
# sha256 非对称加密
|
||||
def hmac_sha256(key: bytes, content: str):
|
||||
return hmac.new(key, content.encode("utf-8"), hashlib.sha256).digest()
|
||||
|
||||
# sha256 hash算法
|
||||
def hash_sha256(content: str):
|
||||
return hashlib.sha256(content.encode("utf-8")).hexdigest()
|
||||
|
||||
def jimeng_auth_headers(opts):
|
||||
apikey = opts.get('apikey')
|
||||
secretkey = opts.get('secretkey')
|
||||
path = opts.get('path')
|
||||
method = opts.get('method')
|
||||
params = opts.get('params')
|
||||
body = opts.get('body')
|
||||
headers = opts.get('headers')
|
||||
content_type = headers.get('Content-Type')
|
||||
x_date = jm_timestamp()
|
||||
short_x_date = DT[:8]
|
||||
credential = {
|
||||
"access_key_id": apikey,
|
||||
"secret_access_key": secretkey,
|
||||
"service": Service,
|
||||
"region": Region,
|
||||
}
|
||||
x_content_sha256 = hash_sha256(body)
|
||||
sign_result = {
|
||||
"Host": Host,
|
||||
"X-Content-Sha256": x_content_sha256,
|
||||
"X-Date": x_date,
|
||||
"Content-Type": ContentType
|
||||
}
|
||||
headers.update(sign_result)
|
||||
signed_headers_str = ";".join(
|
||||
["content-type", "host", "x-content-sha256", "x-date"]
|
||||
)
|
||||
canonical_request_str = "\n".join(
|
||||
[method.upper(),
|
||||
path,
|
||||
norm_query(params),
|
||||
"\n".join(
|
||||
[
|
||||
"content-type:" + content_type,
|
||||
"host:" + Host,
|
||||
"x-content-sha256:" + x_content_sha256,
|
||||
"x-date:" + x_date,
|
||||
]
|
||||
),
|
||||
"",
|
||||
signed_headers_str,
|
||||
x_content_sha256,
|
||||
]
|
||||
)
|
||||
hashed_canonical_request = hash_sha256(canonical_request_str)
|
||||
credential_scope = "/".join([short_x_date, credential["region"], credential["service"], "request"])
|
||||
string_to_sign = "\n".join(["HMAC-SHA256", x_date, credential_scope, hashed_canonical_request])
|
||||
k_date = hmac_sha256(secretkey.encode("utf-8"), short_x_date)
|
||||
k_region = hmac_sha256(k_date, credential["region"])
|
||||
k_service = hmac_sha256(k_region, credential["service"])
|
||||
k_signing = hmac_sha256(k_service, "request")
|
||||
signature = hmac_sha256(k_signing, string_to_sign).hex()
|
||||
headers['Authorization'] = "HMAC-SHA256 Credential={}, SignedHeaders={}, Signature={}".format(
|
||||
apikey + "/" + credential_scope,
|
||||
signed_headers_str,
|
||||
signature,
|
||||
)
|
||||
|
||||
@ -1,18 +0,0 @@
|
||||
import time
|
||||
import jwt
|
||||
|
||||
ak = "" # 填写access key
|
||||
sk = "" # 填写secret key
|
||||
|
||||
def keling_token(ak, sk):
|
||||
headers = {
|
||||
"alg": "HS256",
|
||||
"typ": "JWT"
|
||||
}
|
||||
payload = {
|
||||
"iss": ak,
|
||||
"exp": int(time.time()) + 1800, # 有效时间,此处示例代表当前时间+1800s(30min)
|
||||
"nbf": int(time.time()) - 5 # 开始生效的时间,此处示例代表当前时间-5秒
|
||||
}
|
||||
token = jwt.encode(payload, sk, headers=headers)
|
||||
return token
|
||||
@ -1,148 +0,0 @@
|
||||
import json
|
||||
import time
|
||||
import asyncio
|
||||
from random import randint
|
||||
from functools import partial
|
||||
from traceback import format_exc
|
||||
from appPublic.log import debug, exception, error
|
||||
from appPublic.uniqueID import getID
|
||||
from appPublic.dictObject import DictObject
|
||||
from appPublic.timeUtils import curDateString, timestampstr
|
||||
from appPublic.base64_to_file import base64_to_file, getFilenameFromBase64
|
||||
from ahserver.serverenv import get_serverenv, ServerEnv
|
||||
from ahserver.filestorage import FileStorage
|
||||
from .asyncinference import async_uapi_request
|
||||
from .syncinference import sync_uapi_request
|
||||
from .accounting import llm_accounting, llm_charging
|
||||
from .utils import *
|
||||
|
||||
async def uapi_request(request, llm, callerid, callerorgid, params_kw=None):
|
||||
env = request._run_ns.copy()
|
||||
if not params_kw:
|
||||
params_kw = env.params_kw
|
||||
# callerorgid = await env.get_userorgid()
|
||||
# callerid = await env.get_user()
|
||||
uapi = env.UpAppApi(request)
|
||||
userid = await env.uapi_data.get_calluserid(llm.upappid, orgid=llm.ownerid)
|
||||
outlines = []
|
||||
txt = ''
|
||||
luid = getID()
|
||||
try:
|
||||
start_timestamp = time.time()
|
||||
responsed_seconds = None
|
||||
finish_seconds = None
|
||||
first = True
|
||||
usage = None
|
||||
async for l in uapi.stream_linify(llm.upappid, llm.apiname, userid,
|
||||
params=params_kw):
|
||||
if first:
|
||||
first = False
|
||||
responsed_seconds = time.time() - start_timestamp
|
||||
if isinstance(l, bytes):
|
||||
l = l.decode('utf-8')
|
||||
if l[-1] == '\n':
|
||||
l = l[:-1]
|
||||
debug(f'stream response line={l},{type(l)}')
|
||||
l = ''.join(l.split('\n'))
|
||||
if l and l != '[DONE]':
|
||||
yield_it = False
|
||||
d = {}
|
||||
try:
|
||||
d = json.loads(l)
|
||||
except Exception as e:
|
||||
debug(f'json.loads({l}) error({e})')
|
||||
continue
|
||||
if d.get('reasoning_content'):
|
||||
txt += d.get('reasoning_content')
|
||||
yield_it = True
|
||||
if d.get('content'):
|
||||
txt = txt + d['content']
|
||||
yield_it = True
|
||||
if d.get('usage'):
|
||||
usage = d['usage']
|
||||
d['llmusageid'] = luid
|
||||
outlines.append(d)
|
||||
yield json.dumps(d, ensure_ascii=False) + '\n'
|
||||
if usage is None:
|
||||
error(f'{llm=} response has not usage')
|
||||
finish_seconds = time.time() - start_timestamp
|
||||
if responsed_seconds is None:
|
||||
responsed_seconds = finish_seconds
|
||||
llmusage = DictObject()
|
||||
llmusage.id = luid
|
||||
llmusage.llmid = llm.id
|
||||
llmusage.use_date = curDateString()
|
||||
llmusage.use_time = timestampstr()
|
||||
llmusage.userid = callerid
|
||||
llmusage.usages = json.dumps(usage, ensure_ascii=False, indent=4)
|
||||
debug(f' {usage=}, {type(usage)=}, {llmusage.usages=}')
|
||||
ioinfo = {
|
||||
"input": params_kw,
|
||||
'output': outlines
|
||||
}
|
||||
webpath = await write_llmio(llmusage.id, ioinfo)
|
||||
llmusage.ioinfo = webpath
|
||||
llmusage.transno = params_kw.transno
|
||||
llmusage.responsed_seconds = responsed_seconds
|
||||
llmusage.finish_seconds = finish_seconds
|
||||
llmusage.status = 'SUCCEEDED'
|
||||
llmusage.userorgid = callerorgid
|
||||
llmusage.ownerid = llm.ownerid
|
||||
llmusage.accounting_status = 'created'
|
||||
await write_llmusage(llmusage)
|
||||
except Exception as e:
|
||||
exception(f'{e=},{format_exc()}')
|
||||
estr = erase_apikey(e)
|
||||
ed = {"error": f"ERROR:{estr}", "status": "FAILED" ,"llmusageid": luid}
|
||||
s = json.dumps(ed, ensure_ascii=False)
|
||||
s = ''.join(s.split('\n'))
|
||||
outlines.append(ed)
|
||||
yield f'{s}\n'
|
||||
return
|
||||
|
||||
async def inference_generator(request, *args, params_kw=None, **kw):
|
||||
env = request._run_ns.copy()
|
||||
callerorgid = await env.get_userorgid()
|
||||
callerid = await env.get_user()
|
||||
async for d in _inference_generator(request, callerid,
|
||||
callerorgid, params_kw=params_kw, **kw):
|
||||
yield d
|
||||
|
||||
async def _inference_generator(request, callerid, callerorgid,
|
||||
params_kw={}, **kw):
|
||||
env = request._run_ns
|
||||
if not params_kw:
|
||||
params_kw = env.params_kw
|
||||
if not params_kw.transno:
|
||||
params_kw.transno = getID()
|
||||
llmid = params_kw.llmid
|
||||
f = None
|
||||
llm = await get_llm(llmid)
|
||||
if llm is None:
|
||||
errmsg = f'{{"status": "FAILED", "error":"llmid:{llmid}没找到模型"}}\n'
|
||||
exception(errmsg)
|
||||
yield errmsg
|
||||
return
|
||||
if not params_kw.model:
|
||||
params_kw.model = llm.model
|
||||
if llm.stream == 'async':
|
||||
if llm.callbackurl:
|
||||
cb_url = env.entire_url(llm.callbackurl)
|
||||
params_kw.callbackurl = cb_url
|
||||
f = partial(async_uapi_request, request, llm, callerid, callerorgid, params_kw=params_kw)
|
||||
elif not params_kw.stream:
|
||||
llm.stream = False
|
||||
debug(f'---{params_kw.stream=}, {llm.stream=} ---use sync_uapi_request ')
|
||||
f = partial(sync_uapi_request, request, llm, callerid, callerorgid, params_kw=params_kw)
|
||||
else:
|
||||
llm.stream = True
|
||||
debug(f'---{params_kw.stream=}, {llm.stream=} ---use uapi_request ')
|
||||
f = partial(uapi_request, request, llm, callerid, callerorgid, params_kw=params_kw)
|
||||
async for d in f():
|
||||
yield d
|
||||
|
||||
async def inference(request, *args, params_kw=None, **kw):
|
||||
env = request._run_ns.copy()
|
||||
f = partial(inference_generator, request, *args, params_kw=params_kw, **kw)
|
||||
return await env.stream_response(request, f)
|
||||
|
||||
@ -1,69 +0,0 @@
|
||||
from appPublic.myTE import MyTemplateEngine
|
||||
|
||||
def default_sysmessage():
|
||||
return """{
|
||||
"role":"system",
|
||||
"content":"{{content}}"
|
||||
}"""
|
||||
|
||||
def default_usrmessage():
|
||||
return """{
|
||||
"role":"user",
|
||||
"content":"{{prompt}}"
|
||||
}"""
|
||||
|
||||
def default_llmmessage():
|
||||
return """{
|
||||
"role":"assisant",
|
||||
"content":"{{content}}"
|
||||
}"""
|
||||
|
||||
class BaseMessages:
|
||||
def __init__(self, request, llmid, sys_message, usr_message, llm_message):
|
||||
self.request = request
|
||||
self.llmid = llmid
|
||||
self.sys_message = sys_message
|
||||
self.usr_message = usr_message
|
||||
self.llm_message = llm_message
|
||||
self.te = MyTemplateEngine([])
|
||||
|
||||
async def append_meessages(self, msg_format, **kw):
|
||||
m = self.te.renders(msg_format, kw)
|
||||
msgs = await self.get_messages()
|
||||
msgs.append(m)
|
||||
await self.set_message(msgs)
|
||||
return msgs
|
||||
|
||||
async def append_usr_messages(self, **kw):
|
||||
return await self.append_messages(self.usr_message, **kw)
|
||||
|
||||
async def append_sys_messages(self, **kw):
|
||||
return await self.append_messages(self.sys_message, **kw)
|
||||
|
||||
async def append_llm_messages(self, **kw):
|
||||
return await self.append_messages(self.llm_message, **kw)
|
||||
|
||||
async def get_messages(self):
|
||||
return []
|
||||
|
||||
async def set_messages(self, msgs):
|
||||
pass
|
||||
|
||||
class SessionMessages(BaseMessages):
|
||||
async def get_messages(self):
|
||||
env = self.request._run_ns
|
||||
s = await env.get_session()
|
||||
userid = await env.get_user()
|
||||
mk = f'{self.llmid}_{userid}_msgs'
|
||||
msgs = s[mk]
|
||||
if not msgs:
|
||||
msgs = []
|
||||
return msgs
|
||||
|
||||
async def set_messages(self, msgs):
|
||||
env = self.request._run_ns
|
||||
s = await env.get_session()
|
||||
userid = await env.get_user()
|
||||
mk = f'{self.llmid}_{userid}_msgs'
|
||||
s[mk] = msgs
|
||||
|
||||
@ -1,98 +0,0 @@
|
||||
import json
|
||||
import time
|
||||
import asyncio
|
||||
from random import randint
|
||||
from functools import partial
|
||||
from traceback import format_exc
|
||||
from sqlor.dbpools import DBPools, get_sor_context
|
||||
from appPublic.log import debug, exception, error
|
||||
from appPublic.uniqueID import getID
|
||||
from appPublic.dictObject import DictObject
|
||||
from appPublic.timeUtils import curDateString, timestampstr
|
||||
from appPublic.base64_to_file import base64_to_file, getFilenameFromBase64
|
||||
# from uapi.appapi import UAPI, sor_get_callerid, sor_get_uapi
|
||||
from ahserver.serverenv import get_serverenv, ServerEnv
|
||||
from ahserver.filestorage import FileStorage
|
||||
from .accounting import llm_accounting, llm_charging
|
||||
from .utils import *
|
||||
|
||||
async def sync_uapi_request(request, llm, callerid, callerorgid, params_kw=None):
|
||||
env = request._run_ns.copy()
|
||||
if not params_kw:
|
||||
params_kw = env.params_kw
|
||||
# callerid = await env.get_user()
|
||||
# callerorgid = await env.get_userorgid()
|
||||
# uapi = UAPI(request, sor=sor)
|
||||
uapi = env.UpAppApi(request)
|
||||
userid = await env.uapi_data.get_calluserid(llm.upappid, orgid=llm.ownerid)
|
||||
outlines = []
|
||||
b = None
|
||||
d = None
|
||||
luid = getID()
|
||||
try:
|
||||
start_timestamp = time.time()
|
||||
responsed_seconds = None
|
||||
finish_seconds = None
|
||||
b = await uapi.call(llm.upappid, llm.apiname, userid, params=params_kw)
|
||||
if isinstance(b, bytes):
|
||||
b = b.decode('utf-8')
|
||||
d = json.loads(b)
|
||||
status = d.get('status')
|
||||
usage = d.get('usage')
|
||||
if status and status != 'SUCCEEDED':
|
||||
raise Exception(d['error'])
|
||||
responsed_seconds = time.time() - start_timestamp
|
||||
finish_seconds = responsed_seconds
|
||||
llmusage = DictObject()
|
||||
llmusage.id = luid
|
||||
llmusage.llmid = llm.id
|
||||
llmusage.use_date = curDateString()
|
||||
llmusage.use_time = timestampstr()
|
||||
llmusage.userid = callerid
|
||||
llmusage.usages = json.dumps(usage, ensure_ascii=False)
|
||||
ioinfo = {
|
||||
"input": params_kw,
|
||||
'output': [d]
|
||||
}
|
||||
webpath = await write_llmio(llmusage.id, ioinfo)
|
||||
llmusage.ioinfo = webpath
|
||||
llmusage.transno = params_kw.transno
|
||||
llmusage.responsed_seconds = responsed_seconds
|
||||
llmusage.finish_seconds = finish_seconds
|
||||
llmusage.status = 'SUCCEEDED'
|
||||
llmusage.amount = llmusage.cost = 0.00
|
||||
""" 联机不记账
|
||||
if llm.ppid:
|
||||
try:
|
||||
charging = await llm_charging(llm.ppid, llmusage)
|
||||
if charging:
|
||||
llmusage.amount = charging.amount
|
||||
llmusage.cost = charging.cost
|
||||
else:
|
||||
llmusage.amount = llmusage.cost = 0.0
|
||||
except Exception as e:
|
||||
e = Exception(f'{llm.pid} charging error{e}')
|
||||
exception(f'{e}')
|
||||
else:
|
||||
llmusage.amount = 0
|
||||
llmusage.cost = 0
|
||||
"""
|
||||
llmusage.userorgid = callerorgid
|
||||
llmusage.ownerid = llm.ownerid
|
||||
llmusage.accounting_status = 'created'
|
||||
b = json.dumps(d, ensure_ascii=False)
|
||||
yield b
|
||||
await write_llmusage(llmusage)
|
||||
"""联机不记账
|
||||
if llmusage.amount > 0.0001:
|
||||
await llm_accounting(llmusage)
|
||||
"""
|
||||
except Exception as e:
|
||||
exception(f'{e=},{format_exc()}, {b=}')
|
||||
estr = erase_apikey(e)
|
||||
ed = {"error": f"ERROR:{estr}", "status": "FAILED" ,"llmusageid": luid}
|
||||
s = json.dumps(ed, ensure_ascii=False)
|
||||
s = ''.join(s.split('\n'))
|
||||
outlines.append(ed)
|
||||
yield f'{s}\n'
|
||||
|
||||
@ -1,348 +0,0 @@
|
||||
import json
|
||||
import time
|
||||
import asyncio
|
||||
import aiofiles
|
||||
from random import randint
|
||||
from functools import partial
|
||||
from traceback import format_exc
|
||||
from sqlor.dbpools import DBPools, get_sor_context
|
||||
from appPublic.log import debug, exception, error, critical
|
||||
from appPublic.uniqueID import getID
|
||||
from appPublic.dictObject import DictObject
|
||||
from appPublic.timeUtils import curDateString, timestampstr
|
||||
from uapi.appapi import UAPI, sor_get_callerid, sor_get_uapi
|
||||
from ahserver.serverenv import get_serverenv, ServerEnv
|
||||
from ahserver.filestorage import FileStorage
|
||||
from appPublic.jsonConfig import getConfig
|
||||
from appPublic.streamhttpclient import StreamHttpClient
|
||||
|
||||
async def update_llmusage(ns):
|
||||
env = ServerEnv()
|
||||
async with get_sor_context(env, 'llmage') as sor:
|
||||
await sor.U('llmusage', ns)
|
||||
|
||||
async def get_user_tpac(userid):
|
||||
env = ServerEnv()
|
||||
config = getConfig()
|
||||
async with get_sor_context(env, 'rbac') as sor:
|
||||
recs = await sor.R('users', {'id': userid})
|
||||
if recs:
|
||||
tpac = config.tpacs.get(recs[0].sync_from)
|
||||
return tpac
|
||||
return None
|
||||
|
||||
async def get_tpac_balance(tpac, userid):
|
||||
url = tpac.get_tpac_balance_url
|
||||
hc = StreamHttpClient()
|
||||
try:
|
||||
b = await hc.request('GET', url, params={'userid': userid})
|
||||
if b:
|
||||
d = json.loads(b.decode('utf-8'))
|
||||
if d['status'] == 'ok':
|
||||
return d['balance']
|
||||
exception(f'{url=}, {userid=}, {b} error')
|
||||
return None
|
||||
except Exception as e:
|
||||
exception(f'{url=}, {userid=}, error:{e}')
|
||||
return None
|
||||
|
||||
async def tpac_accounting(tpac, userid, llmid, amount, usage, luid):
|
||||
url = tpac.tpac_accounting_url
|
||||
hc = StreamHttpClient()
|
||||
d = {
|
||||
'userid': userid,
|
||||
'llmid': llmid,
|
||||
'amount': amount,
|
||||
'usage': usage
|
||||
}
|
||||
status = 'failed'
|
||||
try:
|
||||
b = await hc.request('POST', url, data=d)
|
||||
d = json.loads(b.decode('utf-8'))
|
||||
if d['status'] == 'ok':
|
||||
debug(f'{d=}')
|
||||
await update_llmusage({'id': luid, 'accounting_status': 'accounted'})
|
||||
return
|
||||
raise Exception(f'{d} tpac accounting error')
|
||||
except Exception as e:
|
||||
exception(f'{userid=}, {llmid=}, {amount=}, {usage=} tpac accounting error:{e}')
|
||||
raise e
|
||||
|
||||
async def append_new_llmoutput(webpath, output):
|
||||
fs = FileStorage()
|
||||
p = fs.realPath(webpath)
|
||||
if isinstance(output, str):
|
||||
output = json.loads(output)
|
||||
bin = await read_webpath(webpath)
|
||||
io = json.loads(bin.decode('utf-8'))
|
||||
io['output'].append(output)
|
||||
async with aiofiles.open(p, 'wb') as f:
|
||||
iostr = json.dumps(io, ensure_ascii=False, indent=4)
|
||||
await f.write(iostr.encode('utf-8'))
|
||||
|
||||
async def get_lastoutput(webpath):
|
||||
bin = await read_webpath(webpath)
|
||||
io = json.loads(bin.decode('utf-8'))
|
||||
return io['output'][-1]
|
||||
|
||||
async def read_webpath(webpath):
|
||||
fs = FileStorage()
|
||||
p = fs.realPath(webpath)
|
||||
async with aiofiles.open(p,'rb') as f:
|
||||
bin = await f.read()
|
||||
return bin
|
||||
|
||||
async def write_llmio(luid, io_dic):
|
||||
fs = FileStorage()
|
||||
s = io_dic
|
||||
if not isinstance(io_dic, str):
|
||||
s = json.dumps(io_dic, ensure_ascii=False, indent=4)
|
||||
name = f'{luid}.json'
|
||||
webpath = await fs.save(name, s, userid='llmio')
|
||||
return webpath
|
||||
|
||||
async def llm_query_orders(userorgid, page, pagerows=80):
|
||||
env = ServerEnv()
|
||||
async with get_sor_context(env, 'llmage') as sor:
|
||||
sql = """select a.llmid,
|
||||
a.use_date,
|
||||
a.use_time,
|
||||
a.userid,
|
||||
a.usages,
|
||||
a.status,
|
||||
a.amount,
|
||||
a.userorgid,
|
||||
a.accounting_status,
|
||||
b.name,
|
||||
b.model
|
||||
from llmusage a, llm b
|
||||
where userorgid = ${userorgid}$
|
||||
and a.llmid = b.id
|
||||
"""
|
||||
ns = dict(
|
||||
page=page,
|
||||
pagerows=pagerows,
|
||||
sort="use_time desc",
|
||||
userorgid=userorgid)
|
||||
|
||||
data = await sor.sqlExe(sql, ns)
|
||||
return data
|
||||
return {'total': 0, 'rows':[]}
|
||||
|
||||
async def get_llm_by_model(id, lctype=None):
|
||||
env = ServerEnv()
|
||||
async with get_sor_context(env, 'llmage') as sor:
|
||||
sql = 'select * from llm where model=${model}$'
|
||||
recs = await sor.R('llm', {'model': model})
|
||||
return recs
|
||||
|
||||
def erase_apikey(e):
|
||||
e = str(e)
|
||||
ss = e.split('Bearer ')
|
||||
if len(ss) < 2:
|
||||
return e
|
||||
|
||||
for i, c in enumerate(ss[1]):
|
||||
if c in ['"', "'"]:
|
||||
newb = "XXXXXXXX" + ss[1][i:]
|
||||
break
|
||||
return ss[0] + 'Bearer ' + newb
|
||||
|
||||
async def get_llmproviders():
|
||||
env = ServerEnv()
|
||||
async with get_sor_context(env, 'llmage') as sor:
|
||||
sql = """select a.providerid, a.iconid, b.orgname
|
||||
from llm a, organization b
|
||||
where a.providerid = b.id
|
||||
group by a.providerid, a.iconid, b.orgname"""
|
||||
return await sor.sqlExe(sql, {})
|
||||
return []
|
||||
|
||||
async def get_llms_sort_by_provider():
|
||||
env = ServerEnv()
|
||||
async with get_sor_context(env, 'llmage') as sor:
|
||||
today = curDateString()
|
||||
sql = """select a.*, b.orgname from llm a, organization b
|
||||
where a.enabled_date <= ${today}$
|
||||
and a.expired_date > ${today}$
|
||||
and a.providerid = b.id
|
||||
order by a.providerid, a.id
|
||||
"""
|
||||
recs = await sor.sqlExe(sql, {'today': today})
|
||||
d = []
|
||||
x = None
|
||||
oldpid = '-111'
|
||||
for l in recs:
|
||||
if l.providerid != oldpid:
|
||||
x = {
|
||||
'id': l.providerid,
|
||||
'orgname': l.orgname,
|
||||
'llms': [l]
|
||||
}
|
||||
d.append(x)
|
||||
oldpid = l.providerid
|
||||
else:
|
||||
x['llms'].append(l)
|
||||
return d
|
||||
return []
|
||||
|
||||
async def get_llmcatelogs():
|
||||
db = DBPools()
|
||||
dbname = get_serverenv('get_module_dbname')('llmage')
|
||||
async with db.sqlorContext(dbname) as sor:
|
||||
recs = await sor.R('llmcatelog', {})
|
||||
return recs
|
||||
|
||||
return []
|
||||
|
||||
async def get_llms_by_catelog_to_customer(catelogid=None, orderby='providerid'):
|
||||
env = ServerEnv()
|
||||
async with get_sor_context(env, 'llmage') as sor:
|
||||
today = curDateString()
|
||||
# Join with llm_api_map to get catalog relationship
|
||||
sql = """select distinct a.*,
|
||||
b.name as catelogname,
|
||||
m.llmcatelogid as catelog_id,
|
||||
m.apiname,
|
||||
m.query_apiname,
|
||||
m.query_period,
|
||||
m.ppid
|
||||
from llm a
|
||||
join llm_api_map m on a.id = m.llmid
|
||||
join llmcatelog b on m.llmcatelogid = b.id
|
||||
where a.enabled_date <= ${today}$
|
||||
and m.ppid is not null
|
||||
and a.expired_date > ${today}$
|
||||
"""
|
||||
sortstr='catelog_id, ' + orderby
|
||||
params = {'today': today, 'sort': sortstr}
|
||||
if catelogid:
|
||||
sql += " and m.llmcatelogid = ${catelogid}$"
|
||||
params['catelogid'] = catelogid
|
||||
|
||||
debug(f'{sql=}')
|
||||
recs = await sor.sqlExe(sql, params.copy())
|
||||
debug(f'{sql=}, {recs=}, {params=}')
|
||||
d = []
|
||||
cid = ''
|
||||
x = None
|
||||
for r in recs:
|
||||
if cid != r.catelog_id:
|
||||
x = {
|
||||
'catelogid': r.catelog_id,
|
||||
'catelogname': r.catelogname,
|
||||
'llms': [r]
|
||||
}
|
||||
d.append(x)
|
||||
cid = r.catelog_id
|
||||
else:
|
||||
x['llms'].append(r)
|
||||
return d
|
||||
return []
|
||||
|
||||
async def get_llms_by_catelog(catelogid=None, orderby='providerid'):
|
||||
env = ServerEnv()
|
||||
async with get_sor_context(env, 'llmage') as sor:
|
||||
today = curDateString()
|
||||
# Join with llm_api_map to get catalog relationship
|
||||
sql = """select distinct a.*, b.name as catelogname, m.llmcatelogid as catelog_id
|
||||
from llm a
|
||||
join llm_api_map m on a.id = m.llmid
|
||||
join llmcatelog b on m.llmcatelogid = b.id
|
||||
where a.enabled_date <= ${today}$
|
||||
and a.expired_date > ${today}$"""
|
||||
params = {'today': today, 'sort': orderby}
|
||||
if catelogid:
|
||||
sql += " and m.llmcatelogid = ${catelogid}$"
|
||||
params['catelogid'] = catelogid
|
||||
|
||||
sql += " order by m.llmcatelogid, a.id"
|
||||
|
||||
recs = await sor.sqlExe(sql, params)
|
||||
d = []
|
||||
cid = ''
|
||||
x = None
|
||||
for r in recs:
|
||||
if cid != r.catelog_id:
|
||||
x = {
|
||||
'catelogid': r.catelog_id,
|
||||
'catelogname': r.catelogname,
|
||||
'llms': [r]
|
||||
}
|
||||
d.append(x)
|
||||
cid = r.catelog_id
|
||||
else:
|
||||
x['llms'].append(r)
|
||||
return d
|
||||
return []
|
||||
|
||||
async def get_llm(llmid, catelogid=None):
|
||||
today = curDateString()
|
||||
env = ServerEnv()
|
||||
async with get_sor_context(env, 'llmage') as sor:
|
||||
sql = """select a.id,
|
||||
a.name,
|
||||
a.model,
|
||||
a.providerid,
|
||||
a.description,
|
||||
a.iconid,
|
||||
a.upappid,
|
||||
a.ownerid,
|
||||
a.min_balance,
|
||||
m.llmcatelogid,
|
||||
m.apiname,
|
||||
m.query_apiname,
|
||||
m.query_period,
|
||||
m.ppid,
|
||||
e.ioid,
|
||||
e.stream,
|
||||
e.callbackurl,
|
||||
f.input_fields,
|
||||
lc.name as catelogname
|
||||
from llm a
|
||||
,llm_api_map m
|
||||
,llmcatelog lc
|
||||
,upapp c
|
||||
,uapi e
|
||||
,uapiio f
|
||||
where a.id = m.llmid
|
||||
and a.upappid = c.id
|
||||
and c.id = e.upappid
|
||||
and m.apiname = e.name
|
||||
and e.ioid = f.id
|
||||
and a.id = ${llmid}$
|
||||
and a.expired_date > ${today}$
|
||||
and a.enabled_date <= ${today}$
|
||||
"""
|
||||
ns = {'llmid': llmid, 'today': today}
|
||||
if catelogid:
|
||||
sql += ' and m.llmcatelogid = ${catelogid}$ '
|
||||
ns['catelogid'] = catelogid
|
||||
else:
|
||||
sql += " and m.isdefaultcatelog = '1'"
|
||||
recs = await sor.sqlExe(sql, ns.copy())
|
||||
if len(recs) > 0:
|
||||
r = recs[0]
|
||||
return r
|
||||
else:
|
||||
debug(f'{llmid=} not found, {ns=}, {sql=}')
|
||||
return None
|
||||
exception(f'Error: {format_exc()}')
|
||||
return None
|
||||
|
||||
|
||||
async def write_llmusage(llmusage):
|
||||
env = ServerEnv()
|
||||
async with get_sor_context(env, 'llmage') as sor:
|
||||
await sor.C('llmusage', llmusage)
|
||||
|
||||
async def llm_query_price(llmid, config_data):
|
||||
env = ServerEnv()
|
||||
llm = await get_llm(llmid)
|
||||
if llm.ppid is None:
|
||||
e = Exception(f'{llm=} ppid is None')
|
||||
exception(f'{e}')
|
||||
raise e
|
||||
prices = await env.buffered_charging(llm.ppid, config_data)
|
||||
return prices
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user