🪢 chore: Consolidate Pricing and Tx Imports After tx.js Module Removal (#12086)

* 🧹 chore: resolve imports due to rebase

* chore: Update model mocks in unit tests for consistency

- Consolidated model mock implementations across various test files to streamline setup and reduce redundancy.
- Removed duplicate mock definitions for `getMultiplier` and `getCacheMultiplier`, ensuring a unified approach in `recordCollectedUsage.spec.js`, `openai.spec.js`, `responses.unit.spec.js`, and `abortMiddleware.spec.js`.
- Enhanced clarity and maintainability of test files by aligning mock structures with the latest model updates.

* fix: Safeguard token credit checks in transaction tests

- Updated assertions in `transaction.spec.ts` to handle potential null values for `updatedBalance` by using optional chaining.
- Enhanced robustness of tests related to token credit calculations, ensuring they correctly account for scenarios where the balance may not be found.

* chore: transaction methods with bulk insert functionality

- Introduced `bulkInsertTransactions` method in `transaction.ts` to facilitate batch insertion of transaction documents.
- Updated test file `transactions.bulk-parity.spec.ts` to utilize new pricing function assignments and handle potential null values in calculations, improving test robustness.
- Refactored pricing function initialization for clarity and consistency.

* refactor: Enhance type definitions and introduce new utility functions for model matching

- Added `findMatchingPattern` and `matchModelName` utility functions to improve model name matching logic in transaction methods.
- Updated type definitions for `findMatchingPattern` to accept a more specific tokensMap structure, enhancing type safety.
- Refactored `dbMethods` initialization in `transactions.bulk-parity.spec.ts` to include the new utility functions, improving test clarity and functionality.

* refactor: Update database method imports and enhance transaction handling

- Refactored `abortMiddleware.js` to utilize centralized database methods for message handling and conversation retrieval, improving code consistency.
- Enhanced `bulkInsertTransactions` in `transaction.ts` to handle empty document arrays gracefully and added error logging for better debugging.
- Updated type definitions in `transactions.ts` to enforce stricter typing for token types, enhancing type safety across transaction methods.
- Improved test setup in `transactions.bulk-parity.spec.ts` by refining pricing function assignments and ensuring robust handling of potential null values.

* refactor: Update database method references and improve transaction multiplier handling

- Refactored `client.js` to update database method references for `bulkInsertTransactions` and `updateBalance`, ensuring consistency in method usage.
- Enhanced transaction multiplier calculations in `transaction.spec.ts` to provide fallback values for write and read multipliers, improving robustness in cost calculations across structured token spending tests.
This commit is contained in:
Danny Avila 2026-03-05 16:01:52 -05:00 committed by GitHub
parent 8fafda47c2
commit d24fe17a4b
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
17 changed files with 123 additions and 427 deletions

View file

@ -14,10 +14,12 @@
import mongoose from 'mongoose';
import { MongoMemoryServer } from 'mongodb-memory-server';
import {
tokenValues,
CANCEL_RATE,
createMethods,
balanceSchema,
transactionSchema,
premiumTokenValues,
} from '@librechat/data-schemas';
import type { PricingFns, TxMetadata } from './transactions';
import {
@ -26,6 +28,26 @@ import {
prepareTokenSpend,
} from './transactions';
/** Inlined from packages/data-schemas/src/methods/test-helpers.ts — keep in sync */
function findMatchingPattern(
modelName: string,
tokensMap: Record<string, number | Record<string, number>>,
): string | undefined {
const keys = Object.keys(tokensMap);
const lowerModelName = modelName.toLowerCase();
for (let i = keys.length - 1; i >= 0; i--) {
if (lowerModelName.includes(keys[i])) {
return keys[i];
}
}
return undefined;
}
/** Inlined from packages/data-schemas/src/methods/test-helpers.ts — keep in sync */
function matchModelName(modelName: string, _endpoint?: string): string | undefined {
return typeof modelName === 'string' ? modelName : undefined;
}
jest.mock('@librechat/data-schemas', () => {
const actual = jest.requireActual('@librechat/data-schemas');
return {
@ -34,29 +56,23 @@ jest.mock('@librechat/data-schemas', () => {
};
});
// Real pricing functions from api/models/tx.js — same ones the legacy path uses
/* eslint-disable @typescript-eslint/no-require-imports */
const {
getMultiplier,
getCacheMultiplier,
tokenValues,
premiumTokenValues,
} = require('../../../../api/models/tx.js');
/* eslint-enable @typescript-eslint/no-require-imports */
const pricing: PricingFns = { getMultiplier, getCacheMultiplier };
let mongoServer: MongoMemoryServer;
let Transaction: mongoose.Model<unknown>;
let Balance: mongoose.Model<unknown>;
let dbMethods: ReturnType<typeof createMethods>;
let pricing: PricingFns;
let getMultiplier: ReturnType<typeof createMethods>['getMultiplier'];
let getCacheMultiplier: ReturnType<typeof createMethods>['getCacheMultiplier'];
beforeAll(async () => {
mongoServer = await MongoMemoryServer.create();
await mongoose.connect(mongoServer.getUri());
Transaction = mongoose.models.Transaction || mongoose.model('Transaction', transactionSchema);
Balance = mongoose.models.Balance || mongoose.model('Balance', balanceSchema);
dbMethods = createMethods(mongoose);
dbMethods = createMethods(mongoose, { matchModelName, findMatchingPattern });
getMultiplier = dbMethods.getMultiplier;
getCacheMultiplier = dbMethods.getCacheMultiplier;
pricing = { getMultiplier, getCacheMultiplier };
});
afterAll(async () => {
@ -536,8 +552,13 @@ describe('Multi-entry batch parity', () => {
const premiumCompletionRate = (premiumTokenValues as Record<string, Record<string, number>>)[
model
].completion;
const writeMultiplier = getCacheMultiplier({ model, cacheType: 'write' });
const readMultiplier = getCacheMultiplier({ model, cacheType: 'read' });
const promptMultiplier = getMultiplier({
model,
tokenType: 'prompt',
inputTokenCount: totalInput,
});
const writeMultiplier = getCacheMultiplier({ model, cacheType: 'write' }) ?? promptMultiplier;
const readMultiplier = getCacheMultiplier({ model, cacheType: 'read' }) ?? promptMultiplier;
const expectedPromptCost =
tokenUsage.promptTokens.input * premiumPromptRate +

View file

@ -3,9 +3,11 @@ import type { TCustomConfig, TTransactionsConfig } from 'librechat-data-provider
import type { TransactionData } from '@librechat/data-schemas';
import type { EndpointTokenConfig } from '~/types/tokens';
type TokenType = 'prompt' | 'completion';
interface GetMultiplierParams {
valueKey?: string;
tokenType?: string;
tokenType?: TokenType;
model?: string;
endpointTokenConfig?: EndpointTokenConfig;
inputTokenCount?: number;
@ -34,14 +36,14 @@ interface BaseTxData {
}
interface StandardTxData extends BaseTxData {
tokenType: string;
tokenType: TokenType;
rawAmount: number;
inputTokenCount?: number;
valueKey?: string;
}
interface StructuredTxData extends BaseTxData {
tokenType: string;
tokenType: TokenType;
inputTokens?: number;
writeTokens?: number;
readTokens?: number;

View file

@ -1,16 +1,8 @@
/** Configuration object mapping model keys to their respective prompt, completion rates, and context limit
*
* Note: the [key: string]: unknown is not in the original JSDoc typedef in /api/typedefs.js, but I've included it since
* getModelMaxOutputTokens calls getModelTokenValue with a key of 'output', which was not in the original JSDoc typedef,
* but would be referenced in a TokenConfig in the if(matchedPattern) portion of getModelTokenValue.
* So in order to preserve functionality for that case and any others which might reference an additional key I'm unaware of,
* I've included it here until the interface can be typed more tightly.
*/
export interface TokenConfig {
[key: string]: number;
prompt: number;
completion: number;
context: number;
[key: string]: unknown;
}
/** An endpoint's config object mapping model keys to their respective prompt, completion rates, and context limit */