update
This commit is contained in:
@@ -2,7 +2,9 @@ import { app } from '@/app.ts'
|
||||
import { asr } from './modules/index.ts'
|
||||
app.route({
|
||||
path: 'asr',
|
||||
key: 'text'
|
||||
key: 'text',
|
||||
middleware: ['auth'],
|
||||
description: '语音转文字,将base64的音频数据转换为文字, 参数: base64Audio, 为base64编码的音频数据',
|
||||
}).define(async (ctx) => {
|
||||
const base64Audio = ctx.query.base64Audio as string
|
||||
if (!base64Audio) {
|
||||
|
||||
@@ -152,7 +152,12 @@ app
|
||||
const tokenUser = ctx.state.tokenUser;
|
||||
let isUser = !!tokenUser;
|
||||
ctx.body = {
|
||||
list: app.router.routes.map((item) => {
|
||||
list: app.router.routes.filter(item => {
|
||||
if (item.id === 'auth' || item.id === 'auth-can' || item.id === 'check-auth-admin' || item.id === 'auth-admin') {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}).map((item) => {
|
||||
return {
|
||||
id: item.id,
|
||||
path: item.path,
|
||||
|
||||
51
src/routes/ai/cost.ts
Normal file
51
src/routes/ai/cost.ts
Normal file
@@ -0,0 +1,51 @@
|
||||
import { redis } from '@/app.ts'
|
||||
|
||||
export class AICost {
|
||||
/**
|
||||
* 获取余额
|
||||
* @param userId
|
||||
* @returns
|
||||
*/
|
||||
static async getAmount(userId: string): Promise<number> {
|
||||
const cost = await redis.get(`ai:cost:amount:${userId}`);
|
||||
if (!cost) {
|
||||
return 2 * 100 * 10000;// 默认200万调用量 等于 2M 的额度,每一个月的初始额度,每一个月进行重置。
|
||||
}
|
||||
return cost ? parseFloat(cost) : 0;
|
||||
}
|
||||
/**
|
||||
* 扣除余额
|
||||
* @param userId
|
||||
* @param amount
|
||||
*/
|
||||
static async decuctAmount(userId: string, amount: number): Promise<void> {
|
||||
const key = `ai:cost:amount:${userId}`;
|
||||
const current = await this.getAmount(userId);
|
||||
const newAmount = Math.max(0, current - amount);
|
||||
await redis.set(key, newAmount.toString());
|
||||
}
|
||||
/**
|
||||
* 检查余额是否足够
|
||||
* @param userId
|
||||
* @returns
|
||||
*/
|
||||
static async checkAmount(userId: string): Promise<boolean> {
|
||||
const current = await this.getAmount(userId);
|
||||
return current >= 10; // 最低余额10
|
||||
}
|
||||
/**
|
||||
* 增加余额
|
||||
* @param userId
|
||||
* @param amount
|
||||
*/
|
||||
static async addAmount(userId: string, amount: number): Promise<void> {
|
||||
const key = `ai:cost:amount:${userId}`;
|
||||
const current = await this.getAmount(userId);
|
||||
const newAmount = current + amount;
|
||||
await redis.set(key, newAmount.toString());
|
||||
}
|
||||
static async setAmount(userId: string, amount: number): Promise<void> {
|
||||
const key = `ai:cost:amount:${userId}`;
|
||||
await redis.set(key, amount.toString());
|
||||
}
|
||||
}
|
||||
121
src/routes/ai/index.ts
Normal file
121
src/routes/ai/index.ts
Normal file
@@ -0,0 +1,121 @@
|
||||
import { app, ai } from '@/app.ts';
|
||||
import { AICost } from './cost.ts';
|
||||
|
||||
const description = `ai调用工具,参数说明:
|
||||
- model: 使用的模型,默认qwen-plus,可选qwen-plus、qwen-flash、qwen-max、qwen-coder-plus等
|
||||
- messages: 消息数组,格式为[{role: 'user'|'assistant'|'system', content: string}],与OpenAI的chat-completions接口一致
|
||||
- question: 兼容提问参数,如果传入messages则忽略question参数
|
||||
- isJson: 是否只返回json内容,默认false,如果为true则会尝试从AI返回的内容中提取json对象返回`;
|
||||
|
||||
app.route({
|
||||
path: 'ai',
|
||||
key: '',
|
||||
description,
|
||||
middleware: ['auth']
|
||||
}).define(async (ctx) => {
|
||||
const query = ctx.query || {};
|
||||
const tokenUser = ctx.state.tokenUser;
|
||||
const userId = tokenUser.id;
|
||||
const isJson = query.isJson ?? false;
|
||||
const model = query.model || 'qwen-plus';
|
||||
const hasEnough = await AICost.checkAmount(userId);
|
||||
if (!hasEnough) {
|
||||
ctx.throw(402, 'token 余额不足,请充值后重试');
|
||||
}
|
||||
const messages = query.messages || [];
|
||||
if (messages.length === 0 && query.question) {
|
||||
messages.push({ role: 'user', content: query.question });
|
||||
} else if (messages.length === 0) {
|
||||
ctx.throw(400, 'messages不能为空');
|
||||
}
|
||||
await ai.chat(messages, { model, messages });
|
||||
const text = ai.responseText;
|
||||
const cost = ai.total_tokens;
|
||||
let payload: any = undefined;
|
||||
await AICost.decuctAmount(userId, cost);
|
||||
if (isJson) {
|
||||
payload = ai.utils.extractJsonFromMarkdown(text);
|
||||
}
|
||||
|
||||
ctx.body = {
|
||||
text,
|
||||
cost: cost,
|
||||
model,
|
||||
payload
|
||||
}
|
||||
}).addTo(app)
|
||||
|
||||
app.route({
|
||||
path: 'ai',
|
||||
key: 'cost',
|
||||
description: '获取AI调用余额',
|
||||
middleware: ['auth']
|
||||
}).define(async (ctx) => {
|
||||
const tokenUser = ctx.state.tokenUser;
|
||||
const userId = tokenUser.id;
|
||||
const amount = await AICost.getAmount(userId);
|
||||
ctx.body = {
|
||||
amount
|
||||
}
|
||||
}).addTo(app)
|
||||
|
||||
app.route({
|
||||
path: 'ai',
|
||||
key: 'cost/get',
|
||||
description: '检查AI调用余额是否足够',
|
||||
middleware: ['auth-admin']
|
||||
}).define(async (ctx) => {
|
||||
const userId = ctx.query.userId || '';
|
||||
if (!userId) {
|
||||
ctx.throw(400, 'userId不能为空');
|
||||
}
|
||||
const amount = await AICost.getAmount(userId);
|
||||
ctx.body = {
|
||||
amount
|
||||
}
|
||||
}).addTo(app)
|
||||
|
||||
|
||||
app.route({
|
||||
path: 'ai',
|
||||
key: 'cost/add',
|
||||
description: '增加AI调用余额, 需要传入userId和amount两个参数',
|
||||
middleware: ['auth-admin']
|
||||
}).define(async (ctx) => {
|
||||
const userId = ctx.query.userId || '';
|
||||
if (!userId) {
|
||||
ctx.throw(400, 'userId不能为空');
|
||||
}
|
||||
const amount = ctx.query.amount || 0;
|
||||
if (amount <= 0) {
|
||||
ctx.throw(400, 'amount必须大于0');
|
||||
}
|
||||
await AICost.addAmount(userId, amount);
|
||||
const newAmount = await AICost.getAmount(userId);
|
||||
ctx.body = {
|
||||
amount: newAmount
|
||||
}
|
||||
}).addTo(app)
|
||||
|
||||
app.route({
|
||||
path: 'ai',
|
||||
key: 'cost/set',
|
||||
description: '设置AI调用余额, 需要传入userId和amount两个参数',
|
||||
middleware: ['auth-admin']
|
||||
}).define(async (ctx) => {
|
||||
const userId = ctx.query.userId || '';
|
||||
if (!userId) {
|
||||
ctx.throw(400, 'userId不能为空');
|
||||
}
|
||||
|
||||
const amount = ctx.query.amount || 0;
|
||||
if (amount < 0) {
|
||||
ctx.throw(400, 'amount必须大于等于0');
|
||||
}
|
||||
|
||||
await AICost.setAmount(userId, amount);
|
||||
const newAmount = await AICost.getAmount(userId);
|
||||
ctx.body = {
|
||||
amount: newAmount
|
||||
}
|
||||
}).addTo(app)
|
||||
@@ -1,9 +1,5 @@
|
||||
import './container/index.ts';
|
||||
|
||||
import './user/index.ts';
|
||||
|
||||
import './github/index.ts';
|
||||
|
||||
import './app-manager/index.ts';
|
||||
|
||||
import './file/index.ts';
|
||||
@@ -12,6 +8,7 @@ import './micro-app/index.ts';
|
||||
|
||||
import './config/index.ts';
|
||||
|
||||
// import './mark/index.ts';
|
||||
import './file-listener/index.ts';
|
||||
|
||||
import './file-listener/index.ts';
|
||||
|
||||
import './ai/index.ts';
|
||||
Reference in New Issue
Block a user