@casual-simulation/aux-records 4.0.5 → 4.1.0-alpha.21993718399
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/AIChatInterface.d.ts +16 -0
- package/AIChatInterface.js.map +1 -1
- package/AIController.d.ts +13 -6
- package/AIController.js +263 -33
- package/AIController.js.map +1 -1
- package/AnthropicAIChatInterface.js +4 -0
- package/AnthropicAIChatInterface.js.map +1 -1
- package/ConfigurationStore.d.ts +84 -48
- package/DataRecordsController.d.ts +8 -5
- package/DataRecordsController.js +34 -1
- package/DataRecordsController.js.map +1 -1
- package/FileRecordsController.d.ts +7 -4
- package/FileRecordsController.js +18 -1
- package/FileRecordsController.js.map +1 -1
- package/MemoryStore.d.ts +9 -1
- package/MemoryStore.js +46 -0
- package/MemoryStore.js.map +1 -1
- package/MetricsStore.d.ts +12 -0
- package/OpenAIChatInterface.js +4 -0
- package/OpenAIChatInterface.js.map +1 -1
- package/RecordsServer.d.ts +219 -219
- package/ServerConfig.d.ts +328 -48
- package/ServerConfig.js +198 -27
- package/ServerConfig.js.map +1 -1
- package/SubscriptionConfigBuilder.d.ts +4 -3
- package/SubscriptionConfigBuilder.js +7 -7
- package/SubscriptionConfigBuilder.js.map +1 -1
- package/SubscriptionConfiguration.d.ts +171 -112
- package/SubscriptionConfiguration.js +60 -34
- package/SubscriptionConfiguration.js.map +1 -1
- package/TestUtils.d.ts +2 -1
- package/TestUtils.js +17 -1
- package/TestUtils.js.map +1 -1
- package/financial/FinancialController.d.ts +125 -2
- package/financial/FinancialController.js +312 -4
- package/financial/FinancialController.js.map +1 -1
- package/financial/FinancialInterface.d.ts +92 -2
- package/financial/FinancialInterface.js +91 -0
- package/financial/FinancialInterface.js.map +1 -1
- package/financial/FinancialProcessor.d.ts +57 -0
- package/financial/FinancialProcessor.js +431 -0
- package/financial/FinancialProcessor.js.map +1 -0
- package/financial/FinancialStore.d.ts +24 -0
- package/financial/MemoryFinancialInterface.d.ts +8 -0
- package/financial/MemoryFinancialInterface.js +49 -6
- package/financial/MemoryFinancialInterface.js.map +1 -1
- package/financial/index.d.ts +1 -0
- package/financial/index.js +1 -0
- package/financial/index.js.map +1 -1
- package/notifications/MemoryNotificationRecordsStore.d.ts +1 -0
- package/notifications/MemoryNotificationRecordsStore.js +15 -1
- package/notifications/MemoryNotificationRecordsStore.js.map +1 -1
- package/notifications/NotificationRecordsController.d.ts +6 -0
- package/notifications/NotificationRecordsController.js +149 -1
- package/notifications/NotificationRecordsController.js.map +1 -1
- package/notifications/NotificationRecordsStore.d.ts +8 -0
- package/package.json +2 -2
package/AIChatInterface.d.ts
CHANGED
|
@@ -73,6 +73,14 @@ export interface AIChatInterfaceResponse {
|
|
|
73
73
|
* The total number of tokens that were used.
|
|
74
74
|
*/
|
|
75
75
|
totalTokens: number;
|
|
76
|
+
/**
|
|
77
|
+
* The number of input tokens that were used.
|
|
78
|
+
*/
|
|
79
|
+
inputTokens?: number;
|
|
80
|
+
/**
|
|
81
|
+
* The number of output tokens that were used.
|
|
82
|
+
*/
|
|
83
|
+
outputTokens?: number;
|
|
76
84
|
}
|
|
77
85
|
export interface AIChatInterfaceStreamResponse {
|
|
78
86
|
/**
|
|
@@ -83,6 +91,14 @@ export interface AIChatInterfaceStreamResponse {
|
|
|
83
91
|
* The total number of tokens that were used.
|
|
84
92
|
*/
|
|
85
93
|
totalTokens: number;
|
|
94
|
+
/**
|
|
95
|
+
* The number of input tokens that were used.
|
|
96
|
+
*/
|
|
97
|
+
inputTokens?: number;
|
|
98
|
+
/**
|
|
99
|
+
* The number of output tokens that were used.
|
|
100
|
+
*/
|
|
101
|
+
outputTokens?: number;
|
|
86
102
|
}
|
|
87
103
|
export interface AIChatStreamMessage {
|
|
88
104
|
/**
|
package/AIChatInterface.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"AIChatInterface.js","sourceRoot":"","sources":["AIChatInterface.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AACH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;
|
|
1
|
+
{"version":3,"file":"AIChatInterface.js","sourceRoot":"","sources":["AIChatInterface.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AACH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAqQxB;;GAEG;AACH,MAAM,CAAC,MAAM,sBAAsB,GAAG,CAAC,CAAC,MAAM,CAAC;IAC3C,IAAI,EAAE,CAAC,CAAC,KAAK,CAAC;QACV,CAAC,CAAC,OAAO,CAAC,QAAQ,CAAC;QACnB,CAAC,CAAC,OAAO,CAAC,MAAM,CAAC;QACjB,CAAC,CAAC,OAAO,CAAC,WAAW,CAAC;QACtB,CAAC,CAAC,OAAO,CAAC,UAAU,CAAC;KACxB,CAAC;IACF,OAAO,EAAE,CAAC,CAAC,KAAK,CAAC;QACb,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;QACrB,CAAC;aACI,KAAK,CACF,CAAC,CAAC,KAAK,CAAC;YACJ,CAAC,CAAC,MAAM,CAAC;gBACL,IAAI,EAAE,CAAC,CAAC,MAAM,EAAE;aACnB,CAAC;YACF,CAAC,CAAC,MAAM,CAAC;gBACL,MAAM,EAAE,CAAC,CAAC,MAAM,EAAE;gBAClB,QAAQ,EAAE,CAAC,CAAC,MAAM,EAAE;aACvB,CAAC;YACF,CAAC,CAAC,MAAM,CAAC;gBACL,GAAG,EAAE,CAAC,CAAC,GAAG,EAAE;aACf,CAAC;SACL,CAAC,CACL;aACA,GAAG,CAAC,CAAC,CAAC;KACd,CAAC;IACF,MAAM,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE,CAAC,QAAQ,EAAE;CAC3C,CAAC,CAAC"}
|
package/AIController.d.ts
CHANGED
|
@@ -5,13 +5,14 @@ import type { AIGeneratedImage, AIImageInterface } from './AIImageInterface';
|
|
|
5
5
|
import type { MetricsStore } from './MetricsStore';
|
|
6
6
|
import type { ConfigurationStore } from './ConfigurationStore';
|
|
7
7
|
import type { PolicyStore } from './PolicyStore';
|
|
8
|
-
import type { AIHumeInterface
|
|
8
|
+
import type { AIHumeInterface } from './AIHumeInterface';
|
|
9
9
|
import type { AISloydInterface, AISloydInterfaceCreateModelFailure } from './AISloydInterface';
|
|
10
|
-
import type { AuthorizeSubjectFailure,
|
|
10
|
+
import type { AuthorizeSubjectFailure, PolicyController } from './PolicyController';
|
|
11
11
|
import type { UserRole } from '@casual-simulation/aux-common';
|
|
12
12
|
import { type DenialReason, type KnownErrorCodes, type Result, type SimpleError } from '@casual-simulation/aux-common';
|
|
13
13
|
import type { HumeConfig, RecordsStore } from './RecordsStore';
|
|
14
14
|
import type { AIOpenAIRealtimeInterface, CreateRealtimeSessionTokenRequest } from './AIOpenAIRealtimeInterface';
|
|
15
|
+
import { type FinancialController } from './financial/FinancialController';
|
|
15
16
|
export interface AIConfiguration {
|
|
16
17
|
chat: AIChatConfiguration | null;
|
|
17
18
|
generateSkybox: AIGenerateSkyboxConfiguration | null;
|
|
@@ -26,6 +27,7 @@ export interface AIConfiguration {
|
|
|
26
27
|
openai: {
|
|
27
28
|
realtime: AIOpenAIRealtimeConfiguration;
|
|
28
29
|
} | null;
|
|
30
|
+
financial?: FinancialController | null;
|
|
29
31
|
}
|
|
30
32
|
export interface AIChatConfiguration {
|
|
31
33
|
interfaces: AIChatProviders;
|
|
@@ -185,8 +187,13 @@ export declare class AIController {
|
|
|
185
187
|
private _policyStore;
|
|
186
188
|
private _policies;
|
|
187
189
|
private _recordsStore;
|
|
190
|
+
private _financial;
|
|
188
191
|
constructor(configuration: AIConfiguration);
|
|
189
192
|
chat(request: AIChatRequest): Promise<AIChatResponse>;
|
|
193
|
+
/**
|
|
194
|
+
* Calculates the final billing cost for a chat request given the token usage.
|
|
195
|
+
*/
|
|
196
|
+
private _calculateChatBillingCost;
|
|
190
197
|
private _calculateTokenCost;
|
|
191
198
|
chatStream(request: AIChatRequest): AsyncGenerator<Pick<AIChatInterfaceStreamResponse, 'choices'>, AIChatStreamResponse>;
|
|
192
199
|
generateSkybox(request: AIGenerateSkyboxRequest): Promise<AIGenerateSkyboxResponse>;
|
|
@@ -257,7 +264,7 @@ export interface AIChatSuccess {
|
|
|
257
264
|
}
|
|
258
265
|
export interface AIChatFailure {
|
|
259
266
|
success: false;
|
|
260
|
-
errorCode:
|
|
267
|
+
errorCode: KnownErrorCodes;
|
|
261
268
|
errorMessage: string;
|
|
262
269
|
allowedSubscriptionTiers?: string[];
|
|
263
270
|
currentSubscriptionTier?: string;
|
|
@@ -296,7 +303,7 @@ export interface AIGenerateSkyboxSuccess {
|
|
|
296
303
|
}
|
|
297
304
|
export interface AIGenerateSkyboxFailure {
|
|
298
305
|
success: false;
|
|
299
|
-
errorCode:
|
|
306
|
+
errorCode: KnownErrorCodes;
|
|
300
307
|
errorMessage: string;
|
|
301
308
|
allowedSubscriptionTiers?: string[];
|
|
302
309
|
currentSubscriptionTier?: string;
|
|
@@ -400,7 +407,7 @@ export interface AIGenerateImageSuccess {
|
|
|
400
407
|
}
|
|
401
408
|
export interface AIGenerateImageFailure {
|
|
402
409
|
success: false;
|
|
403
|
-
errorCode:
|
|
410
|
+
errorCode: KnownErrorCodes;
|
|
404
411
|
errorMessage: string;
|
|
405
412
|
allowedSubscriptionTiers?: string[];
|
|
406
413
|
currentSubscriptionTier?: string;
|
|
@@ -438,7 +445,7 @@ export interface AIHumeGetAccessTokenSuccess {
|
|
|
438
445
|
}
|
|
439
446
|
export interface AIHumeGetAccessTokenFailure {
|
|
440
447
|
success: false;
|
|
441
|
-
errorCode:
|
|
448
|
+
errorCode: KnownErrorCodes;
|
|
442
449
|
errorMessage: string;
|
|
443
450
|
}
|
|
444
451
|
export interface AISloydGenerateModelRequest {
|
package/AIController.js
CHANGED
|
@@ -8,7 +8,9 @@ import { getHumeAiFeatures, getOpenAiFeatures, getSloydAiFeatures, getSubscripti
|
|
|
8
8
|
import { traced } from './tracing/TracingDecorators';
|
|
9
9
|
import { SpanStatusCode, trace } from '@opentelemetry/api';
|
|
10
10
|
import { fromByteArray } from 'base64-js';
|
|
11
|
-
import { failure, isSuperUserRole, success, } from '@casual-simulation/aux-common';
|
|
11
|
+
import { failure, genericResult, isFailure, isSuperUserRole, logError, success, wrap, } from '@casual-simulation/aux-common';
|
|
12
|
+
import { billForUsage, } from './financial/FinancialController';
|
|
13
|
+
import { BillingCodes, TransferCodes } from './financial/FinancialInterface';
|
|
12
14
|
const TRACE_NAME = 'AIController';
|
|
13
15
|
/**
|
|
14
16
|
* Defines a class that is able to handle AI requests.
|
|
@@ -61,6 +63,7 @@ export class AIController {
|
|
|
61
63
|
this._config = configuration.config;
|
|
62
64
|
this._policyStore = configuration.policies;
|
|
63
65
|
this._recordsStore = configuration.records;
|
|
66
|
+
this._financial = configuration.financial;
|
|
64
67
|
}
|
|
65
68
|
async chat(request) {
|
|
66
69
|
try {
|
|
@@ -179,7 +182,26 @@ export class AIController {
|
|
|
179
182
|
};
|
|
180
183
|
}
|
|
181
184
|
}
|
|
182
|
-
const
|
|
185
|
+
const creditFeePerInputToken = allowedFeatures.ai.chat.creditFeePerInputToken ?? null;
|
|
186
|
+
const creditFeePerOutputToken = allowedFeatures.ai.chat.creditFeePerOutputToken ?? null;
|
|
187
|
+
const preChargeInputTokens = BigInt(this._calculateTokenCost(allowedFeatures.ai.chat.preChargeInputTokens ?? 100, model));
|
|
188
|
+
const preChargeOutputTokens = BigInt(this._calculateTokenCost(allowedFeatures.ai.chat.preChargeOutputTokens ?? 100, model));
|
|
189
|
+
const initialAmount = creditFeePerInputToken || creditFeePerOutputToken
|
|
190
|
+
? preChargeInputTokens * (creditFeePerInputToken ?? 0n) +
|
|
191
|
+
preChargeOutputTokens * (creditFeePerOutputToken ?? 0n)
|
|
192
|
+
: null;
|
|
193
|
+
const billing = await billForUsage(this._financial, {
|
|
194
|
+
userId: request.userId,
|
|
195
|
+
transferCode: TransferCodes.records_usage_fee,
|
|
196
|
+
billingCode: BillingCodes.ai_chat_tokens,
|
|
197
|
+
});
|
|
198
|
+
const initialResult = await billing.next(success({
|
|
199
|
+
initialCost: initialAmount,
|
|
200
|
+
}));
|
|
201
|
+
if (isFailure(initialResult.value)) {
|
|
202
|
+
return genericResult(initialResult.value);
|
|
203
|
+
}
|
|
204
|
+
const chatResult = await wrap(async () => await chat.chat({
|
|
183
205
|
messages: request.messages,
|
|
184
206
|
model: model,
|
|
185
207
|
temperature: request.temperature,
|
|
@@ -189,18 +211,34 @@ export class AIController {
|
|
|
189
211
|
stopWords: request.stopWords,
|
|
190
212
|
userId: request.userId,
|
|
191
213
|
maxTokens,
|
|
192
|
-
});
|
|
193
|
-
if (
|
|
194
|
-
|
|
214
|
+
}));
|
|
215
|
+
if (isFailure(chatResult)) {
|
|
216
|
+
console.error('[AIController] Chat request failed:', chatResult);
|
|
217
|
+
// Need to pass failure to billing to ensure that it cancels pending transfers
|
|
218
|
+
const errorResult = await billing.next(failure({
|
|
219
|
+
errorCode: 'server_error',
|
|
220
|
+
errorMessage: 'A server error occurred.',
|
|
221
|
+
}));
|
|
222
|
+
return genericResult(errorResult.value);
|
|
223
|
+
}
|
|
224
|
+
const cost = this._calculateChatBillingCost(chatResult.value, creditFeePerInputToken, creditFeePerOutputToken, model);
|
|
225
|
+
if (chatResult.value.totalTokens > 0) {
|
|
226
|
+
const adjustedTotalTokens = this._calculateTokenCost(chatResult.value.totalTokens, model);
|
|
195
227
|
await this._metrics.recordChatMetrics({
|
|
196
228
|
userId: request.userId,
|
|
197
229
|
createdAtMs: Date.now(),
|
|
198
|
-
tokens:
|
|
230
|
+
tokens: adjustedTotalTokens,
|
|
199
231
|
});
|
|
200
232
|
}
|
|
233
|
+
const finalResult = await billing.next(success({
|
|
234
|
+
cost,
|
|
235
|
+
}));
|
|
236
|
+
if (isFailure(finalResult.value)) {
|
|
237
|
+
return genericResult(finalResult.value);
|
|
238
|
+
}
|
|
201
239
|
return {
|
|
202
240
|
success: true,
|
|
203
|
-
choices:
|
|
241
|
+
choices: chatResult.value.choices,
|
|
204
242
|
};
|
|
205
243
|
}
|
|
206
244
|
catch (err) {
|
|
@@ -215,11 +253,34 @@ export class AIController {
|
|
|
215
253
|
};
|
|
216
254
|
}
|
|
217
255
|
}
|
|
218
|
-
|
|
219
|
-
|
|
256
|
+
/**
|
|
257
|
+
* Calculates the final billing cost for a chat request given the token usage.
|
|
258
|
+
*/
|
|
259
|
+
_calculateChatBillingCost(chatResult, creditFeePerInputToken, creditFeePerOutputToken, model) {
|
|
260
|
+
let cost = 0n;
|
|
261
|
+
if (chatResult.inputTokens > 0 && creditFeePerInputToken) {
|
|
262
|
+
const adjustedInputTokens = this._calculateTokenCost(chatResult.inputTokens, model);
|
|
263
|
+
cost += BigInt(adjustedInputTokens) * creditFeePerInputToken;
|
|
264
|
+
}
|
|
265
|
+
if (chatResult.outputTokens > 0 && creditFeePerOutputToken) {
|
|
266
|
+
const adjustedOutputTokens = this._calculateTokenCost(chatResult.outputTokens, model);
|
|
267
|
+
cost += BigInt(adjustedOutputTokens) * creditFeePerOutputToken;
|
|
268
|
+
}
|
|
269
|
+
if (!chatResult.inputTokens &&
|
|
270
|
+
!chatResult.outputTokens &&
|
|
271
|
+
chatResult.totalTokens > 0) {
|
|
272
|
+
// Fallback in case the interface doesn't provide input/output token breakdown
|
|
273
|
+
const adjustedTokens = this._calculateTokenCost(chatResult.totalTokens, model);
|
|
274
|
+
cost =
|
|
275
|
+
BigInt(adjustedTokens) *
|
|
276
|
+
(creditFeePerOutputToken ?? creditFeePerInputToken ?? 0n);
|
|
277
|
+
}
|
|
278
|
+
return cost;
|
|
279
|
+
}
|
|
280
|
+
_calculateTokenCost(tokens, model) {
|
|
220
281
|
const tokenModifierRatio = this._chatOptions.tokenModifierRatio;
|
|
221
|
-
const modifier = tokenModifierRatio[model] ?? 1
|
|
222
|
-
const adjustedTokens = modifier *
|
|
282
|
+
const modifier = tokenModifierRatio[model] ?? 1;
|
|
283
|
+
const adjustedTokens = modifier * tokens;
|
|
223
284
|
return adjustedTokens;
|
|
224
285
|
}
|
|
225
286
|
async *chatStream(request) {
|
|
@@ -347,6 +408,25 @@ export class AIController {
|
|
|
347
408
|
};
|
|
348
409
|
}
|
|
349
410
|
}
|
|
411
|
+
const creditFeePerInputToken = allowedFeatures.ai.chat.creditFeePerInputToken ?? null;
|
|
412
|
+
const creditFeePerOutputToken = allowedFeatures.ai.chat.creditFeePerOutputToken ?? null;
|
|
413
|
+
const preChargeInputTokens = BigInt(this._calculateTokenCost(allowedFeatures.ai.chat.preChargeInputTokens ?? 100, model));
|
|
414
|
+
const preChargeOutputTokens = BigInt(this._calculateTokenCost(allowedFeatures.ai.chat.preChargeOutputTokens ?? 100, model));
|
|
415
|
+
const initialAmount = creditFeePerInputToken || creditFeePerOutputToken
|
|
416
|
+
? preChargeInputTokens * (creditFeePerInputToken ?? 0n) +
|
|
417
|
+
preChargeOutputTokens * (creditFeePerOutputToken ?? 0n)
|
|
418
|
+
: null;
|
|
419
|
+
const billing = await billForUsage(this._financial, {
|
|
420
|
+
userId: request.userId,
|
|
421
|
+
transferCode: TransferCodes.records_usage_fee,
|
|
422
|
+
billingCode: BillingCodes.ai_chat_tokens,
|
|
423
|
+
});
|
|
424
|
+
const initialResult = await billing.next(success({
|
|
425
|
+
initialCost: initialAmount,
|
|
426
|
+
}));
|
|
427
|
+
if (isFailure(initialResult.value)) {
|
|
428
|
+
return genericResult(initialResult.value);
|
|
429
|
+
}
|
|
350
430
|
const result = chat.chatStream({
|
|
351
431
|
messages: request.messages,
|
|
352
432
|
model: model,
|
|
@@ -358,9 +438,15 @@ export class AIController {
|
|
|
358
438
|
userId: request.userId,
|
|
359
439
|
maxTokens,
|
|
360
440
|
});
|
|
441
|
+
let totalTokens = 0;
|
|
442
|
+
let totalInputTokens = 0;
|
|
443
|
+
let totalOutputTokens = 0;
|
|
361
444
|
for await (let chunk of result) {
|
|
445
|
+
totalTokens += chunk.totalTokens;
|
|
446
|
+
totalInputTokens += chunk.inputTokens;
|
|
447
|
+
totalOutputTokens += chunk.outputTokens;
|
|
362
448
|
if (chunk.totalTokens > 0) {
|
|
363
|
-
const adjustedTokens = this._calculateTokenCost(chunk, model);
|
|
449
|
+
const adjustedTokens = this._calculateTokenCost(chunk.totalTokens, model);
|
|
364
450
|
await this._metrics.recordChatMetrics({
|
|
365
451
|
userId: request.userId,
|
|
366
452
|
createdAtMs: Date.now(),
|
|
@@ -371,6 +457,17 @@ export class AIController {
|
|
|
371
457
|
choices: chunk.choices,
|
|
372
458
|
};
|
|
373
459
|
}
|
|
460
|
+
const cost = this._calculateChatBillingCost({
|
|
461
|
+
totalTokens,
|
|
462
|
+
inputTokens: totalInputTokens,
|
|
463
|
+
outputTokens: totalOutputTokens,
|
|
464
|
+
}, creditFeePerInputToken, creditFeePerOutputToken, model);
|
|
465
|
+
const finalResult = await billing.next(success({
|
|
466
|
+
cost,
|
|
467
|
+
}));
|
|
468
|
+
if (isFailure(finalResult.value)) {
|
|
469
|
+
return genericResult(finalResult.value);
|
|
470
|
+
}
|
|
374
471
|
return {
|
|
375
472
|
success: true,
|
|
376
473
|
};
|
|
@@ -459,24 +556,61 @@ export class AIController {
|
|
|
459
556
|
};
|
|
460
557
|
}
|
|
461
558
|
}
|
|
462
|
-
const
|
|
559
|
+
const creditFeePerSkybox = allowedFeatures.ai.skyboxes.creditFeePerSkybox ?? null;
|
|
560
|
+
const amount = creditFeePerSkybox
|
|
561
|
+
? BigInt(creditFeePerSkybox)
|
|
562
|
+
: null;
|
|
563
|
+
const billing = await billForUsage(this._financial, {
|
|
564
|
+
userId: request.userId,
|
|
565
|
+
transferCode: TransferCodes.records_usage_fee,
|
|
566
|
+
billingCode: BillingCodes.ai_skybox,
|
|
567
|
+
});
|
|
568
|
+
const initialResult = await billing.next(success({
|
|
569
|
+
initialCost: amount,
|
|
570
|
+
}));
|
|
571
|
+
if (isFailure(initialResult.value)) {
|
|
572
|
+
return genericResult(initialResult.value);
|
|
573
|
+
}
|
|
574
|
+
const result = await wrap(async () => await this._generateSkybox.generateSkybox({
|
|
463
575
|
prompt: request.prompt,
|
|
464
576
|
negativePrompt: request.negativePrompt,
|
|
465
577
|
blockadeLabs: request.blockadeLabs,
|
|
466
|
-
});
|
|
467
|
-
if (result
|
|
578
|
+
}));
|
|
579
|
+
if (isFailure(result)) {
|
|
580
|
+
logError(result.error, '[AIController] Skybox generation error:');
|
|
581
|
+
// Need to pass failure to billing to ensure that it cancels pending transfers
|
|
582
|
+
await billing.next(failure({
|
|
583
|
+
errorCode: 'server_error',
|
|
584
|
+
errorMessage: 'A server error occurred.',
|
|
585
|
+
}));
|
|
586
|
+
return {
|
|
587
|
+
success: false,
|
|
588
|
+
errorCode: 'server_error',
|
|
589
|
+
errorMessage: 'A server error occurred.',
|
|
590
|
+
};
|
|
591
|
+
}
|
|
592
|
+
if (result.value.success === true) {
|
|
468
593
|
await this._metrics.recordSkyboxMetrics({
|
|
469
594
|
userId: request.userId,
|
|
470
595
|
createdAtMs: Date.now(),
|
|
471
596
|
skyboxes: 1,
|
|
472
597
|
});
|
|
598
|
+
const finalResult = await billing.next(success({
|
|
599
|
+
cost: amount,
|
|
600
|
+
}));
|
|
601
|
+
if (isFailure(finalResult.value)) {
|
|
602
|
+
return genericResult(finalResult.value);
|
|
603
|
+
}
|
|
473
604
|
return {
|
|
474
605
|
success: true,
|
|
475
|
-
skyboxId: result.skyboxId,
|
|
606
|
+
skyboxId: result.value.skyboxId,
|
|
476
607
|
};
|
|
477
608
|
}
|
|
478
609
|
else {
|
|
479
|
-
|
|
610
|
+
// Pass the skybox generation error to billing
|
|
611
|
+
await billing.next(failure(result.value));
|
|
612
|
+
// Return the original skybox error, not the billing error
|
|
613
|
+
return result.value;
|
|
480
614
|
}
|
|
481
615
|
}
|
|
482
616
|
catch (err) {
|
|
@@ -628,7 +762,7 @@ export class AIController {
|
|
|
628
762
|
const width = Math.min(request.width ?? this._imageOptions.defaultWidth, this._imageOptions.maxWidth);
|
|
629
763
|
const height = Math.min(request.height ?? this._imageOptions.defaultHeight, this._imageOptions.maxHeight);
|
|
630
764
|
const numberOfImages = Math.min(request.numberOfImages ?? 1, this._imageOptions.maxImages);
|
|
631
|
-
const
|
|
765
|
+
const totalSquarePixels = Math.max(width, height) * numberOfImages;
|
|
632
766
|
const metrics = await this._metrics.getSubscriptionAiImageMetrics({
|
|
633
767
|
ownerId: request.userId,
|
|
634
768
|
});
|
|
@@ -642,7 +776,7 @@ export class AIController {
|
|
|
642
776
|
};
|
|
643
777
|
}
|
|
644
778
|
if (allowedFeatures.ai.images.maxSquarePixelsPerRequest > 0 &&
|
|
645
|
-
|
|
779
|
+
totalSquarePixels >
|
|
646
780
|
allowedFeatures.ai.images.maxSquarePixelsPerRequest) {
|
|
647
781
|
return {
|
|
648
782
|
success: false,
|
|
@@ -651,7 +785,7 @@ export class AIController {
|
|
|
651
785
|
};
|
|
652
786
|
}
|
|
653
787
|
if (allowedFeatures.ai.images.maxSquarePixelsPerPeriod > 0 &&
|
|
654
|
-
|
|
788
|
+
totalSquarePixels + metrics.totalSquarePixelsInCurrentPeriod >
|
|
655
789
|
allowedFeatures.ai.images.maxSquarePixelsPerPeriod) {
|
|
656
790
|
return {
|
|
657
791
|
success: false,
|
|
@@ -669,7 +803,22 @@ export class AIController {
|
|
|
669
803
|
};
|
|
670
804
|
}
|
|
671
805
|
}
|
|
672
|
-
const
|
|
806
|
+
const creditFeePerSquarePixel = allowedFeatures.ai.images.creditFeePerSquarePixel ?? null;
|
|
807
|
+
const amount = creditFeePerSquarePixel
|
|
808
|
+
? BigInt(Math.ceil(totalSquarePixels * creditFeePerSquarePixel))
|
|
809
|
+
: null;
|
|
810
|
+
const billing = await billForUsage(this._financial, {
|
|
811
|
+
userId: request.userId,
|
|
812
|
+
transferCode: TransferCodes.records_usage_fee,
|
|
813
|
+
billingCode: BillingCodes.ai_image_pixels,
|
|
814
|
+
});
|
|
815
|
+
const initialResult = await billing.next(success({
|
|
816
|
+
initialCost: amount,
|
|
817
|
+
}));
|
|
818
|
+
if (isFailure(initialResult.value)) {
|
|
819
|
+
return genericResult(initialResult.value);
|
|
820
|
+
}
|
|
821
|
+
const result = await wrap(async () => await provider.generateImage({
|
|
673
822
|
model: model,
|
|
674
823
|
prompt: request.prompt,
|
|
675
824
|
negativePrompt: request.negativePrompt,
|
|
@@ -683,18 +832,39 @@ export class AIController {
|
|
|
683
832
|
clipGuidancePreset: request.clipGuidancePreset,
|
|
684
833
|
stylePreset: request.stylePreset,
|
|
685
834
|
userId: request.userId,
|
|
686
|
-
});
|
|
687
|
-
if (
|
|
688
|
-
|
|
835
|
+
}));
|
|
836
|
+
if (isFailure(result)) {
|
|
837
|
+
logError(result.error, `[AIController] Generate image error:`);
|
|
838
|
+
// Need to pass failure to billing to ensure that it cancels pending transfers
|
|
839
|
+
await billing.next(failure({
|
|
840
|
+
errorCode: 'server_error',
|
|
841
|
+
errorMessage: 'A server error occurred.',
|
|
842
|
+
}));
|
|
843
|
+
return {
|
|
844
|
+
success: false,
|
|
845
|
+
errorCode: 'server_error',
|
|
846
|
+
errorMessage: 'A server error occurred.',
|
|
847
|
+
};
|
|
848
|
+
}
|
|
849
|
+
if (result.value.success === false) {
|
|
850
|
+
// Pass the image generation error to billing
|
|
851
|
+
await billing.next(failure(result.value));
|
|
852
|
+
return result.value;
|
|
689
853
|
}
|
|
690
854
|
await this._metrics.recordImageMetrics({
|
|
691
855
|
userId: request.userId,
|
|
692
856
|
createdAtMs: Date.now(),
|
|
693
|
-
squarePixels:
|
|
857
|
+
squarePixels: totalSquarePixels,
|
|
694
858
|
});
|
|
859
|
+
const finalResult = await billing.next(success({
|
|
860
|
+
cost: amount,
|
|
861
|
+
}));
|
|
862
|
+
if (isFailure(finalResult.value)) {
|
|
863
|
+
return genericResult(finalResult.value);
|
|
864
|
+
}
|
|
695
865
|
return {
|
|
696
866
|
success: true,
|
|
697
|
-
images: result.images,
|
|
867
|
+
images: result.value.images,
|
|
698
868
|
};
|
|
699
869
|
}
|
|
700
870
|
catch (err) {
|
|
@@ -789,11 +959,30 @@ export class AIController {
|
|
|
789
959
|
};
|
|
790
960
|
}
|
|
791
961
|
}
|
|
962
|
+
const fee = features.creditFeePerAccessToken;
|
|
963
|
+
const billing = await billForUsage(this._financial, {
|
|
964
|
+
userId: context.context.recordOwnerId,
|
|
965
|
+
studioId: context.context.recordStudioId,
|
|
966
|
+
transferCode: TransferCodes.records_usage_fee,
|
|
967
|
+
billingCode: BillingCodes.ai_hume_access_token,
|
|
968
|
+
});
|
|
969
|
+
const initialResult = await billing.next(success({
|
|
970
|
+
initialCost: fee,
|
|
971
|
+
}));
|
|
972
|
+
if (isFailure(initialResult.value)) {
|
|
973
|
+
return genericResult(initialResult.value);
|
|
974
|
+
}
|
|
792
975
|
const result = await this._humeInterface.getAccessToken({
|
|
793
976
|
apiKey: humeConfig.apiKey,
|
|
794
977
|
secretKey: humeConfig.secretKey,
|
|
795
978
|
});
|
|
796
|
-
if (result.success) {
|
|
979
|
+
if (result.success === true) {
|
|
980
|
+
const costResult = await billing.next(success({
|
|
981
|
+
cost: fee,
|
|
982
|
+
}));
|
|
983
|
+
if (isFailure(costResult.value)) {
|
|
984
|
+
return genericResult(costResult.value);
|
|
985
|
+
}
|
|
797
986
|
return {
|
|
798
987
|
success: true,
|
|
799
988
|
accessToken: result.accessToken,
|
|
@@ -803,6 +992,10 @@ export class AIController {
|
|
|
803
992
|
};
|
|
804
993
|
}
|
|
805
994
|
else {
|
|
995
|
+
const cancelResult = await billing.next(failure(result));
|
|
996
|
+
if (isFailure(cancelResult.value)) {
|
|
997
|
+
return genericResult(cancelResult.value);
|
|
998
|
+
}
|
|
806
999
|
return result;
|
|
807
1000
|
}
|
|
808
1001
|
}
|
|
@@ -1013,27 +1206,64 @@ export class AIController {
|
|
|
1013
1206
|
errorMessage: "The subscription doesn't support the given model.",
|
|
1014
1207
|
};
|
|
1015
1208
|
}
|
|
1209
|
+
const creditFeePerRealtimeSession = features.realtime.creditFeePerRealtimeSession ?? null;
|
|
1210
|
+
const amount = creditFeePerRealtimeSession
|
|
1211
|
+
? BigInt(creditFeePerRealtimeSession)
|
|
1212
|
+
: null;
|
|
1213
|
+
const billing = await billForUsage(this._financial, {
|
|
1214
|
+
userId: context.context.recordOwnerId ?? undefined,
|
|
1215
|
+
studioId: context.context.recordStudioId ?? undefined,
|
|
1216
|
+
transferCode: TransferCodes.records_usage_fee,
|
|
1217
|
+
billingCode: BillingCodes.ai_openai_realtime_session,
|
|
1218
|
+
});
|
|
1219
|
+
const initialResult = await billing.next(success({
|
|
1220
|
+
initialCost: amount,
|
|
1221
|
+
}));
|
|
1222
|
+
if (isFailure(initialResult.value)) {
|
|
1223
|
+
return genericResult(initialResult.value);
|
|
1224
|
+
}
|
|
1016
1225
|
const tokenRequest = {
|
|
1017
1226
|
...request.request,
|
|
1018
1227
|
maxResponseOutputTokens: features.realtime.maxResponseOutputTokens ??
|
|
1019
1228
|
request.request.maxResponseOutputTokens ??
|
|
1020
1229
|
undefined,
|
|
1021
1230
|
};
|
|
1022
|
-
const result = await this._openAIRealtimeInterface.createRealtimeSessionToken(tokenRequest);
|
|
1023
|
-
if (result
|
|
1024
|
-
|
|
1231
|
+
const result = await wrap(async () => await this._openAIRealtimeInterface.createRealtimeSessionToken(tokenRequest));
|
|
1232
|
+
if (isFailure(result)) {
|
|
1233
|
+
console.error('[AIController] Create OpenAI Realtime session token request failed:', result);
|
|
1234
|
+
// Need to pass failure to billing to ensure that it cancels pending transfers
|
|
1235
|
+
await billing.next(failure({
|
|
1236
|
+
errorCode: 'server_error',
|
|
1237
|
+
errorMessage: 'A server error occurred.',
|
|
1238
|
+
}));
|
|
1239
|
+
return {
|
|
1240
|
+
success: false,
|
|
1241
|
+
errorCode: 'server_error',
|
|
1242
|
+
errorMessage: 'A server error occurred.',
|
|
1243
|
+
};
|
|
1244
|
+
}
|
|
1245
|
+
if (result.value.success === false) {
|
|
1246
|
+
// Pass the token creation error to billing
|
|
1247
|
+
await billing.next(failure(result.value));
|
|
1248
|
+
return result.value;
|
|
1025
1249
|
}
|
|
1026
1250
|
await this._metrics.recordOpenAIRealtimeMetrics({
|
|
1027
1251
|
userId: context.context.recordOwnerId ?? undefined,
|
|
1028
1252
|
studioId: context.context.recordStudioId ?? undefined,
|
|
1029
|
-
sessionId: result.sessionId,
|
|
1253
|
+
sessionId: result.value.sessionId,
|
|
1030
1254
|
createdAtMs: Date.now(),
|
|
1031
1255
|
request: tokenRequest,
|
|
1032
1256
|
});
|
|
1257
|
+
const finalResult = await billing.next(success({
|
|
1258
|
+
cost: amount,
|
|
1259
|
+
}));
|
|
1260
|
+
if (isFailure(finalResult.value)) {
|
|
1261
|
+
return genericResult(finalResult.value);
|
|
1262
|
+
}
|
|
1033
1263
|
return {
|
|
1034
1264
|
success: true,
|
|
1035
|
-
sessionId: result.sessionId,
|
|
1036
|
-
clientSecret: result.clientSecret,
|
|
1265
|
+
sessionId: result.value.sessionId,
|
|
1266
|
+
clientSecret: result.value.clientSecret,
|
|
1037
1267
|
};
|
|
1038
1268
|
}
|
|
1039
1269
|
catch (err) {
|