@remit/backend 2.5.0 → 2.6.1

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.
@@ -1,6 +1,6 @@
1
- import type { ClassificationConfigured, ClassificationNotConfigured, ClassificationPreset, UpdateUserSettingsInput } from "@remit/api-openapi-types";
1
+ import type { ClassificationConfigured, ClassificationNotConfigured, ClassificationPreset, ClassificationUsage, UpdateUserSettingsInput } from "@remit/api-openapi-types";
2
2
  import type { IAccountSettingRepository } from "@remit/data-ports";
3
- import { type StoredClassification } from "@remit/data-ports/classification-presets";
3
+ import { type SavedClassificationProvider, type StoredClassification } from "@remit/data-ports/classification-presets";
4
4
  import { type ClassificationLabelDeps } from "@remit/mailbox-service";
5
5
  import { type SecretsService, type SerializedEncryptedPayload } from "@remit/secrets-service";
6
6
  import type { UserSettingsOutcome } from "./user-settings.js";
@@ -33,7 +33,8 @@ export interface ClassificationProviderSave {
33
33
  }
34
34
  type ClassificationInput = Pick<UpdateUserSettingsInput, "classificationPresetId" | "classificationModel" | "classificationBaseUrl" | "classificationApiKey" | "classificationEnabled" | "classificationDailyLimit">;
35
35
  export declare const decideClassificationProvider: (input: ClassificationInput, stored: StoredClassification) => Decision<ClassificationProviderSave>;
36
- export declare const publicClassification: (stored: StoredClassification) => ClassificationNotConfigured | ClassificationConfigured;
36
+ export declare const publicClassification: (stored: StoredClassification, usage: ClassificationUsage[]) => ClassificationNotConfigured | ClassificationConfigured;
37
+ export declare const readClassificationUsage: (accountSetting: Pick<IAccountSettingRepository, "getMany">, accountConfigId: string, accountIds: readonly string[], saved: Pick<SavedClassificationProvider, "presetId" | "dailyLimit">, now: number) => Promise<ClassificationUsage[]>;
37
38
  export declare const classificationPresetsResponse: () => ClassificationPreset[];
38
39
  interface ClassificationChange {
39
40
  provider: ClassificationProviderSave | undefined;
@@ -1,4 +1,5 @@
1
- import { CLASSIFICATION_PRESETS, CLASSIFICATION_SETTING_NAMES, } from "@remit/data-ports/classification-presets";
1
+ import { parseAccountSettingValue } from "@remit/data-ports/account-settings";
2
+ import { CLASSIFICATION_PRESETS, CLASSIFICATION_SETTING_NAMES, classificationUsageName, utcDateOf, } from "@remit/data-ports/classification-presets";
2
3
  import { AccountSettingName } from "@remit/domain-enums";
3
4
  import { ensureClassificationLabels, } from "@remit/mailbox-service";
4
5
  import { serializeEncryptedPayload, } from "@remit/secrets-service";
@@ -64,11 +65,48 @@ export const decideClassificationProvider = (input, stored) => {
64
65
  },
65
66
  };
66
67
  };
