dev: record token-based usages

This commit is contained in:
KernelDeimos
2024-11-27 13:53:22 -05:00
parent e96bae5b7d
commit 9ddc84e6cf
4 changed files with 91 additions and 7 deletions

View File

@@ -1,4 +1,5 @@
const BaseService = require("../../services/BaseService");
const { DB_WRITE } = require("../../services/database/consts");
const { TypeSpec } = require("../../services/drivers/meta/Construct");
const { TypedValue } = require("../../services/drivers/meta/Runtime");
const { Context } = require("../../util/context");
@@ -21,16 +22,47 @@ class AIChatService extends BaseService {
_init () {
this.kvkey = this.modules.uuidv4();
this.db = this.services.get('database').get(DB_WRITE, 'ai-usage');
const svc_event = this.services.get('event');
svc_event.on('ai.prompt.report-usage', async (_, details) => {
const user_id = details.actor?.type?.user?.id;
const app_id = details.actor?.type?.app?.id;
const service_name = details.service_used;
const model_name = details.model_used;
const value_uint_1 = details.usage?.input_tokens;
const value_uint_2 = details.usage?.output_tokens;
const values = {
user_id: details.actor?.type?.user?.id,
app_id: details.actor?.type?.app?.id,
service_name: details.service_used,
model_name: details.model_used,
value_uint_1: details.usage?.input_tokens,
value_uint_2: details.usage?.output_tokens,
};
console.log('reporting usage', { user_id, app_id, service_name, model_name, value_uint_1, value_uint_2 }); });
let model_details = this.detail_model_map[details.model_used];
if ( Array.isArray(model_details) ) {
for ( const model of model_details ) {
if ( model.provider === details.service_used ) {
model_details = model;
break;
}
}
}
if ( Array.isArray(model_details) ) {
model_details = model_details[0];
}
if ( model_details ) {
values.cost = 0 + // for formatting
model_details.cost.input * details.usage.input_tokens
// cents/MTok tokens
+
model_details.cost.output * details.usage.output_tokens
// cents/MTok tokens
;
} else {
this.log.error('could not find model details', { details });
}
await this.db.insert('ai_usage', values);
});
}
async ['__on_boot.consolidation'] () {

View File

@@ -52,6 +52,21 @@ class BaseDatabaseAccessService extends BaseService {
return this._write(query, params);
}
insert (table_name, data) {
const values = Object.values(data);
const sql = this._gen_insert_sql(table_name, data);
console.log('INSERT SQL', sql);
return this.write(sql, values);
}
_gen_insert_sql (table_name, data) {
const cols = Object.keys(data);
return 'INSERT INTO `' + table_name + '` ' +
'(' + cols.map(str => '`' + str + '`').join(', ') + ') ' +
'VALUES (' + cols.map(() => '?').join(', ') + ')';
}
batch_write (statements) {
return this._batch_write(statements);
}

View File

@@ -141,6 +141,9 @@ class SqliteDatabaseAccessService extends BaseDatabaseAccessService {
[29, [
'0032_signup_metadata.sql',
]],
[30, [
'0033_ai-usage.sql',
]],
];
// Database upgrade logic

View File

@@ -0,0 +1,34 @@
CREATE TABLE `ai_usage` (
`id` INTEGER PRIMARY KEY AUTOINCREMENT,
`user_id` INTEGER NOT NULL,
`app_id` INTEGER DEFAULT NULL,
`service_name` TEXT NOT NULL,
`model_name` TEXT NOT NULL,
-- set this to a string when service:model alone does not make
-- the numeric values below fungible
`price_modifier` TEXT DEFAULT NULL,
-- expected cost of request in µ¢ (microcents)
`cost` int DEFAULT NULL,
-- input tokens
`value_uint_1` int DEFAULT NULL,
-- output tokens
`value_uint_2` int DEFAULT NULL,
-- miscelaneous values for future use
`value_uint_3` int DEFAULT NULL,
`value_uint_4` int DEFAULT NULL,
`value_uint_5` int DEFAULT NULL,
`created_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
FOREIGN KEY("user_id") REFERENCES "user" ("id") ON DELETE CASCADE ON UPDATE CASCADE,
FOREIGN KEY("app_id") REFERENCES "apps" ("id") ON DELETE SET NULL ON UPDATE CASCADE
);
CREATE INDEX `idx_ai_usage_service_name` ON `ai_usage` (`service_name`);
CREATE INDEX `idx_ai_usage_model_name` ON `ai_usage` (`model_name`);
CREATE INDEX `idx_ai_usage_price_modifier` ON `ai_usage` (`price_modifier`);
CREATE INDEX `idx_ai_usage_created_at` ON `ai_usage` (`created_at`);