@remit/backend 2.4.0 → 2.6.0
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/dist/src/handlers/classification-settings.d.ts +4 -7
- package/dist/src/handlers/classification-settings.js +40 -46
- package/dist/src/handlers/classification-test.js +2 -1
- package/dist/src/handlers/message.js +2 -0
- package/dist/src/handlers/user-settings.js +9 -3
- package/dist/src/service/create-remit-client.js +6 -0
- package/package.json +1 -1
|
@@ -1,17 +1,14 @@
|
|
|
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 SavedClassificationProvider, type StoredClassification } from "@remit/data-ports/classification-presets";
|
|
3
4
|
import { type ClassificationLabelDeps } from "@remit/mailbox-service";
|
|
4
5
|
import { type SecretsService, type SerializedEncryptedPayload } from "@remit/secrets-service";
|
|
5
6
|
import type { UserSettingsOutcome } from "./user-settings.js";
|
|
6
|
-
export declare const CLASSIFICATION_SETTING_NAMES: readonly ["ClassificationPresetId", "ClassificationModel", "ClassificationBaseUrl", "ClassificationApiKey", "ClassificationEnabled", "ClassificationDailyLimit"];
|
|
7
7
|
export type ClassificationSettingsDeps = {
|
|
8
8
|
accountSetting: Pick<IAccountSettingRepository, "getMany" | "upsert" | "delete">;
|
|
9
9
|
secrets: Pick<SecretsService, "encrypt">;
|
|
10
10
|
label: ClassificationLabelDeps;
|
|
11
11
|
};
|
|
12
|
-
export type StoredClassification = ClassificationNotConfigured | (ClassificationConfigured & {
|
|
13
|
-
apiKeyCiphertext: SerializedEncryptedPayload | undefined;
|
|
14
|
-
});
|
|
15
12
|
type Refusal = Extract<UserSettingsOutcome, {
|
|
16
13
|
ok: false;
|
|
17
14
|
}>;
|
|
@@ -36,8 +33,8 @@ export interface ClassificationProviderSave {
|
|
|
36
33
|
}
|
|
37
34
|
type ClassificationInput = Pick<UpdateUserSettingsInput, "classificationPresetId" | "classificationModel" | "classificationBaseUrl" | "classificationApiKey" | "classificationEnabled" | "classificationDailyLimit">;
|
|
38
35
|
export declare const decideClassificationProvider: (input: ClassificationInput, stored: StoredClassification) => Decision<ClassificationProviderSave>;
|
|
39
|
-
export declare const
|
|
40
|
-
export declare const
|
|
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[]>;
|
|
41
38
|
export declare const classificationPresetsResponse: () => ClassificationPreset[];
|
|
42
39
|
interface ClassificationChange {
|
|
43
40
|
provider: ClassificationProviderSave | undefined;
|
|
@@ -1,16 +1,8 @@
|
|
|
1
1
|
import { parseAccountSettingValue } from "@remit/data-ports/account-settings";
|
|
2
|
-
import { CLASSIFICATION_PRESETS,
|
|
2
|
+
import { CLASSIFICATION_PRESETS, CLASSIFICATION_SETTING_NAMES, classificationUsageName, utcDateOf, } from "@remit/data-ports/classification-presets";
|
|
3
3
|
import { AccountSettingName } from "@remit/domain-enums";
|
|
4
4
|
import { ensureClassificationLabels, } from "@remit/mailbox-service";
|
|
5
5
|
import { serializeEncryptedPayload, } from "@remit/secrets-service";
|
|
6
|
-
export const CLASSIFICATION_SETTING_NAMES = [
|
|
7
|
-
AccountSettingName.ClassificationPresetId,
|
|
8
|
-
AccountSettingName.ClassificationModel,
|
|
9
|
-
AccountSettingName.ClassificationBaseUrl,
|
|
10
|
-
AccountSettingName.ClassificationApiKey,
|
|
11
|
-
AccountSettingName.ClassificationEnabled,
|
|
12
|
-
AccountSettingName.ClassificationDailyLimit,
|
|
13
|
-
];
|
|
14
6
|
const refuse = (code, message) => ({
|
|
15
7
|
ok: false,
|
|
16
8
|
error: { code, message },
|
|
@@ -73,46 +65,48 @@ export const decideClassificationProvider = (input, stored) => {
|
|
|
73
65
|
},
|
|
74
66
|
};
|
|
75
67
|
};
|
|
76
|
-
const
|
|
77
|
-
export const readStoredClassification = async (accountSetting, accountConfigId) => {
|
|
78
|
-
const rows = await accountSetting.getMany([accountConfigId], CLASSIFICATION_SETTING_NAMES);
|
|
79
|
-
const presetRow = settingOf(rows, AccountSettingName.ClassificationPresetId);
|
|
80
|
-
if (!presetRow)
|
|
81
|
-
return { state: "NotConfigured" };
|
|
82
|
-
const presetId = parseAccountSettingValue(AccountSettingName.ClassificationPresetId, presetRow.value).value;
|
|
83
|
-
if (!isClassificationPresetId(presetId)) {
|
|
84
|
-
throw new Error(`Stored classification preset is unknown: ${presetId}`);
|
|
85
|
-
}
|
|
86
|
-
const stringOf = (name) => {
|
|
87
|
-
const row = settingOf(rows, name);
|
|
88
|
-
return row ? parseAccountSettingValue(name, row.value).value : "";
|
|
89
|
-
};
|
|
90
|
-
const keyRow = settingOf(rows, AccountSettingName.ClassificationApiKey);
|
|
91
|
-
const enabledRow = settingOf(rows, AccountSettingName.ClassificationEnabled);
|
|
92
|
-
const limitRow = settingOf(rows, AccountSettingName.ClassificationDailyLimit);
|
|
93
|
-
return {
|
|
94
|
-
state: "Configured",
|
|
95
|
-
presetId,
|
|
96
|
-
model: stringOf(AccountSettingName.ClassificationModel),
|
|
97
|
-
baseUrl: stringOf(AccountSettingName.ClassificationBaseUrl),
|
|
98
|
-
apiKeySaved: keyRow !== undefined,
|
|
99
|
-
apiKeyCiphertext: keyRow
|
|
100
|
-
? parseAccountSettingValue(AccountSettingName.ClassificationApiKey, keyRow.value).value
|
|
101
|
-
: undefined,
|
|
102
|
-
enabled: enabledRow
|
|
103
|
-
? parseAccountSettingValue(AccountSettingName.ClassificationEnabled, enabledRow.value).value
|
|
104
|
-
: false,
|
|
105
|
-
dailyLimit: limitRow
|
|
106
|
-
? parseAccountSettingValue(AccountSettingName.ClassificationDailyLimit, limitRow.value).value
|
|
107
|
-
: DEFAULT_CLASSIFICATION_DAILY_LIMIT,
|
|
108
|
-
savedAt: presetRow.updatedAt,
|
|
109
|
-
};
|
|
110
|
-
};
|
|
111
|
-
export const publicClassification = (stored) => {
|
|
68
|
+
export const publicClassification = (stored, usage) => {
|
|
112
69
|
if (stored.state === "NotConfigured")
|
|
113
70
|
return stored;
|
|
114
71
|
const { apiKeyCiphertext: _ciphertext, ...configured } = stored;
|
|
115
|
-
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
|
+
});
|
|
116
110
|
};
|
|
117
111
|
export const classificationPresetsResponse = () => Object.values(CLASSIFICATION_PRESETS);
|
|
118
112
|
export const decideClassificationChange = (input, stored) => {
|
|
@@ -1,7 +1,8 @@
|
|
|
1
|
+
import { readStoredClassification } from "@remit/data-ports/classification-presets";
|
|
1
2
|
import { ClassificationFailureKind as FailureKind, ClassificationField as Field, } from "@remit/domain-enums";
|
|
2
3
|
import { CLASSIFIER_LABELS, CLASSIFIER_SAMPLE_MESSAGE, classifyWithProvider, } from "@remit/mailbox-service/classifier";
|
|
3
4
|
import { deserializeEncryptedPayload, } from "@remit/secrets-service";
|
|
4
|
-
import { decideClassificationProvider,
|
|
5
|
+
import { decideClassificationProvider, } from "./classification-settings.js";
|
|
5
6
|
const REACH_FIX = "Check the endpoint runs and the Remit server can reach it";
|
|
6
7
|
const DEFAULT_MODEL_FIX = "Use the preset's default model";
|
|
7
8
|
const FAILURE_HELP = {
|
|
@@ -279,6 +279,7 @@ export const MessageOperations = {
|
|
|
279
279
|
const folder = {
|
|
280
280
|
mailboxId: mailbox.mailboxId,
|
|
281
281
|
fullPath: mailbox.fullPath,
|
|
282
|
+
initialSyncCompletedAt: mailbox.initialSyncCompletedAt,
|
|
282
283
|
placement: await resolveFolderPlacement(client.mailboxSpecialUse, accountId, mailbox.mailboxId),
|
|
283
284
|
};
|
|
284
285
|
const scope = await client.createConnectionScope(accountId);
|
|
@@ -385,6 +386,7 @@ export const MessageOperations = {
|
|
|
385
386
|
const folder = {
|
|
386
387
|
mailboxId: mailbox.mailboxId,
|
|
387
388
|
fullPath: mailbox.fullPath,
|
|
389
|
+
initialSyncCompletedAt: mailbox.initialSyncCompletedAt,
|
|
388
390
|
placement: await resolveFolderPlacement(client.mailboxSpecialUse, accountId, mailbox.mailboxId),
|
|
389
391
|
};
|
|
390
392
|
const scope = await client.createConnectionScope(accountId);
|
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
import { invitationSenderInEffect, sendingAccountFor, } from "@remit/calendar-service";
|
|
2
2
|
import { parseAccountSettingValue } from "@remit/data-ports/account-settings";
|
|
3
|
+
import { readStoredClassification } from "@remit/data-ports/classification-presets";
|
|
3
4
|
import { AccountSettingName } from "@remit/domain-enums";
|
|
4
5
|
import { logger } from "@remit/logger";
|
|
5
6
|
import { badRequest } from "./calendar.js";
|
|
6
|
-
import { classificationPresetsResponse, clearClassification, decideClassificationChange, publicClassification,
|
|
7
|
+
import { classificationPresetsResponse, clearClassification, decideClassificationChange, publicClassification, readClassificationUsage, writeClassificationChange, } from "./classification-settings.js";
|
|
7
8
|
import { testClassificationProvider } from "./classification-test.js";
|
|
8
9
|
/** The account the reader chose to send invitations from, if they chose one. */
|
|
9
10
|
export const chosenInvitationSender = async (accountSetting, accountConfigId) => {
|
|
@@ -14,12 +15,17 @@ export const chosenInvitationSender = async (accountSetting, accountConfigId) =>
|
|
|
14
15
|
};
|
|
15
16
|
export const readUserSettings = async (deps, accountConfigId) => {
|
|
16
17
|
const chosen = await chosenInvitationSender(deps.accountSetting, accountConfigId);
|
|
17
|
-
const
|
|
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
|
+
: [];
|
|
18
24
|
return {
|
|
19
25
|
...(inEffect && { defaultInvitationSenderAccountId: inEffect.accountId }),
|
|
20
26
|
defaultInvitationSenderChosen: inEffect !== undefined && inEffect.accountId === chosen,
|
|
21
27
|
classificationPresets: classificationPresetsResponse(),
|
|
22
|
-
classification: publicClassification(
|
|
28
|
+
classification: publicClassification(stored, usage),
|
|
23
29
|
};
|
|
24
30
|
};
|
|
25
31
|
export const updateUserSettingsFor = async (deps, accountConfigId, input) => {
|
|
@@ -158,6 +158,12 @@ export const composeReadPathServices = (core, queues, sync) => ({
|
|
|
158
158
|
calendarStore: core.calendarStore,
|
|
159
159
|
filterService: core.filter,
|
|
160
160
|
accountService: core.account,
|
|
161
|
+
}, {
|
|
162
|
+
accountSetting: core.accountSetting,
|
|
163
|
+
secrets: core.secrets,
|
|
164
|
+
labelService: core.label,
|
|
165
|
+
messageLabelService: core.messageLabel,
|
|
166
|
+
logger,
|
|
161
167
|
}),
|
|
162
168
|
});
|
|
163
169
|
export const createRemitClient = (deps) => {
|