From cd7755da6bb71a9c28d950c061fd5cf55f36d7e1 Mon Sep 17 00:00:00 2001 From: KernelDeimos Date: Tue, 10 Dec 2024 15:58:22 -0500 Subject: [PATCH] clean: misc cleanups --- src/backend/src/api/APIError.js | 12 -- src/backend/src/api/filesystem/UserParam.js | 3 - src/backend/src/config.js | 8 +- src/backend/src/definitions/Driver.js | 190 ------------------ src/backend/src/errors/error_help_details.js | 14 +- .../src/modules/puterai/AIChatService.js | 11 + .../puterai/OpenAICompletionService.js | 3 +- .../src/modules/web/lib/api_error_handler.js | 3 - 8 files changed, 24 insertions(+), 220 deletions(-) delete mode 100644 src/backend/src/definitions/Driver.js diff --git a/src/backend/src/api/APIError.js b/src/backend/src/api/APIError.js index 75a93fa62..eb16f341d 100644 --- a/src/backend/src/api/APIError.js +++ b/src/backend/src/api/APIError.js @@ -475,18 +475,6 @@ module.exports = class APIError { status: 400, message: 'Incorrect or missing anti-CSRF token.', }, - - // Chat - // TODO: specifying these errors here might be a violation - // of separation of concerns. Services could register their - // own errors with an error registry. - 'max_tokens_exceeded': { - status: 400, - message: ({ input_tokens, max_tokens }) => - `Input exceeds maximum token count. ` + - `Input has ${input_tokens} tokens, ` + - `but the maximum is ${max_tokens}.`, - }, }; /** diff --git a/src/backend/src/api/filesystem/UserParam.js b/src/backend/src/api/filesystem/UserParam.js index 563824a8f..75ff1fcf4 100644 --- a/src/backend/src/api/filesystem/UserParam.js +++ b/src/backend/src/api/filesystem/UserParam.js @@ -17,9 +17,6 @@ * along with this program. If not, see . */ module.exports = class UserParam { - constructor () { - // - } consolidate ({ req }) { return req.user; } diff --git a/src/backend/src/config.js b/src/backend/src/config.js index 65f3c1f29..39eab6b93 100644 --- a/src/backend/src/config.js +++ b/src/backend/src/config.js @@ -140,15 +140,15 @@ if ( config.os.refined ) { module.exports = config; // NEW_CONFIG_LOADING +const maybe_port = config => + config.pub_port !== 80 && config.pub_port !== 443 ? ':' + config.pub_port : ''; const computed_defaults = { pub_port: config => config.http_port, - origin: config => config.protocol + '://' + config.domain + - (config.pub_port !== 80 && config.pub_port !== 443 ? ':' + config.pub_port : ''), + origin: config => config.protocol + '://' + config.domain + maybe_port(config), api_base_url: config => config.experimental_no_subdomain ? config.origin - : config.protocol + '://api.' + config.domain + - (config.pub_port !== 80 && config.pub_port !== 443 ? ':' + config.pub_port : ''), + : config.protocol + '://api.' + config.domain + maybe_port(config), social_card: config => `${config.origin}/assets/img/screenshot.png`, }; diff --git a/src/backend/src/definitions/Driver.js b/src/backend/src/definitions/Driver.js deleted file mode 100644 index 4da28a424..000000000 --- a/src/backend/src/definitions/Driver.js +++ /dev/null @@ -1,190 +0,0 @@ -/* - * Copyright (C) 2024 Puter Technologies Inc. - * - * This file is part of Puter. - * - * Puter is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as published - * by the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - */ -const { AdvancedBase } = require("@heyputer/putility"); -const { Context } = require('../util/context') -const APIError = require("../api/APIError"); -const { AppUnderUserActorType, UserActorType } = require("../services/auth/Actor"); -const { BaseOperation } = require("../services/OperationTraceService"); -const { CodeUtil } = require("../codex/CodeUtil"); - -/** - * Base class for all driver implementations. - * - * @deprecated - we use traits on services now. This class is kept for compatibility - * with EntityStoreImplementation and DBKVStore which still use this. - */ -class Driver extends AdvancedBase { - constructor (...a) { - super(...a); - const methods = this._get_merged_static_object('METHODS'); - // Turn each method into an operation - for ( const k in methods ) { - methods[k] = CodeUtil.mrwrap(methods[k], BaseOperation, { - name: `${this.constructor.ID}:${k}`, - }); - }; - this.methods = methods; - this.sla = this._get_merged_static_object('SLA'); - } - - async call (method, args) { - if ( ! this.methods[method] ) { - throw new Error(`method not found: ${method}`); - } - - const pseudo_this = Object.assign({}, this); - - const context = Context.get(); - pseudo_this.context = context; - pseudo_this.services = context.get('services'); - const services = context.get('services'); - pseudo_this.log = services.get('log-service').create(this.constructor.name); - - await this._sla_enforcement(method); - - return await this.methods[method].call(pseudo_this, args); - } - - async _sla_enforcement (method) { - const context = Context.get(); - const services = context.get('services'); - const method_key = `${this.constructor.ID}:${method}`; - const svc_sla = services.get('sla'); - - // System SLA enforcement - { - const sla_key = `driver:impl:${method_key}`; - const sla = await svc_sla.get('system', sla_key); - - const sys_method_key = `system:${method_key}`; - - // short-term rate limiting - if ( sla?.rate_limit ) { - const svc_rateLimit = services.get('rate-limit'); - let eventual_success = false; - for ( let i = 0 ; i < 60 ; i++ ) { - try { - await svc_rateLimit.check_and_increment(sys_method_key, sla.rate_limit.max, sla.rate_limit.period); - eventual_success = true; - break; - } catch ( e ) { - if ( - ! ( e instanceof APIError ) || - e.fields.code !== 'rate_limit_exceeded' - ) throw e; - await new Promise((resolve) => setTimeout(resolve, 1000)); - } - } - if ( ! eventual_success ) { - throw APIError.create('server_rate_exceeded'); - } - } - } - - // test_mode is checked to prevent rate limiting when it is enabled - const test_mode = context.get('test_mode'); - - // User SLA enforcement - { - const actor = context.get('actor').get_related_actor(UserActorType); - - const user_is_verified = !! actor.type.user.email_confirmed; - - const sla_key = `driver:impl:${method_key}`; - const sla = await svc_sla.get( - user_is_verified ? 'user_verified' : 'user_unverified', - sla_key - ); - - // short-term rate limiting - if ( sla?.rate_limit ) { - const svc_rateLimit = services.get('rate-limit'); - await svc_rateLimit.check_and_increment(method_key, sla.rate_limit.max, sla.rate_limit.period); - } - - // long-term rate limiting - if ( sla?.monthly_limit && ! test_mode ) { - const svc_monthlyUsage = services.get('monthly-usage'); - const count = await svc_monthlyUsage.check( - actor, { - 'driver.interface': this.constructor.INTERFACE, - 'driver.implementation': this.constructor.ID, - 'driver.method': method, - }); - if ( count >= sla.monthly_limit ) { - throw APIError.create('monthly_limit_exceeded', null, { - method_key, - limit: sla.monthly_limit, - }); - } - } - } - - // App SLA enforcement - await (async () => { - const actor = context.get('actor'); - if ( ! ( actor.type instanceof AppUnderUserActorType ) ) return; - - const sla_key = `driver:impl:${method_key}`; - const sla = await svc_sla.get('app_default', sla_key); - - // long-term rate limiting - if ( sla?.monthly_limit && ! test_mode ) { - const svc_monthlyUsage = services.get('monthly-usage'); - const count = await svc_monthlyUsage.check( - actor, { - 'driver.interface': this.constructor.INTERFACE, - 'driver.implementation': this.constructor.ID, - 'driver.method': method, - }); - if ( count >= sla.monthly_limit ) { - throw APIError.create('monthly_limit_exceeded', null, { - method_key, - limit: sla.monthly_limit, - }); - } - } - })(); - - // Record monthly usage - if ( ! test_mode ) { - const actor = context.get('actor'); - const svc_monthlyUsage = services.get('monthly-usage'); - const extra = { - 'driver.interface': this.constructor.INTERFACE, - 'driver.implementation': this.constructor.ID, - 'driver.method': method, - ...(this.get_usage_extra ? this.get_usage_extra() : {}), - }; - await svc_monthlyUsage.increment(actor, method_key, extra); - } - } - - async get_response_meta () { - return { - driver: this.constructor.ID, - driver_version: this.constructor.VERSION, - driver_interface: this.constructor.INTERFACE, - }; - } -} - -module.exports = { - Driver, -}; diff --git a/src/backend/src/errors/error_help_details.js b/src/backend/src/errors/error_help_details.js index d9e0e0dfc..8511012a0 100644 --- a/src/backend/src/errors/error_help_details.js +++ b/src/backend/src/errors/error_help_details.js @@ -50,7 +50,7 @@ const error_help_details = [ apply (more) { more.references = [ ...reused.runtime_env_references, - ] + ]; } }, { @@ -68,10 +68,10 @@ const error_help_details = [ { title: 'Set CONFIG_PATH or RUNTIME_PATH environment variable', }, - ], + ]; more.references = [ ...reused.runtime_env_references, - ] + ]; } }, { @@ -83,7 +83,7 @@ const error_help_details = [ { title: 'Create a valid config file', }, - ] + ]; } }, { @@ -112,7 +112,7 @@ const error_help_details = [ use: 'describes why this error occurs', url: 'https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Errors/Invalid_const_assignment' }, - ] + ]; } }, { @@ -122,7 +122,7 @@ const error_help_details = [ apply (more) { more.notes = [ 'It looks like this might be our fault.', - ] + ]; more.solutions = [ { title: `Check for an issue on ` + @@ -135,7 +135,7 @@ const error_help_details = [ 'create one' ) + '.' } - ] + ]; } }, { diff --git a/src/backend/src/modules/puterai/AIChatService.js b/src/backend/src/modules/puterai/AIChatService.js index 860cd6d94..0cca9f132 100644 --- a/src/backend/src/modules/puterai/AIChatService.js +++ b/src/backend/src/modules/puterai/AIChatService.js @@ -92,6 +92,17 @@ class AIChatService extends BaseService { await this.db.insert('ai_usage', values); }); + + const svc_apiErrpr = this.services.get('api-error'); + svc_apiErrpr.register({ + max_tokens_exceeded: { + status: 400, + message: ({ input_tokens, max_tokens }) => + `Input exceeds maximum token count. ` + + `Input has ${input_tokens} tokens, ` + + `but the maximum is ${max_tokens}.`, + }, + }); } diff --git a/src/backend/src/modules/puterai/OpenAICompletionService.js b/src/backend/src/modules/puterai/OpenAICompletionService.js index c5d95af5b..1bfed00e7 100644 --- a/src/backend/src/modules/puterai/OpenAICompletionService.js +++ b/src/backend/src/modules/puterai/OpenAICompletionService.js @@ -341,8 +341,9 @@ class OpenAICompletionService extends BaseService { const max_tokens = 4096 - token_count; console.log('MAX TOKENS ???', max_tokens); + const svc_apiErrpr = this.services.get('api-error'); if ( max_tokens <= 8 ) { - throw APIError.create('max_tokens_exceeded', null, { + throw svc_apiErrpr.create('max_tokens_exceeded', { input_tokens: token_count, max_tokens: 4096 - 8, }); diff --git a/src/backend/src/modules/web/lib/api_error_handler.js b/src/backend/src/modules/web/lib/api_error_handler.js index 2e8f16ce9..c711392c9 100644 --- a/src/backend/src/modules/web/lib/api_error_handler.js +++ b/src/backend/src/modules/web/lib/api_error_handler.js @@ -26,9 +26,6 @@ const APIError = require('../../../api/APIError.js'); * Since Express 5 is not yet released, this function is used by * eggspress() to handle errors instead of as a middleware. * - * @todo remove this function and use express error handling - * when Express 5 is released - * * @param {*} err * @param {*} req * @param {*} res