main #118
@ -178,7 +178,7 @@ async def _query_model_usage_rows(sor, conditions, limit=None, offset=None):
|
|||||||
sql = """
|
sql = """
|
||||||
SELECT id, userid, llmid, original_price, orderid, bill_status, usage_content, created_at
|
SELECT id, userid, llmid, original_price, orderid, bill_status, usage_content, created_at
|
||||||
FROM model_usage
|
FROM model_usage
|
||||||
WHERE %s
|
WHERE %s AND bill_status != '0'
|
||||||
ORDER BY created_at DESC
|
ORDER BY created_at DESC
|
||||||
""" % where_clause
|
""" % where_clause
|
||||||
if limit is not None:
|
if limit is not None:
|
||||||
@ -188,7 +188,7 @@ async def _query_model_usage_rows(sor, conditions, limit=None, offset=None):
|
|||||||
|
|
||||||
async def _count_model_usage(sor, conditions):
|
async def _count_model_usage(sor, conditions):
|
||||||
where_clause = ' AND '.join(conditions) if conditions else '1 = 1'
|
where_clause = ' AND '.join(conditions) if conditions else '1 = 1'
|
||||||
sql = 'SELECT COUNT(*) AS total_count FROM model_usage WHERE %s' % where_clause
|
sql = 'SELECT COUNT(*) AS total_count FROM model_usage WHERE %s AND bill_status != "0"' % where_clause
|
||||||
return (await sor.sqlExe(sql, {}))[0]['total_count']
|
return (await sor.sqlExe(sql, {}))[0]['total_count']
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@ -95,7 +95,7 @@ def _normalize_usage_row(row, bill_amount_map=None):
|
|||||||
'prompt_tokens': int(usage.get('prompt_tokens') or 0),
|
'prompt_tokens': int(usage.get('prompt_tokens') or 0),
|
||||||
'completion_tokens': int(usage.get('completion_tokens') or 0),
|
'completion_tokens': int(usage.get('completion_tokens') or 0),
|
||||||
'total_tokens': int(usage.get('total_tokens') or 0),
|
'total_tokens': int(usage.get('total_tokens') or 0),
|
||||||
'amount': round(amount, 4),
|
'amount': round(amount, 2),
|
||||||
'bill_status': row.get('bill_status'),
|
'bill_status': row.get('bill_status'),
|
||||||
'orderid': orderid,
|
'orderid': orderid,
|
||||||
'usage_time': row.get('created_at'),
|
'usage_time': row.get('created_at'),
|
||||||
@ -155,7 +155,7 @@ async def _query_model_usage_rows(sor, conditions, limit=None, offset=None):
|
|||||||
sql = """
|
sql = """
|
||||||
SELECT id, userid, llmid, original_price, orderid, bill_status, usage_content, created_at
|
SELECT id, userid, llmid, original_price, orderid, bill_status, usage_content, created_at
|
||||||
FROM model_usage
|
FROM model_usage
|
||||||
WHERE %s
|
WHERE %s AND bill_status != '0'
|
||||||
ORDER BY created_at DESC
|
ORDER BY created_at DESC
|
||||||
""" % where_clause
|
""" % where_clause
|
||||||
if limit is not None:
|
if limit is not None:
|
||||||
@ -165,7 +165,7 @@ async def _query_model_usage_rows(sor, conditions, limit=None, offset=None):
|
|||||||
|
|
||||||
async def _count_model_usage(sor, conditions):
|
async def _count_model_usage(sor, conditions):
|
||||||
where_clause = ' AND '.join(conditions) if conditions else '1 = 1'
|
where_clause = ' AND '.join(conditions) if conditions else '1 = 1'
|
||||||
sql = 'SELECT COUNT(*) AS total_count FROM model_usage WHERE %s' % where_clause
|
sql = 'SELECT COUNT(*) AS total_count FROM model_usage WHERE %s AND bill_status != "0"' % where_clause
|
||||||
return (await sor.sqlExe(sql, {}))[0]['total_count']
|
return (await sor.sqlExe(sql, {}))[0]['total_count']
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@ -260,6 +260,13 @@ async def process_user_billing(ns={}):
|
|||||||
'status': 'error',
|
'status': 'error',
|
||||||
'msg': 'llmid必传'
|
'msg': 'llmid必传'
|
||||||
}
|
}
|
||||||
|
model_name = ns.get('model')
|
||||||
|
if not model_name:
|
||||||
|
debug(f"{userid} process_user_billing model必传")
|
||||||
|
return {
|
||||||
|
'status': 'error',
|
||||||
|
'msg': 'model必传'
|
||||||
|
}
|
||||||
|
|
||||||
try:
|
try:
|
||||||
amount = round(float(amount), 12)
|
amount = round(float(amount), 12)
|
||||||
@ -275,6 +282,9 @@ async def process_user_billing(ns={}):
|
|||||||
async with db.sqlorContext('kboss') as sor:
|
async with db.sqlorContext('kboss') as sor:
|
||||||
try:
|
try:
|
||||||
product_li = await sor.R('product', {'providerpid': llmid, 'del_flg': '0'})
|
product_li = await sor.R('product', {'providerpid': llmid, 'del_flg': '0'})
|
||||||
|
if not product_li:
|
||||||
|
product_li = await sor.R('product', {'product_code': model_name, 'del_flg': '0'})
|
||||||
|
|
||||||
if not product_li:
|
if not product_li:
|
||||||
debug(f"{userid} process_user_billing 未找到对应产品,请确认")
|
debug(f"{userid} process_user_billing 未找到对应产品,请确认")
|
||||||
return {
|
return {
|
||||||
|
|||||||
@ -296,6 +296,8 @@ export default {
|
|||||||
formatter: '{b}: {c} Token ({d}%)'
|
formatter: '{b}: {c} Token ({d}%)'
|
||||||
},
|
},
|
||||||
legend: {
|
legend: {
|
||||||
|
orient: 'horizontal',
|
||||||
|
left: 'center',
|
||||||
bottom: 0,
|
bottom: 0,
|
||||||
icon: 'circle',
|
icon: 'circle',
|
||||||
textStyle: {
|
textStyle: {
|
||||||
@ -307,11 +309,20 @@ export default {
|
|||||||
{
|
{
|
||||||
name: 'Token占比',
|
name: 'Token占比',
|
||||||
type: 'pie',
|
type: 'pie',
|
||||||
radius: ['52%', '72%'],
|
radius: ['55%', '70%'],
|
||||||
center: ['50%', '44%'],
|
center: ['50%', '48%'],
|
||||||
avoidLabelOverlap: true,
|
avoidLabelOverlap: true,
|
||||||
label: {
|
label: {
|
||||||
formatter: '{b}\n{d}%'
|
show: true,
|
||||||
|
position: 'outside',
|
||||||
|
formatter: '{b}\n{d}%',
|
||||||
|
lineHeight: 18,
|
||||||
|
fontSize: 12
|
||||||
|
},
|
||||||
|
labelLine: {
|
||||||
|
length: 8,
|
||||||
|
length2: 12,
|
||||||
|
smooth: true
|
||||||
},
|
},
|
||||||
data: hasData
|
data: hasData
|
||||||
? [
|
? [
|
||||||
@ -321,7 +332,7 @@ export default {
|
|||||||
: [{ name: '暂无数据', value: 1 }],
|
: [{ name: '暂无数据', value: 1 }],
|
||||||
itemStyle: {
|
itemStyle: {
|
||||||
borderColor: '#fff',
|
borderColor: '#fff',
|
||||||
borderWidth: 3
|
borderWidth: 2
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
@ -332,10 +343,11 @@ export default {
|
|||||||
const rankList = this.modelUsageRank.slice().reverse()
|
const rankList = this.modelUsageRank.slice().reverse()
|
||||||
this.modelRankChart.setOption({
|
this.modelRankChart.setOption({
|
||||||
grid: {
|
grid: {
|
||||||
top: 12,
|
top: 24,
|
||||||
right: 24,
|
right: 30,
|
||||||
bottom: 20,
|
bottom: 28,
|
||||||
left: 92
|
left: 100,
|
||||||
|
containLabel: false
|
||||||
},
|
},
|
||||||
tooltip: {
|
tooltip: {
|
||||||
trigger: 'axis',
|
trigger: 'axis',
|
||||||
@ -371,15 +383,16 @@ export default {
|
|||||||
axisTick: { show: false },
|
axisTick: { show: false },
|
||||||
axisLabel: {
|
axisLabel: {
|
||||||
color: '#606266',
|
color: '#606266',
|
||||||
width: 82,
|
width: 88,
|
||||||
overflow: 'truncate'
|
overflow: 'truncate',
|
||||||
|
margin: 8
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
series: [
|
series: [
|
||||||
{
|
{
|
||||||
name: 'Token消耗',
|
name: 'Token消耗',
|
||||||
type: 'bar',
|
type: 'bar',
|
||||||
barWidth: 12,
|
barWidth: 14,
|
||||||
data: rankList.map(item => item.total_tokens),
|
data: rankList.map(item => item.total_tokens),
|
||||||
itemStyle: {
|
itemStyle: {
|
||||||
color: new echarts.graphic.LinearGradient(0, 0, 1, 0, [
|
color: new echarts.graphic.LinearGradient(0, 0, 1, 0, [
|
||||||
@ -392,6 +405,7 @@ export default {
|
|||||||
show: true,
|
show: true,
|
||||||
position: 'right',
|
position: 'right',
|
||||||
color: '#344054',
|
color: '#344054',
|
||||||
|
fontSize: 11,
|
||||||
formatter: params => this.formatNumber(params.value)
|
formatter: params => this.formatNumber(params.value)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -657,11 +671,11 @@ export default {
|
|||||||
|
|
||||||
.chart-box {
|
.chart-box {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: 240px;
|
height: 260px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.model-rank-chart {
|
.model-rank-chart {
|
||||||
height: 300px;
|
height: 320px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.ratio-list,
|
.ratio-list,
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user