diff --git a/extensions/meteringAndBilling/routes/usage.js b/extensions/meteringAndBilling/routes/usage.js index c78b029d..ebced8bd 100644 --- a/extensions/meteringAndBilling/routes/usage.js +++ b/extensions/meteringAndBilling/routes/usage.js @@ -1,24 +1,24 @@ -/** @type {import('@heyputer/backend/src/services/MeteringService/MeteringServiceWrapper.mjs').MeteringAndBillingServiceWrapper} */ -const meteringAndBillingServiceWrapper = extension.import('service:meteringService'); +/** @type {import('@heyputer/backend/src/services/MeteringService/MeteringServiceWrapper.mjs').MeteringServiceWrapper} */ +const meteringServiceWrapper = extension.import('service:meteringService'); // TODO DS: move this to its own router and just use under this path -extension.get('/meteringAndBilling/usage', { subdomain: 'api' }, async (req, res) => { - const meteringAndBillingService = meteringAndBillingServiceWrapper.meteringAndBillingService; +extension.get('/metering/usage', { subdomain: 'api' }, async (req, res) => { + const meteringService = meteringServiceWrapper.meteringService; const actor = req.actor; if ( !actor ) { throw Error('actor not found in context'); } - const actorUsagePromise = meteringAndBillingService.getActorCurrentMonthUsageDetails(actor); - const actorAllowanceInfoPromise = meteringAndBillingService.getAllowedUsage(actor); + const actorUsagePromise = meteringService.getActorCurrentMonthUsageDetails(actor); + const actorAllowanceInfoPromise = meteringService.getAllowedUsage(actor); const [actorUsage, allowanceInfo] = await Promise.all([actorUsagePromise, actorAllowanceInfoPromise]); res.status(200).json({ ...actorUsage, allowanceInfo }); return; }); -extension.get('/meteringAndBilling/usage/:appId', { subdomain: 'api' }, async (req, res) => { - const meteringAndBillingService = meteringAndBillingServiceWrapper.meteringAndBillingService; +extension.get('/metering/usage/:appId', { subdomain: 'api' }, async (req, res) => { + const meteringService = meteringServiceWrapper.meteringService; const actor = req.actor; if ( !actor ) { @@ -30,9 +30,9 @@ extension.get('/meteringAndBilling/usage/:appId', { subdomain: 'api' }, async (r return; } - const appUsage = await meteringAndBillingService.getActorCurrentMonthAppUsageDetails(actor, appId); + const appUsage = await meteringService.getActorCurrentMonthAppUsageDetails(actor, appId); res.status(200).json(appUsage); return; }); -console.debug('Loaded /meteringAndBilling/usage route'); \ No newline at end of file +console.debug('Loaded /metering/usage route'); \ No newline at end of file diff --git a/src/backend/src/CoreModule.js b/src/backend/src/CoreModule.js index 76c6e961..39137057 100644 --- a/src/backend/src/CoreModule.js +++ b/src/backend/src/CoreModule.js @@ -17,14 +17,14 @@ * 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 Library = require("./definitions/Library"); -const { NotificationES } = require("./om/entitystorage/NotificationES"); -const { ProtectedAppES } = require("./om/entitystorage/ProtectedAppES"); +const { AdvancedBase } = require('@heyputer/putility'); +const Library = require('./definitions/Library'); +const { NotificationES } = require('./om/entitystorage/NotificationES'); +const { ProtectedAppES } = require('./om/entitystorage/ProtectedAppES'); const { Context } = require('./util/context'); -const { LLOWrite } = require("./filesystem/ll_operations/ll_write"); -const { LLRead } = require("./filesystem/ll_operations/ll_read"); -const { RuntimeModule } = require("./extension/RuntimeModule.js"); +const { LLOWrite } = require('./filesystem/ll_operations/ll_write'); +const { LLRead } = require('./filesystem/ll_operations/ll_read'); +const { RuntimeModule } = require('./extension/RuntimeModule.js'); /** * Core module for the Puter platform that includes essential services including @@ -135,7 +135,7 @@ const install = async ({ context, services, app, useapi, modapi }) => { const { NAPIThumbnailService } = require('./services/thumbnails/NAPIThumbnailService'); const { RateLimitService } = require('./services/sla/RateLimitService'); const { AuthService } = require('./services/auth/AuthService'); - const { PreAuthService } = require("./services/auth/PreAuthService"); + const { PreAuthService } = require('./services/auth/PreAuthService'); const { SLAService } = require('./services/sla/SLAService'); const { PermissionService } = require('./services/auth/PermissionService'); const { ACLService } = require('./services/auth/ACLService'); @@ -167,7 +167,7 @@ const install = async ({ context, services, app, useapi, modapi }) => { const FilesystemAPIService = require('./services/FilesystemAPIService'); const ServeGUIService = require('./services/ServeGUIService'); const PuterAPIService = require('./services/PuterAPIService'); - const { RefreshAssociationsService } = require("./services/RefreshAssociationsService"); + const { RefreshAssociationsService } = require('./services/RefreshAssociationsService'); // Service names beginning with '__' aren't called by other services; // these provide data/functionality to other services or produce // side-effects from the events of other services. @@ -215,7 +215,7 @@ const install = async ({ context, services, app, useapi, modapi }) => { }); const { EntriService } = require('./services/EntriService.js'); - services.registerService("entri-service", EntriService); + services.registerService('entri-service', EntriService); const { InformationService } = require('./services/information/InformationService'); services.registerService('information', InformationService); @@ -305,7 +305,7 @@ const install = async ({ context, services, app, useapi, modapi }) => { const { OTPService } = require('./services/auth/OTPService'); services.registerService('otp', OTPService); - const { UserProtectedEndpointsService } = require("./services/web/UserProtectedEndpointsService"); + const { UserProtectedEndpointsService } = require('./services/web/UserProtectedEndpointsService'); services.registerService('__user-protected-endpoints', UserProtectedEndpointsService); const { AntiCSRFService } = require('./services/auth/AntiCSRFService'); @@ -326,7 +326,7 @@ const install = async ({ context, services, app, useapi, modapi }) => { const { DevTODService } = require('./services/DevTODService'); services.registerService('__dev-tod', DevTODService); - const { DriverService } = require("./services/drivers/DriverService"); + const { DriverService } = require('./services/drivers/DriverService'); services.registerService('driver', DriverService); const { ScriptService } = require('./services/ScriptService'); @@ -412,10 +412,10 @@ const install = async ({ context, services, app, useapi, modapi }) => { services.registerService('__chat-api', ChatAPIService); const { WorkerService } = require('./services/worker/WorkerService'); - services.registerService("worker-service", WorkerService); + services.registerService('worker-service', WorkerService); - const { MeteringAndBillingServiceWrapper } = require("./services/MeteringService/MeteringServiceWrapper.mjs"); - services.registerService('meteringService', MeteringAndBillingServiceWrapper); + const { MeteringServiceWrapper } = require('./services/MeteringService/MeteringServiceWrapper.mjs'); + services.registerService('meteringService', MeteringServiceWrapper); const { PermissionShortcutService } = require('./services/auth/PermissionShortcutService'); services.registerService('permission-shortcut', PermissionShortcutService); diff --git a/src/backend/src/filesystem/ll_operations/ll_read.js b/src/backend/src/filesystem/ll_operations/ll_read.js index 47a86c54..a4069e23 100644 --- a/src/backend/src/filesystem/ll_operations/ll_read.js +++ b/src/backend/src/filesystem/ll_operations/ll_read.js @@ -94,8 +94,8 @@ class LLRead extends LLFilesystemOperation { //define metering service - /** @type {import("../../services/MeteringService/MeteringService").MeteringAndBillingService} */ - const meteringService = Context.get('services').get('meteringService').meteringAndBillingService; + /** @type {import("../../services/MeteringService/MeteringService").MeteringService} */ + const meteringService = Context.get('services').get('meteringService').meteringService; // check file cache const maybe_buffer = await fileCacheService.try_get(fsNode); // TODO DS: do we need those cache hit logs? if ( maybe_buffer ) { diff --git a/src/backend/src/modules/puterai/AIChatService.js b/src/backend/src/modules/puterai/AIChatService.js index 103aecf3..9edc00c8 100644 --- a/src/backend/src/modules/puterai/AIChatService.js +++ b/src/backend/src/modules/puterai/AIChatService.js @@ -18,17 +18,17 @@ */ // METADATA // {"ai-commented":{"service":"claude"}} -const { PassThrough } = require("stream"); -const APIError = require("../../api/APIError"); -const config = require("../../config"); -const BaseService = require("../../services/BaseService"); -const { DB_WRITE } = require("../../services/database/consts"); -const { TypedValue } = require("../../services/drivers/meta/Runtime"); -const { Context } = require("../../util/context"); -const { AsModeration } = require("./lib/AsModeration"); -const FunctionCalling = require("./lib/FunctionCalling"); -const Messages = require("./lib/Messages"); -const Streaming = require("./lib/Streaming"); +const { PassThrough } = require('stream'); +const APIError = require('../../api/APIError'); +const config = require('../../config'); +const BaseService = require('../../services/BaseService'); +const { DB_WRITE } = require('../../services/database/consts'); +const { TypedValue } = require('../../services/drivers/meta/Runtime'); +const { Context } = require('../../util/context'); +const { AsModeration } = require('./lib/AsModeration'); +const FunctionCalling = require('./lib/FunctionCalling'); +const Messages = require('./lib/Messages'); +const Streaming = require('./lib/Streaming'); // Maximum number of fallback attempts when a model fails, including the first attempt const MAX_FALLBACKS = 3 + 1; // includes first attempt @@ -47,9 +47,9 @@ class AIChatService extends BaseService { cuid2: require('@paralleldrive/cuid2').createId, }; - /** @type {import('../../services/MeteringService/MeteringService').MeteringAndBillingService} */ - get meteringAndBillingService(){ - return this.services.get('meteringService').meteringAndBillingService; + /** @type {import('../../services/MeteringService/MeteringService').MeteringService} */ + get meteringService(){ + return this.services.get('meteringService').meteringService; } /** * Initializes the service by setting up core properties. @@ -98,7 +98,7 @@ class AIChatService extends BaseService { max_tokens_exceeded: { status: 400, message: ({ input_tokens, max_tokens }) => - `Input exceeds maximum token count. ` + + 'Input exceeds maximum token count. ' + `Input has ${input_tokens} tokens, ` + `but the maximum is ${max_tokens}.`, }, @@ -360,7 +360,7 @@ class AIChatService extends BaseService { if ( ! model_details ) { // TODO (xiaochen): replace with a standard link - const available_models_url = this.global_config.origin + "/puterai/chat/models"; + const available_models_url = this.global_config.origin + '/puterai/chat/models'; throw APIError.create('field_invalid', null, { key: 'model', @@ -374,7 +374,7 @@ class AIChatService extends BaseService { const model_max_tokens = model_details.max_tokens; const text = Messages.extract_text(parameters.messages); const approximate_input_cost = text.length / 4 * model_input_cost; // TODO DS: guesstimate tokens better, - const usageAllowed = await this.meteringAndBillingService.hasEnoughCredits(actor, approximate_input_cost); + const usageAllowed = await this.meteringService.hasEnoughCredits(actor, approximate_input_cost); // Handle usage limits reached case if ( !usageAllowed ) { @@ -386,7 +386,7 @@ class AIChatService extends BaseService { } // available is no longer defined, so use meteringService to get available credits - const availableCredits = await this.meteringAndBillingService.getRemainingUsage(actor); + const availableCredits = await this.meteringService.getRemainingUsage(actor); const max_allowed_output_amount = availableCredits - approximate_input_cost; @@ -482,7 +482,7 @@ class AIChatService extends BaseService { // Check usage for fallback model too (with updated method) const actor = Context.get('actor'); - const fallbackUsageAllowed = await this.meteringAndBillingService.hasEnoughCredits(actor, 1); + const fallbackUsageAllowed = await this.meteringService.hasEnoughCredits(actor, 1); // If usage not allowed for fallback, use usage-limited-chat instead if ( !fallbackUsageAllowed ) { diff --git a/src/backend/src/modules/puterai/AWSPollyService.js b/src/backend/src/modules/puterai/AWSPollyService.js index 56e5c899..bf3d41a1 100644 --- a/src/backend/src/modules/puterai/AWSPollyService.js +++ b/src/backend/src/modules/puterai/AWSPollyService.js @@ -18,11 +18,11 @@ */ // METADATA // {"ai-commented":{"service":"claude"}} -const { PollyClient, SynthesizeSpeechCommand, DescribeVoicesCommand } = require("@aws-sdk/client-polly"); -const BaseService = require("../../services/BaseService"); -const { TypedValue } = require("../../services/drivers/meta/Runtime"); -const APIError = require("../../api/APIError"); -const { Context } = require("../../util/context"); +const { PollyClient, SynthesizeSpeechCommand, DescribeVoicesCommand } = require('@aws-sdk/client-polly'); +const BaseService = require('../../services/BaseService'); +const { TypedValue } = require('../../services/drivers/meta/Runtime'); +const APIError = require('../../api/APIError'); +const { Context } = require('../../util/context'); // Polly price calculation per engine const ENGINE_PRICING = { @@ -45,9 +45,9 @@ const VALID_ENGINES = ['standard', 'neural', 'long-form', 'generative']; */ class AWSPollyService extends BaseService { - /** @type {import('../../services/MeteringService/MeteringService').MeteringAndBillingService} */ - get meteringAndBillingService() { - return this.services.get('meteringService').meteringAndBillingService; + /** @type {import('../../services/MeteringService/MeteringService').MeteringService} */ + get meteringService() { + return this.services.get('meteringService').meteringService; } static MODULES = { @@ -134,7 +134,7 @@ class AWSPollyService extends BaseService { const usageType = `aws-polly:${engine}:character`; - const usageAllowed = await this.meteringAndBillingService.hasEnoughCreditsFor(actor, usageType, text.length); + const usageAllowed = await this.meteringService.hasEnoughCreditsFor(actor, usageType, text.length); if ( ! usageAllowed ) { throw APIError.create('insufficient_funds'); @@ -149,7 +149,7 @@ class AWSPollyService extends BaseService { }); // AWS Polly TTS metering: track character count, voice, engine, cost, audio duration if available - this.meteringAndBillingService.incrementUsage(actor, usageType, text.length); + this.meteringService.incrementUsage(actor, usageType, text.length); const speech = new TypedValue({ $: 'stream', diff --git a/src/backend/src/modules/puterai/AWSTextractService.js b/src/backend/src/modules/puterai/AWSTextractService.js index 74672a4a..ddfc32a6 100644 --- a/src/backend/src/modules/puterai/AWSTextractService.js +++ b/src/backend/src/modules/puterai/AWSTextractService.js @@ -18,11 +18,11 @@ */ // METADATA // {"ai-commented":{"service":"claude"}} -const { TextractClient, AnalyzeDocumentCommand, InvalidS3ObjectException } = require("@aws-sdk/client-textract"); +const { TextractClient, AnalyzeDocumentCommand, InvalidS3ObjectException } = require('@aws-sdk/client-textract'); -const BaseService = require("../../services/BaseService"); -const APIError = require("../../api/APIError"); -const { Context } = require("../../util/context"); +const BaseService = require('../../services/BaseService'); +const APIError = require('../../api/APIError'); +const { Context } = require('../../util/context'); /** * AWSTextractService class - Provides OCR (Optical Character Recognition) functionality using AWS Textract @@ -31,9 +31,9 @@ const { Context } = require("../../util/context"); * Handles both S3-stored and buffer-based document processing with automatic region management. */ class AWSTextractService extends BaseService { - /** @type {import('../../services/MeteringService/MeteringService').MeteringAndBillingService} */ - get meteringAndBillingService(){ - return this.services.get('meteringService').meteringAndBillingService; + /** @type {import('../../services/MeteringService/MeteringService').MeteringService} */ + get meteringService(){ + return this.services.get('meteringService').meteringService; } /** * AWS Textract service for OCR functionality @@ -146,9 +146,9 @@ class AWSTextractService extends BaseService { } = await this._get_client_and_document(file_facade); const actor = Context.get('actor'); - const usageType = "aws-textract:detect-document-text:page"; + const usageType = 'aws-textract:detect-document-text:page'; - const usageAllowed = await this.meteringAndBillingService.hasEnoughCreditsFor(actor, usageType, 1); // allow them to pass if they have enough for 1 page atleast + const usageAllowed = await this.meteringService.hasEnoughCreditsFor(actor, usageType, 1); // allow them to pass if they have enough for 1 page atleast if ( ! usageAllowed ) { throw APIError.create('insufficient_funds'); @@ -191,7 +191,7 @@ class AWSTextractService extends BaseService { if ( block.BlockType === 'PAGE' ) pageCount += 1; } } - this.meteringAndBillingService.incrementUsage(actor, usageType, pageCount || 1); + this.meteringService.incrementUsage(actor, usageType, pageCount || 1); return textractResp; } diff --git a/src/backend/src/modules/puterai/ClaudeService.js b/src/backend/src/modules/puterai/ClaudeService.js index 8221ffac..c8b83f52 100644 --- a/src/backend/src/modules/puterai/ClaudeService.js +++ b/src/backend/src/modules/puterai/ClaudeService.js @@ -18,13 +18,13 @@ */ // METADATA // {"ai-commented":{"service":"claude"}} -const { default: Anthropic, toFile } = require("@anthropic-ai/sdk"); -const BaseService = require("../../services/BaseService"); -const FunctionCalling = require("./lib/FunctionCalling"); -const Messages = require("./lib/Messages"); -const FSNodeParam = require("../../api/filesystem/FSNodeParam"); -const { LLRead } = require("../../filesystem/ll_operations/ll_read"); -const { Context } = require("../../util/context"); +const { default: Anthropic, toFile } = require('@anthropic-ai/sdk'); +const BaseService = require('../../services/BaseService'); +const FunctionCalling = require('./lib/FunctionCalling'); +const Messages = require('./lib/Messages'); +const FSNodeParam = require('../../api/filesystem/FSNodeParam'); +const { LLRead } = require('../../filesystem/ll_operations/ll_read'); +const { Context } = require('../../util/context'); /** * ClaudeService class extends BaseService to provide integration with Anthropic's Claude AI models. @@ -50,8 +50,8 @@ class ClaudeService extends BaseService { * @returns {Promise} */ - /** @type {import('../../services/MeteringService/MeteringService').MeteringAndBillingService} */ - #meteringAndBillingService; + /** @type {import('../../services/MeteringService/MeteringService').MeteringService} */ + #meteringService; async _init() { this.anthropic = new Anthropic({ @@ -68,7 +68,7 @@ class ClaudeService extends BaseService { service_name: this.service_name, alias: true, }); - this.#meteringAndBillingService = this.services.get('meteringService').meteringAndBillingService; // TODO DS: move to proper extensions + this.#meteringService = this.services.get('meteringService').meteringService; // TODO DS: move to proper extensions } /** @@ -341,7 +341,7 @@ class ClaudeService extends BaseService { // TODO DS: get this inside the class as a private method once the methods aren't exported directly billForUsage(actor, model, usage) { - this.#meteringAndBillingService.utilRecordUsageObject(usage, actor, `claude:${this.models_().find(m => [m.id, ...(m.aliases || [])].includes(model)).id}`); + this.#meteringService.utilRecordUsageObject(usage, actor, `claude:${this.models_().find(m => [m.id, ...(m.aliases || [])].includes(model)).id}`); }; /** diff --git a/src/backend/src/modules/puterai/DeepSeekService.js b/src/backend/src/modules/puterai/DeepSeekService.js index cb0af051..4915b8e2 100644 --- a/src/backend/src/modules/puterai/DeepSeekService.js +++ b/src/backend/src/modules/puterai/DeepSeekService.js @@ -18,9 +18,9 @@ */ // METADATA // {"ai-commented":{"service":"claude"}} -const BaseService = require("../../services/BaseService"); -const { Context } = require("../../util/context"); -const OpenAIUtil = require("./lib/OpenAIUtil"); +const BaseService = require('../../services/BaseService'); +const { Context } = require('../../util/context'); +const OpenAIUtil = require('./lib/OpenAIUtil'); const dedent = require('dedent'); /** @@ -36,9 +36,9 @@ class DeepSeekService extends BaseService { }; /** - * @type {import('../../services/MeteringService/MeteringService').MeteringAndBillingService} + * @type {import('../../services/MeteringService/MeteringService').MeteringService} */ - meteringAndBillingService; + meteringService; /** * Gets the system prompt used for AI interactions * @returns {string} The base system prompt that identifies the AI as running on Puter @@ -63,7 +63,7 @@ class DeepSeekService extends BaseService { service_name: this.service_name, alias: true, }); - this.meteringAndBillingService = this.services.get('meteringService').meteringAndBillingService; + this.meteringService = this.services.get('meteringService').meteringService; } /** @@ -114,7 +114,7 @@ class DeepSeekService extends BaseService { for ( const message of messages ) { // DeepSeek doesn't appreciate arrays here if ( message.tool_calls && Array.isArray(message.content) ) { - message.content = ""; + message.content = ''; } } @@ -167,7 +167,7 @@ class DeepSeekService extends BaseService { return OpenAIUtil.handle_completion_output({ usage_calculator: ({ usage }) => { const trackedUsage = OpenAIUtil.extractMeteredUsage(usage); - this.meteringAndBillingService.utilRecordUsageObject(trackedUsage, actor, `deepseek:${modelDetails.id}`); + this.meteringService.utilRecordUsageObject(trackedUsage, actor, `deepseek:${modelDetails.id}`); const legacyCostCalculator = OpenAIUtil.create_usage_calculator({ model_details: modelDetails, }); diff --git a/src/backend/src/modules/puterai/GeminiImageGenerationService.js b/src/backend/src/modules/puterai/GeminiImageGenerationService.js index 22b822fb..a4d55d4b 100644 --- a/src/backend/src/modules/puterai/GeminiImageGenerationService.js +++ b/src/backend/src/modules/puterai/GeminiImageGenerationService.js @@ -18,10 +18,10 @@ */ // METADATA // {"ai-commented":{"service":"claude"}} -const APIError = require("../../api/APIError"); -const BaseService = require("../../services/BaseService"); -const { TypedValue } = require("../../services/drivers/meta/Runtime"); -const { Context } = require("../../util/context"); +const APIError = require('../../api/APIError'); +const BaseService = require('../../services/BaseService'); +const { TypedValue } = require('../../services/drivers/meta/Runtime'); +const { Context } = require('../../util/context'); const { GoogleGenAI } = require('@google/genai'); /** @@ -30,9 +30,9 @@ const { GoogleGenAI } = require('@google/genai'); * the puter-image-generation interface. */ class GeminiImageGenerationService extends BaseService { - /** @type {import('../../services/MeteringService/MeteringService').MeteringAndBillingService} */ - get meteringAndBillingService(){ - return this.services.get('meteringService').meteringAndBillingService; + /** @type {import('../../services/MeteringService/MeteringService').MeteringService} */ + get meteringService(){ + return this.services.get('meteringService').meteringService; } static MODULES = { }; @@ -40,7 +40,7 @@ class GeminiImageGenerationService extends BaseService { _construct() { this.models_ = { 'gemini-2.5-flash-image-preview': { - "1024x1024": 0.039, + '1024x1024': 0.039, }, }; } @@ -167,7 +167,7 @@ class GeminiImageGenerationService extends BaseService { const usageType = `gemini:${model}:${price_key}`; - const usageAllowed = await this.meteringAndBillingService.hasEnoughCreditsFor(actor, usageType, 1); + const usageAllowed = await this.meteringService.hasEnoughCreditsFor(actor, usageType, 1); if ( !usageAllowed ) { throw APIError.create('insufficient_funds'); @@ -192,19 +192,19 @@ class GeminiImageGenerationService extends BaseService { } const response = await this.genAI.models.generateContent({ - model: "gemini-2.5-flash-image-preview", + model: 'gemini-2.5-flash-image-preview', contents: contents, }); // Metering usage tracking // Gemini usage: always 1 image, resolution, cost, model - this.meteringAndBillingService.incrementUsage(actor, usageType, 1); + this.meteringService.incrementUsage(actor, usageType, 1); let url = undefined; for ( const part of response.candidates[0].content.parts ) { if ( part.text ) { // do nothing here } else if ( part.inlineData ) { const imageData = part.inlineData.data; - url = "data:image/png;base64," + imageData; + url = 'data:image/png;base64,' + imageData; } } diff --git a/src/backend/src/modules/puterai/GeminiService.js b/src/backend/src/modules/puterai/GeminiService.js index e69a841f..c76fe5f7 100644 --- a/src/backend/src/modules/puterai/GeminiService.js +++ b/src/backend/src/modules/puterai/GeminiService.js @@ -1,14 +1,14 @@ -const BaseService = require("../../services/BaseService"); +const BaseService = require('../../services/BaseService'); const { GoogleGenerativeAI } = require('@google/generative-ai'); -const GeminiSquareHole = require("./lib/GeminiSquareHole"); -const FunctionCalling = require("./lib/FunctionCalling"); -const { Context } = require("../../util/context"); +const GeminiSquareHole = require('./lib/GeminiSquareHole'); +const FunctionCalling = require('./lib/FunctionCalling'); +const { Context } = require('../../util/context'); class GeminiService extends BaseService { /** - * @type {import('../../services/MeteringService/MeteringService').MeteringAndBillingService} + * @type {import('../../services/MeteringService/MeteringService').MeteringService} */ - meteringAndBillingService = undefined; + meteringService = undefined; async _init() { const svc_aiChat = this.services.get('ai-chat'); @@ -16,7 +16,7 @@ class GeminiService extends BaseService { service_name: this.service_name, alias: true, }); - this.meteringAndBillingService = this.services.get('meteringService').meteringAndBillingService; + this.meteringService = this.services.get('meteringService').meteringService; } static IMPLEMENTS = { @@ -85,7 +85,7 @@ class GeminiService extends BaseService { completion_tokens: usageMetadata.candidatesTokenCount, cached_tokens: usageMetadata.cachedContentTokenCount || 0, }; - this.meteringAndBillingService.utilRecordUsageObject(trackedUsage, actor, meteringPrefix); + this.meteringService.utilRecordUsageObject(trackedUsage, actor, meteringPrefix); }, }), }; @@ -104,7 +104,7 @@ class GeminiService extends BaseService { completion_tokens: genResult.response.usageMetadata.candidatesTokenCount, cached_tokens: genResult.response.usageMetadata.cachedContentTokenCount || 0, }; - this.meteringAndBillingService.utilRecordUsageObject(trackedUsage, actor, meteringPrefix); + this.meteringService.utilRecordUsageObject(trackedUsage, actor, meteringPrefix); return result; } }, diff --git a/src/backend/src/modules/puterai/GroqAIService.js b/src/backend/src/modules/puterai/GroqAIService.js index 96d7667f..7d900a49 100644 --- a/src/backend/src/modules/puterai/GroqAIService.js +++ b/src/backend/src/modules/puterai/GroqAIService.js @@ -18,11 +18,11 @@ */ // METADATA // {"ai-commented":{"service":"claude"}} -const BaseService = require("../../services/BaseService"); -const { Context } = require("../../util/context"); -const OpenAIUtil = require("./lib/OpenAIUtil"); +const BaseService = require('../../services/BaseService'); +const { Context } = require('../../util/context'); +const OpenAIUtil = require('./lib/OpenAIUtil'); -/** @type {import('../../services/MeteringService/MeteringService').MeteringAndBillingService} */ +/** @type {import('../../services/MeteringService/MeteringService').MeteringService} */ /** * Service class for integrating with Groq AI's language models. @@ -34,8 +34,8 @@ const OpenAIUtil = require("./lib/OpenAIUtil"); * @extends BaseService */ class GroqAIService extends BaseService { - /** @type {import('../../services/MeteringService/MeteringService').MeteringAndBillingService} */ - meteringAndBillingService; + /** @type {import('../../services/MeteringService/MeteringService').MeteringService} */ + meteringService; static MODULES = { Groq: require('groq-sdk'), }; @@ -56,7 +56,7 @@ class GroqAIService extends BaseService { service_name: this.service_name, alias: true, }); - this.meteringAndBillingService = this.services.get('meteringService').meteringAndBillingService; // TODO DS: move to proper extensions + this.meteringService = this.services.get('meteringService').meteringService; // TODO DS: move to proper extensions } /** @@ -104,7 +104,7 @@ class GroqAIService extends BaseService { for ( const message of messages ) { // Curiously, DeepSeek has the exact same deviation if ( message.tool_calls && Array.isArray(message.content) ) { - message.content = ""; + message.content = ''; } } @@ -128,7 +128,7 @@ class GroqAIService extends BaseService { }, usage_calculator: ({ usage }) => { const trackedUsage = OpenAIUtil.extractMeteredUsage(usage); - this.meteringAndBillingService.utilRecordUsageObject(trackedUsage, actor, `groq:${modelDetails.id}`); + this.meteringService.utilRecordUsageObject(trackedUsage, actor, `groq:${modelDetails.id}`); // Still return legacy cost calculation for compatibility const legacyCostCalculator = OpenAIUtil.create_usage_calculator({ model_details: modelDetails, @@ -201,38 +201,38 @@ class GroqAIService extends BaseService { }, }, { - "id": "llama-3.1-70b-versatile", - "name": "Llama 3.1 70B Versatile 128k", - "context": 128000, - "cost": { - "currency": "usd-cents", - "tokens": 1000000, - "input": 59, - "output": 79, + 'id': 'llama-3.1-70b-versatile', + 'name': 'Llama 3.1 70B Versatile 128k', + 'context': 128000, + 'cost': { + 'currency': 'usd-cents', + 'tokens': 1000000, + 'input': 59, + 'output': 79, }, }, { // This was only available on their Discord, not // on the pricing page. - "id": "llama-3.1-70b-specdec", - "name": "Llama 3.1 8B Instant 128k", - "context": 128000, - "cost": { - "currency": "usd-cents", - "tokens": 1000000, - "input": 59, - "output": 99, + 'id': 'llama-3.1-70b-specdec', + 'name': 'Llama 3.1 8B Instant 128k', + 'context': 128000, + 'cost': { + 'currency': 'usd-cents', + 'tokens': 1000000, + 'input': 59, + 'output': 99, }, }, { - "id": "llama-3.1-8b-instant", - "name": "Llama 3.1 8B Instant 128k", - "context": 131072, - "cost": { - "currency": "usd-cents", - "tokens": 1000000, - "input": 5, - "output": 8, + 'id': 'llama-3.1-8b-instant', + 'name': 'Llama 3.1 8B Instant 128k', + 'context': 131072, + 'cost': { + 'currency': 'usd-cents', + 'tokens': 1000000, + 'input': 5, + 'output': 8, }, max_tokens: 131072, }, @@ -261,25 +261,25 @@ class GroqAIService extends BaseService { max_tokens: 512, }, { - "id": "llama-3.2-1b-preview", - "name": "Llama 3.2 1B (Preview) 8k", - "context": 128000, - "cost": { - "currency": "usd-cents", - "tokens": 1000000, - "input": 4, - "output": 4, + 'id': 'llama-3.2-1b-preview', + 'name': 'Llama 3.2 1B (Preview) 8k', + 'context': 128000, + 'cost': { + 'currency': 'usd-cents', + 'tokens': 1000000, + 'input': 4, + 'output': 4, }, }, { - "id": "llama-3.2-3b-preview", - "name": "Llama 3.2 3B (Preview) 8k", - "context": 128000, - "cost": { - "currency": "usd-cents", - "tokens": 1000000, - "input": 6, - "output": 6, + 'id': 'llama-3.2-3b-preview', + 'name': 'Llama 3.2 3B (Preview) 8k', + 'context': 128000, + 'cost': { + 'currency': 'usd-cents', + 'tokens': 1000000, + 'input': 6, + 'output': 6, }, }, { @@ -303,47 +303,47 @@ class GroqAIService extends BaseService { }, }, { - "id": "llama3-70b-8192", - "name": "Llama 3 70B 8k", - "context": 8192, - "cost": { - "currency": "usd-cents", - "tokens": 1000000, - "input": 59, - "output": 79, + 'id': 'llama3-70b-8192', + 'name': 'Llama 3 70B 8k', + 'context': 8192, + 'cost': { + 'currency': 'usd-cents', + 'tokens': 1000000, + 'input': 59, + 'output': 79, }, }, { - "id": "llama3-8b-8192", - "name": "Llama 3 8B 8k", - "context": 8192, - "cost": { - "currency": "usd-cents", - "tokens": 1000000, - "input": 5, - "output": 8, + 'id': 'llama3-8b-8192', + 'name': 'Llama 3 8B 8k', + 'context': 8192, + 'cost': { + 'currency': 'usd-cents', + 'tokens': 1000000, + 'input': 5, + 'output': 8, }, }, { - "id": "mixtral-8x7b-32768", - "name": "Mixtral 8x7B Instruct 32k", - "context": 32768, - "cost": { - "currency": "usd-cents", - "tokens": 1000000, - "input": 24, - "output": 24, + 'id': 'mixtral-8x7b-32768', + 'name': 'Mixtral 8x7B Instruct 32k', + 'context': 32768, + 'cost': { + 'currency': 'usd-cents', + 'tokens': 1000000, + 'input': 24, + 'output': 24, }, }, { - "id": "llama-guard-3-8b", - "name": "Llama Guard 3 8B 8k", - "context": 8192, - "cost": { - "currency": "usd-cents", - "tokens": 1000000, - "input": 20, - "output": 20, + 'id': 'llama-guard-3-8b', + 'name': 'Llama Guard 3 8B 8k', + 'context': 8192, + 'cost': { + 'currency': 'usd-cents', + 'tokens': 1000000, + 'input': 20, + 'output': 20, }, }, ]; diff --git a/src/backend/src/modules/puterai/MistralAIService.js b/src/backend/src/modules/puterai/MistralAIService.js index 3312bb0e..1a7e81f0 100644 --- a/src/backend/src/modules/puterai/MistralAIService.js +++ b/src/backend/src/modules/puterai/MistralAIService.js @@ -18,10 +18,10 @@ */ // METADATA // {"ai-commented":{"service":"claude"}} -const BaseService = require("../../services/BaseService"); +const BaseService = require('../../services/BaseService'); const axios = require('axios'); -const OpenAIUtil = require("./lib/OpenAIUtil"); -const { Context } = require("../../util/context"); +const OpenAIUtil = require('./lib/OpenAIUtil'); +const { Context } = require('../../util/context'); /** * MistralAIService class extends BaseService to provide integration with the Mistral AI API. @@ -31,8 +31,8 @@ const { Context } = require("../../util/context"); * for different models and implements the puter-chat-completion interface. */ class MistralAIService extends BaseService { - /** @type {import('../../services/MeteringService/MeteringService').MeteringAndBillingService} */ - meteringAndBillingService; + /** @type {import('../../services/MeteringService/MeteringService').MeteringService} */ + meteringService; static MODULES = { '@mistralai/mistralai': require('@mistralai/mistralai'), }; @@ -250,7 +250,7 @@ class MistralAIService extends BaseService { alias: true, }); - this.meteringAndBillingService = this.services.get('meteringService').meteringAndBillingService; + this.meteringService = this.services.get('meteringService').meteringService; // TODO: make this event-driven so it doesn't hold up boot await this.populate_models_(); @@ -371,7 +371,7 @@ class MistralAIService extends BaseService { const snake_usage = {}; for ( const key in chunk.usage ) { - const snakeKey = key.replace(/([A-Z])/g, "_$1").toLowerCase(); + const snakeKey = key.replace(/([A-Z])/g, '_$1').toLowerCase(); snake_usage[snakeKey] = chunk.usage[key]; } @@ -388,9 +388,7 @@ class MistralAIService extends BaseService { stream, usage_calculator: ({ usage }) => { const trackedUsage = OpenAIUtil.extractMeteredUsage(usage); - if ( this.meteringAndBillingService ) { - this.meteringAndBillingService.utilRecordUsageObject(trackedUsage, actor, `mistral:${modelDetails.id}`); - } + this.meteringService.utilRecordUsageObject(trackedUsage, actor, `mistral:${modelDetails.id}`); // Still return legacy cost calculation for compatibility const legacyCostCalculator = OpenAIUtil.create_usage_calculator({ model_details: modelDetails, diff --git a/src/backend/src/modules/puterai/OpenAIImageGenerationService.js b/src/backend/src/modules/puterai/OpenAIImageGenerationService.js index 17deb55c..1a28c16f 100644 --- a/src/backend/src/modules/puterai/OpenAIImageGenerationService.js +++ b/src/backend/src/modules/puterai/OpenAIImageGenerationService.js @@ -18,10 +18,10 @@ */ // METADATA // {"ai-commented":{"service":"claude"}} -const APIError = require("../../api/APIError"); -const BaseService = require("../../services/BaseService"); -const { TypedValue } = require("../../services/drivers/meta/Runtime"); -const { Context } = require("../../util/context"); +const APIError = require('../../api/APIError'); +const BaseService = require('../../services/BaseService'); +const { TypedValue } = require('../../services/drivers/meta/Runtime'); +const { Context } = require('../../util/context'); /** * Service class for generating images using OpenAI's DALL-E API. @@ -31,9 +31,9 @@ const { Context } = require("../../util/context"); * validation, and spending tracking. */ class OpenAIImageGenerationService extends BaseService { - /** @type {import('../../services/MeteringService/MeteringService').MeteringAndBillingService} */ - get meteringAndBillingService(){ - return this.services.get('meteringService').meteringAndBillingService; + /** @type {import('../../services/MeteringService/MeteringService').MeteringService} */ + get meteringService(){ + return this.services.get('meteringService').meteringService; } static MODULES = { @@ -43,15 +43,15 @@ class OpenAIImageGenerationService extends BaseService { _construct() { this.models_ = { 'gpt-image-1': { - "low:1024x1024": 0.011, - "low:1024x1536": 0.016, - "low:1536x1024": 0.016, - "medium:1024x1024": 0.042, - "medium:1024x1536": 0.063, - "medium:1536x1024": 0.063, - "high:1024x1024": 0.167, - "high:1024x1536": 0.25, - "high:1536x1024": 0.25, + 'low:1024x1024': 0.011, + 'low:1024x1536': 0.016, + 'low:1536x1024': 0.016, + 'medium:1024x1024': 0.042, + 'medium:1024x1536': 0.063, + 'medium:1536x1024': 0.063, + 'high:1024x1024': 0.167, + 'high:1024x1536': 0.25, + 'high:1536x1024': 0.25, }, 'dall-e-3': { '1024x1024': 0.04, @@ -193,7 +193,7 @@ class OpenAIImageGenerationService extends BaseService { } const usageType = `openai:${model}:${price_key}`; - const usageAllowed = await this.meteringAndBillingService.hasEnoughCreditsFor(actor, usageType, 1); + const usageAllowed = await this.meteringService.hasEnoughCreditsFor(actor, usageType, 1); if ( ! usageAllowed ) { throw APIError.create('insufficient_funds'); @@ -210,7 +210,7 @@ class OpenAIImageGenerationService extends BaseService { const result = await this.openai.images.generate(apiParams); // For image generation, usage is typically image count and resolution - this.meteringAndBillingService.incrementUsage(actor, usageType, 1); + this.meteringService.incrementUsage(actor, usageType, 1); const spending_meta = { model, @@ -218,10 +218,10 @@ class OpenAIImageGenerationService extends BaseService { }; if ( quality ) { - spending_meta.size = quality + ":" + spending_meta.size; + spending_meta.size = quality + ':' + spending_meta.size; } - const url = result.data?.[0]?.url || (result.data?.[0]?.b64_json ? "data:image/png;base64," + result.data[0].b64_json : null); + const url = result.data?.[0]?.url || (result.data?.[0]?.b64_json ? 'data:image/png;base64,' + result.data[0].b64_json : null); if ( !url ) { throw new Error('Failed to extract image URL from OpenAI response'); diff --git a/src/backend/src/modules/puterai/OpenAiCompletionService/OpenAICompletionService.mjs b/src/backend/src/modules/puterai/OpenAiCompletionService/OpenAICompletionService.mjs index 83ea7d4d..2f93ca90 100644 --- a/src/backend/src/modules/puterai/OpenAiCompletionService/OpenAICompletionService.mjs +++ b/src/backend/src/modules/puterai/OpenAiCompletionService/OpenAICompletionService.mjs @@ -48,13 +48,13 @@ export class OpenAICompletionService { #models; - /** @type {import('../../../services/MeteringService/MeteringService.js').MeteringAndBillingService} */ - #meteringAndBillingService; + /** @type {import('../../../services/MeteringService/MeteringService.js').MeteringService} */ + #meteringService; - constructor({ serviceName, config, globalConfig, aiChatService, meteringAndBillingService, models = OPEN_AI_MODELS, defaultModel = 'gpt-4.1-nano' }) { + constructor({ serviceName, config, globalConfig, aiChatService, meteringService, models = OPEN_AI_MODELS, defaultModel = 'gpt-4.1-nano' }) { this.#models = models; this.#defaultModel = defaultModel; - this.#meteringAndBillingService = meteringAndBillingService; + this.#meteringService = meteringService; let apiKey = config?.services?.openai?.apiKey ?? globalConfig?.services?.openai?.apiKey; @@ -208,7 +208,7 @@ export class OpenAICompletionService { delete task.contentPart.puter_path; task.contentPart.type = 'text'; task.contentPart.text = `{error: input file exceeded maximum of ${MAX_FILE_SIZE} bytes; ` + - `the user did not write this message}`; // "poor man's system prompt" + 'the user did not write this message}'; // "poor man's system prompt" return; // "continue" } @@ -236,8 +236,8 @@ export class OpenAICompletionService { }; } else { task.contentPart.type = 'text'; - task.contentPart.text = `{error: input file has unsupported MIME type; ` + - `the user did not write this message}`; // "poor man's system prompt" + task.contentPart.text = '{error: input file has unsupported MIME type; ' + + 'the user did not write this message}'; // "poor man's system prompt" } })()); } @@ -271,7 +271,7 @@ export class OpenAICompletionService { cached_tokens: usage.prompt_tokens_details?.cached_tokens ?? 0, }; - this.#meteringAndBillingService.utilRecordUsageObject(trackedUsage, actor, `openai:${modelDetails.id}`); + this.#meteringService.utilRecordUsageObject(trackedUsage, actor, `openai:${modelDetails.id}`); const legacyCostCalculator = OpenAIUtil.create_usage_calculator({ model_details: modelDetails, }); diff --git a/src/backend/src/modules/puterai/OpenAiCompletionService/index.mjs b/src/backend/src/modules/puterai/OpenAiCompletionService/index.mjs index 0452a102..73ab2dc9 100644 --- a/src/backend/src/modules/puterai/OpenAiCompletionService/index.mjs +++ b/src/backend/src/modules/puterai/OpenAiCompletionService/index.mjs @@ -31,8 +31,8 @@ export class OpenAICompletionServiceWrapper extends BaseService { serviceName: this.service_name, config: this.config, globalConfig: this.global_config, - aiChatService: this.services.get("ai-chat"), - meteringAndBillingService: this.services.get('meteringService').meteringAndBillingService, + aiChatService: this.services.get('ai-chat'), + meteringService: this.services.get('meteringService').meteringService, }); } diff --git a/src/backend/src/modules/puterai/OpenRouterService.js b/src/backend/src/modules/puterai/OpenRouterService.js index 994da65f..150a8ee7 100644 --- a/src/backend/src/modules/puterai/OpenRouterService.js +++ b/src/backend/src/modules/puterai/OpenRouterService.js @@ -18,10 +18,10 @@ */ // METADATA // {"ai-commented":{"service":"claude"}} -const APIError = require("../../api/APIError"); -const BaseService = require("../../services/BaseService"); -const OpenAIUtil = require("./lib/OpenAIUtil"); -const { Context } = require("../../util/context"); +const APIError = require('../../api/APIError'); +const BaseService = require('../../services/BaseService'); +const OpenAIUtil = require('./lib/OpenAIUtil'); +const { Context } = require('../../util/context'); /** * XAIService class - Provides integration with X.AI's API for chat completions @@ -46,8 +46,8 @@ class OpenRouterService extends BaseService { return model; } - /** @type {import('../../services/MeteringService/MeteringService').MeteringAndBillingService} */ - meteringAndBillingService; + /** @type {import('../../services/MeteringService/MeteringService').MeteringService} */ + meteringService; /** * Initializes the XAI service by setting up the OpenAI client and registering with the AI chat provider @@ -67,7 +67,7 @@ class OpenRouterService extends BaseService { service_name: this.service_name, alias: true, }); - this.meteringAndBillingService = this.services.get('meteringService').meteringAndBillingService; // TODO DS: move to proper extensions + this.meteringService = this.services.get('meteringService').meteringService; // TODO DS: move to proper extensions } /** @@ -152,7 +152,7 @@ class OpenRouterService extends BaseService { const costOverwrites = Object.fromEntries(Object.keys(trackedUsage).map((k) => { return [k, rawPriceModelDetails.cost[k] * trackedUsage[k]]; })); - this.meteringAndBillingService.utilRecordUsageObject(trackedUsage, actor, modelDetails.id, costOverwrites); + this.meteringService.utilRecordUsageObject(trackedUsage, actor, modelDetails.id, costOverwrites); const legacyCostCalculator = OpenAIUtil.create_usage_calculator({ model_details: modelDetails, }); diff --git a/src/backend/src/modules/puterai/TogetherAIService.js b/src/backend/src/modules/puterai/TogetherAIService.js index ab013648..b5853471 100644 --- a/src/backend/src/modules/puterai/TogetherAIService.js +++ b/src/backend/src/modules/puterai/TogetherAIService.js @@ -18,13 +18,13 @@ */ // METADATA // {"ai-commented":{"service":"claude"}} -const { PassThrough } = require("stream"); -const BaseService = require("../../services/BaseService"); -const { TypedValue } = require("../../services/drivers/meta/Runtime"); -const { nou } = require("../../util/langutil"); +const { PassThrough } = require('stream'); +const BaseService = require('../../services/BaseService'); +const { TypedValue } = require('../../services/drivers/meta/Runtime'); +const { nou } = require('../../util/langutil'); const { Together } = require('together-ai'); -const OpenAIUtil = require("./lib/OpenAIUtil"); -const { Context } = require("../../util/context"); +const OpenAIUtil = require('./lib/OpenAIUtil'); +const { Context } = require('../../util/context'); /** * TogetherAIService class provides integration with Together AI's language models. @@ -35,9 +35,9 @@ const { Context } = require("../../util/context"); */ class TogetherAIService extends BaseService { /** - * @type {import('../../services/MeteringService/MeteringService').MeteringAndBillingService} + * @type {import('../../services/MeteringService/MeteringService').MeteringService} */ - meteringAndBillingService; + meteringService; static MODULES = { kv: globalThis.kv, uuidv4: require('uuid').v4, @@ -60,7 +60,7 @@ class TogetherAIService extends BaseService { service_name: this.service_name, alias: true, }); - this.meteringAndBillingService = this.services.get('meteringService').meteringAndBillingService; + this.meteringService = this.services.get('meteringService').meteringService; } /** @@ -133,7 +133,7 @@ class TogetherAIService extends BaseService { prompt_tokens: trackedUsage.prompt_tokens * (modelDetails?.cost?.input ?? 0), completion_tokens: trackedUsage.completion_tokens * (modelDetails?.cost?.output ?? 0), }; - this.meteringAndBillingService.utilRecordUsageObject(trackedUsage, actor, modelId, costOverrides); + this.meteringService.utilRecordUsageObject(trackedUsage, actor, modelId, costOverrides); } if ( chunk.choices.length < 1 ) continue; @@ -163,7 +163,7 @@ class TogetherAIService extends BaseService { output_tokens: completion.usage.completion_tokens, }; // Metering: record usage for non-streamed completion - this.meteringAndBillingService.utilRecordUsageObject(completion.usage, actor, modelId); + this.meteringService.utilRecordUsageObject(completion.usage, actor, modelId); return ret; }, }, diff --git a/src/backend/src/modules/puterai/XAIService.js b/src/backend/src/modules/puterai/XAIService.js index 3599a6ca..5e704d03 100644 --- a/src/backend/src/modules/puterai/XAIService.js +++ b/src/backend/src/modules/puterai/XAIService.js @@ -18,9 +18,9 @@ */ // METADATA // {"ai-commented":{"service":"claude"}} -const BaseService = require("../../services/BaseService"); -const { Context } = require("../../util/context"); -const OpenAIUtil = require("./lib/OpenAIUtil"); +const BaseService = require('../../services/BaseService'); +const { Context } = require('../../util/context'); +const OpenAIUtil = require('./lib/OpenAIUtil'); /** * XAIService class - Provides integration with X.AI's API for chat completions @@ -33,8 +33,8 @@ class XAIService extends BaseService { static MODULES = { openai: require('openai'), }; - /** @type {import('../../services/MeteringService/MeteringService').MeteringAndBillingService} */ - meteringAndBillingService; + /** @type {import('../../services/MeteringService/MeteringService').MeteringService} */ + meteringService; adapt_model(model) { return model; @@ -48,7 +48,7 @@ class XAIService extends BaseService { async _init() { this.openai = new this.modules.openai.OpenAI({ apiKey: this.global_config.services.xai.apiKey, - baseURL: "https://api.x.ai/v1", + baseURL: 'https://api.x.ai/v1', }); const svc_aiChat = this.services.get('ai-chat'); @@ -56,7 +56,7 @@ class XAIService extends BaseService { service_name: this.service_name, alias: true, }); - this.meteringAndBillingService = this.services.get('meteringService').meteringAndBillingService; // TODO DS: move to proper extensions + this.meteringService = this.services.get('meteringService').meteringService; // TODO DS: move to proper extensions } /** @@ -128,7 +128,7 @@ class XAIService extends BaseService { cached_tokens: usage.prompt_tokens_details?.cached_tokens ?? 0, }; - this.meteringAndBillingService.utilRecordUsageObject(trackedUsage, actor, `openai:${modelDetails.id}`); + this.meteringService.utilRecordUsageObject(trackedUsage, actor, `openai:${modelDetails.id}`); const legacyCostCalculator = OpenAIUtil.create_usage_calculator({ model_details: modelDetails, }); diff --git a/src/backend/src/modules/puterfs/lib/PuterFSProvider.js b/src/backend/src/modules/puterfs/lib/PuterFSProvider.js index afaf9365..2e3f7c55 100644 --- a/src/backend/src/modules/puterfs/lib/PuterFSProvider.js +++ b/src/backend/src/modules/puterfs/lib/PuterFSProvider.js @@ -50,9 +50,9 @@ class PuterFSProvider extends putility.AdvancedBase { return Context.get('services'); } - /** @type {import('../../../services/MeteringService/MeteringService.js').MeteringAndBillingService} */ + /** @type {import('../../../services/MeteringService/MeteringService.js').MeteringService} */ get #meteringService() { - return this.#services.get('meteringService').meteringAndBillingService; + return this.#services.get('meteringService').meteringService; } constructor(...a) { diff --git a/src/backend/src/routers/file.js b/src/backend/src/routers/file.js index 20e7cf3c..6f19c792 100644 --- a/src/backend/src/routers/file.js +++ b/src/backend/src/routers/file.js @@ -30,8 +30,8 @@ const { Actor } = require('../services/auth/Actor'); // -----------------------------------------------------------------------// router.get('/file', async (req, res, next) => { // services and "services" - /** @type {import('../services/MeteringService/MeteringService').MeteringAndBillingService} */ - const meteringService = req.services.get('meteringService').meteringAndBillingService; + /** @type {import('../services/MeteringService/MeteringService').MeteringService} */ + const meteringService = req.services.get('meteringService').meteringService; const log = req.services.get('log-service').create('/file'); const errors = req.services.get('error-service').create(log); const db = req.services.get('database').get(DB_WRITE, 'filesystem'); diff --git a/src/backend/src/services/MeteringService/MeteringService.ts b/src/backend/src/services/MeteringService/MeteringService.ts index fd6095fa..336ab0e5 100644 --- a/src/backend/src/services/MeteringService/MeteringService.ts +++ b/src/backend/src/services/MeteringService/MeteringService.ts @@ -7,17 +7,17 @@ import type { SUService } from '../SUService.js'; import { DEFAULT_FREE_SUBSCRIPTION, DEFAULT_TEMP_SUBSCRIPTION, GLOBAL_APP_KEY, METRICS_PREFIX, PERIOD_ESCAPE, POLICY_PREFIX } from './consts.js'; import { COST_MAPS } from './costMaps/index.js'; import { SUB_POLICIES } from './subPolicies/index.js'; -import { MeteringAndBillingServiceDeps, UsageAddons, UsageByType } from './types.js'; +import { MeteringServiceDeps, UsageAddons, UsageByType } from './types.js'; /** * Handles usage metering and supports stubbs for billing methods for current scoped actor */ -export class MeteringAndBillingService { +export class MeteringService { #kvStore: DBKVStore; #superUserService: SUService; #alarmService: AlarmService; #eventService: EventService; - constructor({ kvStore, superUserService, alarmService, eventService }: MeteringAndBillingServiceDeps) { + constructor({ kvStore, superUserService, alarmService, eventService }: MeteringServiceDeps) { this.#superUserService = superUserService; this.#kvStore = kvStore; this.#alarmService = alarmService; diff --git a/src/backend/src/services/MeteringService/MeteringServiceWrapper.mjs b/src/backend/src/services/MeteringService/MeteringServiceWrapper.mjs index c9cd4b5b..1a8231fc 100644 --- a/src/backend/src/services/MeteringService/MeteringServiceWrapper.mjs +++ b/src/backend/src/services/MeteringService/MeteringServiceWrapper.mjs @@ -1,12 +1,12 @@ import BaseService from '../BaseService.js'; -import { MeteringAndBillingService } from "./MeteringService.js"; +import { MeteringService } from './MeteringService.js'; -export class MeteringAndBillingServiceWrapper extends BaseService { +export class MeteringServiceWrapper extends BaseService { - /** @type {import('./MeteringService.js').MeteringAndBillingService} */ - meteringAndBillingService = undefined; + /** @type {import('./MeteringService.js').MeteringService} */ + meteringService = undefined; _init() { - this.meteringAndBillingService = new MeteringAndBillingService({ + this.meteringService = new MeteringService({ kvStore: this.services.get('puter-kvstore').as('puter-kvstore'), superUserService: this.services.get('su'), alarmService: this.services.get('alarm'), diff --git a/src/backend/src/services/MeteringService/types.ts b/src/backend/src/services/MeteringService/types.ts index 28cc89cb..233755ee 100644 --- a/src/backend/src/services/MeteringService/types.ts +++ b/src/backend/src/services/MeteringService/types.ts @@ -1,7 +1,7 @@ -import type { AlarmService } from "../../modules/core/AlarmService"; -import type { EventService } from "../EventService"; -import type { DBKVStore } from "../repositories/DBKVStore/DBKVStore"; -import type { SUService } from "../SUService"; +import type { AlarmService } from '../../modules/core/AlarmService'; +import type { EventService } from '../EventService'; +import type { DBKVStore } from '../repositories/DBKVStore/DBKVStore'; +import type { SUService } from '../SUService'; export interface UsageAddons { purchasedCredits: number // total extra credits purchased - not expirable @@ -16,7 +16,7 @@ export interface UsageByType { [serviceName: string]: number } -export interface MeteringAndBillingServiceDeps { +export interface MeteringServiceDeps { kvStore: DBKVStore, superUserService: SUService, alarmService: AlarmService diff --git a/src/backend/src/services/repositories/DBKVStore/DBKVStore.ts b/src/backend/src/services/repositories/DBKVStore/DBKVStore.ts index fdda5f0a..6bde2e9a 100644 --- a/src/backend/src/services/repositories/DBKVStore/DBKVStore.ts +++ b/src/backend/src/services/repositories/DBKVStore/DBKVStore.ts @@ -5,20 +5,20 @@ import APIError from '../../../api/APIError.js'; // eslint-disable-next-line @typescript-eslint/ban-ts-comment // @ts-ignore import { Context } from '../../../util/context.js'; -import type { MeteringAndBillingService } from '../../MeteringService/MeteringService.js'; +import type { MeteringService } from '../../MeteringService/MeteringService.js'; const GLOBAL_APP_KEY = 'global'; export class DBKVStore { // eslint-disable-next-line @typescript-eslint/no-explicit-any #db: any; - #meteringService: MeteringAndBillingService; + #meteringService: MeteringService; #global_config: Record = {}; // TODO DS: make table name configurable - constructor({ sqlClient, meteringAndBillingService, globalConfig }: { sqlClient: unknown, meteringAndBillingService: MeteringAndBillingService, globalConfig: Record }) { + constructor({ sqlClient, meteringService, globalConfig }: { sqlClient: unknown, meteringService: MeteringService, globalConfig: Record }) { this.#db = sqlClient; - this.#meteringService = meteringAndBillingService; + this.#meteringService = meteringService; this.#global_config = globalConfig; } diff --git a/src/backend/src/services/repositories/DBKVStore/index.mjs b/src/backend/src/services/repositories/DBKVStore/index.mjs index 0eff0335..0aa8845d 100644 --- a/src/backend/src/services/repositories/DBKVStore/index.mjs +++ b/src/backend/src/services/repositories/DBKVStore/index.mjs @@ -8,7 +8,7 @@ export class DBKVServiceWrapper extends BaseService { /** @type {DBKVStore} */ this.kvStore = new DBKVStore({ sqlClient: this.services.get('database').get(DB_READ, 'kvstore'), - meteringAndBillingService: this.services.get('meteringService').meteringAndBillingService, + meteringService: this.services.get('meteringService').meteringService, globalConfig: this.global_config, }); } diff --git a/src/puter-js/src/modules/Auth.js b/src/puter-js/src/modules/Auth.js index 20795b20..afdd218e 100644 --- a/src/puter-js/src/modules/Auth.js +++ b/src/puter-js/src/modules/Auth.js @@ -174,7 +174,7 @@ class Auth{ async getMonthlyUsage() { try { - const resp = await fetch(`${this.APIOrigin}/meteringAndBilling/usage`, { + const resp = await fetch(`${this.APIOrigin}/metering/usage`, { headers: { Authorization: `Bearer ${this.authToken}`, }, @@ -216,7 +216,7 @@ class Auth{ } try { - const resp = await fetch(`${this.APIOrigin}/meteringAndBilling/usage/${appId}`, { + const resp = await fetch(`${this.APIOrigin}/metering/usage/${appId}`, { headers: { Authorization: `Bearer ${this.authToken}`, },