dev: add usage check hook for ai services

This commit is contained in:
KernelDeimos
2024-11-27 16:08:56 -05:00
parent 237dc94377
commit d2c1878ed4
3 changed files with 56 additions and 1 deletions

View File

@@ -365,6 +365,11 @@ module.exports = class APIError {
message: ({ method_key, limit }) =>
`Monthly limit exceeded for method ${quot(method_key)}: ${limit} requests per month.`,
},
'monthly_usage_exceeded': {
status: 429,
message: ({ limit, unit }) =>
`Monthly limit exceeded: ${limit} ${unit} per month.`,
},
'server_rate_exceeded': {
status: 503,
message: 'System-wide rate limit exceeded. Please try again later.',

View File

@@ -1,3 +1,5 @@
const APIError = require("../../api/APIError");
const { PermissionUtil } = require("../../services/auth/PermissionService");
const BaseService = require("../../services/BaseService");
const { DB_WRITE } = require("../../services/database/consts");
const { TypeSpec } = require("../../services/drivers/meta/Construct");
@@ -28,7 +30,7 @@ class AIChatService extends BaseService {
svc_event.on('ai.prompt.report-usage', async (_, details) => {
const values = {
user_id: details.actor?.type?.user?.id,
app_id: details.actor?.type?.app?.id,
app_id: details.actor?.type?.app?.id ?? null,
service_name: details.service_used,
model_name: details.model_used,
value_uint_1: details.usage?.input_tokens,
@@ -196,6 +198,11 @@ class AIChatService extends BaseService {
let ret, error, errors = [];
let service_used = intended_service;
let model_used = this.get_model_from_request(parameters);
await this.check_usage_({
actor: Context.get('actor'),
service: service_used,
model: model_used,
});
try {
ret = await svc_driver.call_new_({
actor: Context.get('actor'),
@@ -239,6 +246,11 @@ class AIChatService extends BaseService {
fallback_model_name
});
await this.check_usage_({
actor: Context.get('actor'),
service: fallback_service_name,
model: fallback_model_name,
});
try {
ret = await svc_driver.call_new_({
actor: Context.get('actor'),
@@ -318,6 +330,35 @@ class AIChatService extends BaseService {
}
}
async check_usage_ ({ actor, service, model }) {
const svc_permission = this.services.get('permission');
const svc_event = this.services.get('event');
const reading = await svc_permission.scan(actor, `paid-services:ai-chat`);
const options = PermissionUtil.reading_to_options(reading);
// Query current ai usage in terms of cost
const [row] = await this.db.read(
'SELECT SUM(`cost`) AS sum FROM `ai_usage` ' +
'WHERE `user_id` = ?',
[actor.type.user.id]
);
const cost_used = row?.sum || 0;
const event = {
allowed: true,
actor,
service, model,
cost_used,
permission_options: options,
};
await svc_event.emit('ai.prompt.check-usage', event);
if ( event.error ) throw event.error;
if ( ! event.allowed ) {
throw new APIError('forbidden');
}
}
async moderate ({ messages }) {
const svc_openai = this.services.get('openai-completion');

View File

@@ -44,7 +44,15 @@ module.exports = eggspress('/drivers/usage', {
user: {}, // map[str(iface:method)]{date,count,max}
apps: {}, // []{app,map[str(iface:method)]{date,count,max}}
app_objects: {},
usages: [],
};
const event = {
usages: [],
};
const svc_event = x.get('services').get('event');
await svc_event.emit('usages.query', event);
usages.usages = event.usages;
const rows = await db.read(
'SELECT * FROM `service_usage_monthly` WHERE user_id=? ' +
@@ -184,5 +192,6 @@ module.exports = eggspress('/drivers/usage', {
user: Object.values(usages.user),
apps: usages.apps,
app_objects: usages.app_objects,
usages: usages.usages,
});
})