feat: rename meteringAndBilling to metering (#1800)

This commit is contained in:
Daniel Salazar
2025-10-21 14:59:24 -07:00
committed by GitHub
parent 435d26608a
commit 16fd614b84
26 changed files with 276 additions and 278 deletions

View File

@@ -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');
console.debug('Loaded /metering/usage route');

View File

@@ -17,14 +17,14 @@
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
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);

View File

@@ -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 ) {

View File

@@ -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 ) {

View File

@@ -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',

View File

@@ -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;
}

View File

@@ -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<void>}
*/
/** @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}`);
};
/**

View File

@@ -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,
});

View File

@@ -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;
}
}

View File

@@ -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;
}
},

View File

@@ -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,
},
},
];

View File

@@ -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,

View File

@@ -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');

View File

@@ -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,
});

View File

@@ -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,
});
}

View File

@@ -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,
});

View File

@@ -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;
},
},

View File

@@ -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,
});

View File

@@ -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) {

View File

@@ -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');

View File

@@ -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;

View File

@@ -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'),

View File

@@ -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

View File

@@ -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<string, unknown> = {};
// TODO DS: make table name configurable
constructor({ sqlClient, meteringAndBillingService, globalConfig }: { sqlClient: unknown, meteringAndBillingService: MeteringAndBillingService, globalConfig: Record<string, unknown> }) {
constructor({ sqlClient, meteringService, globalConfig }: { sqlClient: unknown, meteringService: MeteringService, globalConfig: Record<string, unknown> }) {
this.#db = sqlClient;
this.#meteringService = meteringAndBillingService;
this.#meteringService = meteringService;
this.#global_config = globalConfig;
}

View File

@@ -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,
});
}

View File

@@ -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}`,
},