mirror of
https://github.com/HeyPuter/puter.git
synced 2026-01-05 20:50:22 -06:00
feat: rename meteringAndBilling to metering (#1800)
This commit is contained in:
@@ -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');
|
||||
@@ -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);
|
||||
|
||||
@@ -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 ) {
|
||||
|
||||
@@ -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 ) {
|
||||
|
||||
@@ -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',
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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}`);
|
||||
};
|
||||
|
||||
/**
|
||||
|
||||
@@ -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,
|
||||
});
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
},
|
||||
|
||||
@@ -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,
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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');
|
||||
|
||||
@@ -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,
|
||||
});
|
||||
|
||||
@@ -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,
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -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,
|
||||
});
|
||||
|
||||
@@ -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;
|
||||
},
|
||||
},
|
||||
|
||||
@@ -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,
|
||||
});
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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');
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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'),
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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,
|
||||
});
|
||||
}
|
||||
|
||||
@@ -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}`,
|
||||
},
|
||||
|
||||
Reference in New Issue
Block a user