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