67
- export const publicClassification = (stored) => {
68
+ export const publicClassification = (stored, usage) => {
68
69
  if (stored.state === "NotConfigured")
69
70
  return stored;
70
71
  const { apiKeyCiphertext: _ciphertext, ...configured } = stored;
71
- return configured;
72
+ return { ...configured, usage };
73
+ };
74
+ const CLASSIFICATION_INPUT_TOKENS_PER_CALL = 1_500;
75
+ const nextUtcMidnight = (epochMs) => {
76
+ const now = new Date(epochMs);
77
+ return new Date(Date.UTC(now.getUTCFullYear(), now.getUTCMonth(), now.getUTCDate() + 1)).toISOString();
78
+ };
79
+ const callsOn = (usage, date) => usage?.date === date ? usage.calls : 0;
80
+ const classificationCost = (preset, calls) => {
81
+ if (preset.price.state === "NoPrice")
82
+ return { state: "NoPrice" };
83
+ return {
84
+ state: "Priced",
85
+ currency: preset.price.currency,
86
+ amount: (calls *
87
+ CLASSIFICATION_INPUT_TOKENS_PER_CALL *
88
+ preset.price.perMillionInputTokens) /
89
+ 1_000_000,
90
+ };
91
+ };
92
+ export const readClassificationUsage = async (accountSetting, accountConfigId, accountIds, saved, now) => {
93
+ const rows = await accountSetting.getMany([accountConfigId], accountIds.map(classificationUsageName));
94
+ const today = utcDateOf(now);
95
+ const resetsAt = nextUtcMidnight(now);
96
+ const preset = CLASSIFICATION_PRESETS[saved.presetId];
97
+ return accountIds.map((accountId) => {
98
+ const row = rows.find((candidate) => candidate.name === classificationUsageName(accountId));
99
+ const calls = callsOn(row
100
+ ? parseAccountSettingValue(AccountSettingName.ClassificationUsage, row.value).value
101
+ : undefined, today);
102
+ return {
103
+ accountId,
104
+ calls,
105
+ dailyLimit: saved.dailyLimit,
106
+ resetsAt,
107
+ cost: classificationCost(preset, calls),
108
+ };
109
+ });
72
110
  };
73
111
  export const classificationPresetsResponse = () => Object.values(CLASSIFICATION_PRESETS);
74
112
  export const decideClassificationChange = (input, stored) => {
@@ -4,7 +4,7 @@ import { readStoredClassification } from "@remit/data-ports/classification-prese
4
4
  import { AccountSettingName } from "@remit/domain-enums";
5
5
  import { logger } from "@remit/logger";
6
6
  import { badRequest } from "./calendar.js";
7
- import { classificationPresetsResponse, clearClassification, decideClassificationChange, publicClassification, writeClassificationChange, } from "./classification-settings.js";
7
+ import { classificationPresetsResponse, clearClassification, decideClassificationChange, publicClassification, readClassificationUsage, writeClassificationChange, } from "./classification-settings.js";
8
8
  import { testClassificationProvider } from "./classification-test.js";
9
9
  /** The account the reader chose to send invitations from, if they chose one. */
10
10
  export const chosenInvitationSender = async (accountSetting, accountConfigId) => {
@@ -15,12 +15,17 @@ export const chosenInvitationSender = async (accountSetting, accountConfigId) =>
15
15
  };
16
16
  export const readUserSettings = async (deps, accountConfigId) => {
17
17
  const chosen = await chosenInvitationSender(deps.accountSetting, accountConfigId);
18
- const inEffect = invitationSenderInEffect(await deps.accounts.listAllByAccountConfig(accountConfigId), chosen);
18
+ const accounts = await deps.accounts.listAllByAccountConfig(accountConfigId);
19
+ const inEffect = invitationSenderInEffect(accounts, chosen);
20
+ const stored = await readStoredClassification(deps.accountSetting, accountConfigId);
21
+ const usage = stored.state === "Configured"
22
+ ? await readClassificationUsage(deps.accountSetting, accountConfigId, accounts.map((account) => account.accountId), stored, Date.now())
23
+ : [];
19
24
  return {
20
25
  ...(inEffect && { defaultInvitationSenderAccountId: inEffect.accountId }),
21
26
  defaultInvitationSenderChosen: inEffect !== undefined && inEffect.accountId === chosen,
22
27
  classificationPresets: classificationPresetsResponse(),
23
- classification: publicClassification(await readStoredClassification(deps.accountSetting, accountConfigId)),
28
+ classification: publicClassification(stored, usage),
24
29
  };
25
30
  };
26
31
  export const updateUserSettingsFor = async (deps, accountConfigId, input) => {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@remit/backend",
3
- "version": "2.5.0",
3
+ "version": "2.6.1",
4
4
  "description": "Remit Mail Inspector API backend",
5
5
  "license": "MIT",
6
6
  "author": "",