@remit/backend 2.7.1 → 2.8.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 +15 -6
- package/dist/src/handlers/classification-settings.js +42 -15
- package/dist/src/handlers/classification-test.d.ts +2 -3
- package/dist/src/handlers/classification-test.js +2 -3
- package/dist/src/handlers/user-settings.d.ts +4 -1
- package/dist/src/handlers/user-settings.js +16 -7
- package/dist/src/service/create-remit-client.js +2 -0
- package/package.json +1 -1
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import type { ClassificationConfigured, ClassificationNotConfigured, ClassificationPreset, ClassificationUsage, UpdateUserSettingsInput } from "@remit/api-openapi-types";
|
|
1
|
+
import type { ClassificationConfigured, ClassificationNotConfigured, ClassificationPreset, ClassificationPresetId, ClassificationUsage, UpdateUserSettingsInput } from "@remit/api-openapi-types";
|
|
2
2
|
import type { IAccountSettingRepository } from "@remit/data-ports";
|
|
3
|
-
import { type
|
|
3
|
+
import { type ClassificationResolverDeps, type ResolvedClassification, type ResolvedClassificationProvider } from "@remit/data-ports/classification-resolver";
|
|
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";
|
|
@@ -32,16 +32,25 @@ export interface ClassificationProviderSave {
|
|
|
32
32
|
apiKey: ApiKeyChange;
|
|
33
33
|
}
|
|
34
34
|
type ClassificationInput = Pick<UpdateUserSettingsInput, "classificationPresetId" | "classificationModel" | "classificationBaseUrl" | "classificationApiKey" | "classificationEnabled" | "classificationDailyLimit">;
|
|
35
|
-
export
|
|
36
|
-
|
|
37
|
-
|
|
35
|
+
export type KnownClassification = Exclude<ResolvedClassification, {
|
|
36
|
+
state: "UnknownPreset";
|
|
37
|
+
}>;
|
|
38
|
+
export declare const readKnownClassification: (deps: ClassificationResolverDeps, accountConfigId: string) => Promise<KnownClassification>;
|
|
39
|
+
export declare const decideClassificationProvider: (input: ClassificationInput, known: KnownClassification) => Decision<ClassificationProviderSave>;
|
|
40
|
+
export declare const publicClassification: (resolved: ResolvedClassificationProvider | {
|
|
41
|
+
state: "NotConfigured";
|
|
42
|
+
}, usage: ClassificationUsage[]) => ClassificationNotConfigured | ClassificationConfigured;
|
|
43
|
+
export declare const readClassificationUsage: (accountSetting: Pick<IAccountSettingRepository, "getMany">, accountConfigId: string, accountIds: readonly string[], saved: {
|
|
44
|
+
presetId: ClassificationPresetId;
|
|
45
|
+
dailyLimit: number;
|
|
46
|
+
}, now: number) => Promise<ClassificationUsage[]>;
|
|
38
47
|
export declare const classificationPresetsResponse: () => ClassificationPreset[];
|
|
39
48
|
interface ClassificationChange {
|
|
40
49
|
provider: ClassificationProviderSave | undefined;
|
|
41
50
|
enabled: boolean | undefined;
|
|
42
51
|
dailyLimit: number | undefined;
|
|
43
52
|
}
|
|
44
|
-
export declare const decideClassificationChange: (input: ClassificationInput,
|
|
53
|
+
export declare const decideClassificationChange: (input: ClassificationInput, known: KnownClassification) => Decision<ClassificationChange>;
|
|
45
54
|
export declare const writeClassificationChange: (deps: ClassificationSettingsDeps, accountConfigId: string, change: ClassificationChange) => Promise<void>;
|
|
46
55
|
export declare const clearClassification: (accountSetting: Pick<IAccountSettingRepository, "delete">, accountConfigId: string) => Promise<void>;
|
|
47
56
|
export {};
|
|
@@ -1,8 +1,16 @@
|
|
|
1
1
|
import { parseAccountSettingValue } from "@remit/data-ports/account-settings";
|
|
2
2
|
import { CLASSIFICATION_PRESETS, CLASSIFICATION_SETTING_NAMES, classificationUsageName, utcDateOf, } from "@remit/data-ports/classification-presets";
|
|
3
|
+
import { ClassificationSource, readResolvedClassification, } from "@remit/data-ports/classification-resolver";
|
|
3
4
|
import { AccountSettingName } from "@remit/domain-enums";
|
|
4
5
|
import { ensureClassificationLabels, } from "@remit/mailbox-service";
|
|
5
6
|
import { serializeEncryptedPayload, } from "@remit/secrets-service";
|
|
7
|
+
export const readKnownClassification = async (deps, accountConfigId) => {
|
|
8
|
+
const resolved = await readResolvedClassification(deps, accountConfigId);
|
|
9
|
+
if (resolved.state === "UnknownPreset") {
|
|
10
|
+
throw new Error(`Stored classification preset is unknown: ${resolved.presetId}`);
|
|
11
|
+
}
|
|
12
|
+
return resolved;
|
|
13
|
+
};
|
|
6
14
|
const refuse = (code, message) => ({
|
|
7
15
|
ok: false,
|
|
8
16
|
error: { code, message },
|
|
@@ -40,21 +48,31 @@ const apiKeyFor = (preset, requested, keptKey) => {
|
|
|
40
48
|
}
|
|
41
49
|
return refuse("ClassificationApiKeyRequired", `classificationApiKey is required for ${preset.label}`);
|
|
42
50
|
};
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
51
|
+
const presetIdOf = (known) => {
|
|
52
|
+
if (known.state === "NotConfigured")
|
|
53
|
+
return undefined;
|
|
54
|
+
if (known.state === "Unrunnable")
|
|
55
|
+
return known.presetId;
|
|
56
|
+
return known.presetId.value;
|
|
57
|
+
};
|
|
58
|
+
export const decideClassificationProvider = (input, known) => {
|
|
59
|
+
const presetId = input.classificationPresetId ?? presetIdOf(known);
|
|
46
60
|
if (presetId === undefined) {
|
|
47
61
|
return refuse("ClassificationNotConfigured", "classificationPresetId is required: no classification provider is saved");
|
|
48
62
|
}
|
|
49
63
|
const preset = CLASSIFICATION_PRESETS[presetId];
|
|
50
|
-
const kept =
|
|
51
|
-
|
|
64
|
+
const kept = known.state === "Configured" && known.presetId.value === presetId
|
|
65
|
+
? known
|
|
66
|
+
: undefined;
|
|
67
|
+
const baseUrl = baseUrlFor(preset, (input.classificationBaseUrl ?? kept?.baseUrl.value ?? "").trim());
|
|
52
68
|
if (!baseUrl.ok)
|
|
53
69
|
return baseUrl;
|
|
54
|
-
const apiKey = apiKeyFor(preset, (input.classificationApiKey ?? "").trim(), kept?.
|
|
70
|
+
const apiKey = apiKeyFor(preset, (input.classificationApiKey ?? "").trim(), kept?.apiKey.source === ClassificationSource.Saved
|
|
71
|
+
? kept.apiKey.ciphertext
|
|
72
|
+
: undefined);
|
|
55
73
|
if (!apiKey.ok)
|
|
56
74
|
return apiKey;
|
|
57
|
-
const model = (input.classificationModel ?? kept?.model ?? "").trim();
|
|
75
|
+
const model = (input.classificationModel ?? kept?.model.value ?? "").trim();
|
|
58
76
|
return {
|
|
59
77
|
ok: true,
|
|
60
78
|
value: {
|
|
@@ -65,11 +83,20 @@ export const decideClassificationProvider = (input, stored) => {
|
|
|
65
83
|
},
|
|
66
84
|
};
|
|
67
85
|
};
|
|
68
|
-
export const publicClassification = (
|
|
69
|
-
if (
|
|
70
|
-
return
|
|
71
|
-
|
|
72
|
-
|
|
86
|
+
export const publicClassification = (resolved, usage) => {
|
|
87
|
+
if (resolved.state === "NotConfigured")
|
|
88
|
+
return resolved;
|
|
89
|
+
return {
|
|
90
|
+
state: "Configured",
|
|
91
|
+
presetId: resolved.presetId.value,
|
|
92
|
+
model: resolved.model.value,
|
|
93
|
+
baseUrl: resolved.baseUrl.value,
|
|
94
|
+
apiKeySaved: resolved.apiKey.source === ClassificationSource.Saved,
|
|
95
|
+
enabled: resolved.enabled.value,
|
|
96
|
+
dailyLimit: resolved.dailyLimit.value,
|
|
97
|
+
savedAt: resolved.classifiesAfter,
|
|
98
|
+
usage,
|
|
99
|
+
};
|
|
73
100
|
};
|
|
74
101
|
const CLASSIFICATION_INPUT_TOKENS_PER_CALL = 1_500;
|
|
75
102
|
const nextUtcMidnight = (epochMs) => {
|
|
@@ -109,15 +136,15 @@ export const readClassificationUsage = async (accountSetting, accountConfigId, a
|
|
|
109
136
|
});
|
|
110
137
|
};
|
|
111
138
|
export const classificationPresetsResponse = () => Object.values(CLASSIFICATION_PRESETS);
|
|
112
|
-
export const decideClassificationChange = (input,
|
|
139
|
+
export const decideClassificationChange = (input, known) => {
|
|
113
140
|
let provider;
|
|
114
141
|
if (savesClassificationProvider(input)) {
|
|
115
|
-
const decided = decideClassificationProvider(input,
|
|
142
|
+
const decided = decideClassificationProvider(input, known);
|
|
116
143
|
if (!decided.ok)
|
|
117
144
|
return decided;
|
|
118
145
|
provider = decided.value;
|
|
119
146
|
}
|
|
120
|
-
const configured = provider !== undefined ||
|
|
147
|
+
const configured = provider !== undefined || known.state === "Configured";
|
|
121
148
|
if (input.classificationEnabled === true && !configured) {
|
|
122
149
|
return refuse("ClassificationNotConfigured", "classificationEnabled needs a saved classification provider");
|
|
123
150
|
}
|
|
@@ -1,10 +1,9 @@
|
|
|
1
1
|
import type { ClassificationTestFailed, ClassificationTestInput, ClassificationTestPassed } from "@remit/api-openapi-types";
|
|
2
|
-
import type {
|
|
2
|
+
import type { ClassificationResolverDeps } from "@remit/data-ports/classification-resolver";
|
|
3
3
|
import { type ClassifierLogger } from "@remit/mailbox-service/classifier";
|
|
4
4
|
import { type SecretsService } from "@remit/secrets-service";
|
|
5
5
|
import type { UserSettingsOutcome } from "./user-settings.js";
|
|
6
|
-
export interface ClassificationTestDeps {
|
|
7
|
-
accountSetting: Pick<IAccountSettingRepository, "getMany">;
|
|
6
|
+
export interface ClassificationTestDeps extends ClassificationResolverDeps {
|
|
8
7
|
secrets: Pick<SecretsService, "decrypt">;
|
|
9
8
|
logger: ClassifierLogger;
|
|
10
9
|
}
|
|
@@ -1,8 +1,7 @@
|
|
|
1
|
-
import { readStoredClassification } from "@remit/data-ports/classification-presets";
|
|
2
1
|
import { ClassificationFailureKind as FailureKind, ClassificationField as Field, } from "@remit/domain-enums";
|
|
3
2
|
import { CLASSIFIER_LABELS, CLASSIFIER_SAMPLE_MESSAGE, classifyWithProvider, } from "@remit/mailbox-service/classifier";
|
|
4
3
|
import { deserializeEncryptedPayload, } from "@remit/secrets-service";
|
|
5
|
-
import { decideClassificationProvider, } from "./classification-settings.js";
|
|
4
|
+
import { decideClassificationProvider, readKnownClassification, } from "./classification-settings.js";
|
|
6
5
|
const REACH_FIX = "Check the endpoint runs and the Remit server can reach it";
|
|
7
6
|
const DEFAULT_MODEL_FIX = "Use the preset's default model";
|
|
8
7
|
const FAILURE_HELP = {
|
|
@@ -59,7 +58,7 @@ export const testClassificationProvider = async (deps, accountConfigId, input) =
|
|
|
59
58
|
classificationModel: input.model,
|
|
60
59
|
classificationBaseUrl: input.baseUrl,
|
|
61
60
|
classificationApiKey: input.apiKey,
|
|
62
|
-
}, await
|
|
61
|
+
}, await readKnownClassification(deps, accountConfigId));
|
|
63
62
|
if (!decided.ok)
|
|
64
63
|
return decided;
|
|
65
64
|
const save = decided.value;
|
|
@@ -1,10 +1,13 @@
|
|
|
1
1
|
import type { UpdateUserSettingsInput, UserSettingsResponse } from "@remit/api-openapi-types";
|
|
2
|
-
import type { IAccountRepository, IAccountSettingRepository } from "@remit/data-ports";
|
|
2
|
+
import type { IAccountConfigRepository, IAccountRepository, IAccountSettingRepository } from "@remit/data-ports";
|
|
3
|
+
import type { ClassificationDeploymentDefault } from "@remit/data-ports/classification-presets";
|
|
3
4
|
import type { ClassificationLabelDeps } from "@remit/mailbox-service";
|
|
4
5
|
import type { SecretsService } from "@remit/secrets-service";
|
|
5
6
|
import type { OperationHandler, UserSettingsOperationIds } from "../types.js";
|
|
6
7
|
export interface UserSettingsDeps {
|
|
7
8
|
accountSetting: Pick<IAccountSettingRepository, "get" | "getMany" | "upsert" | "delete">;
|
|
9
|
+
accountConfig: Pick<IAccountConfigRepository, "get">;
|
|
10
|
+
classificationDefault: ClassificationDeploymentDefault;
|
|
8
11
|
accounts: Pick<IAccountRepository, "listAllByAccountConfig">;
|
|
9
12
|
secrets: Pick<SecretsService, "encrypt">;
|
|
10
13
|
label: ClassificationLabelDeps;
|
|
@@ -1,10 +1,9 @@
|
|
|
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";
|
|
4
3
|
import { AccountSettingName } from "@remit/domain-enums";
|
|
5
4
|
import { logger } from "@remit/logger";
|
|
6
5
|
import { badRequest } from "./calendar.js";
|
|
7
|
-
import { classificationPresetsResponse, clearClassification, decideClassificationChange, publicClassification, readClassificationUsage, writeClassificationChange, } from "./classification-settings.js";
|
|
6
|
+
import { classificationPresetsResponse, clearClassification, decideClassificationChange, publicClassification, readClassificationUsage, readKnownClassification, writeClassificationChange, } from "./classification-settings.js";
|
|
8
7
|
import { testClassificationProvider } from "./classification-test.js";
|
|
9
8
|
/** The account the reader chose to send invitations from, if they chose one. */
|
|
10
9
|
export const chosenInvitationSender = async (accountSetting, accountConfigId) => {
|
|
@@ -17,19 +16,25 @@ export const readUserSettings = async (deps, accountConfigId) => {
|
|
|
17
16
|
const chosen = await chosenInvitationSender(deps.accountSetting, accountConfigId);
|
|
18
17
|
const accounts = await deps.accounts.listAllByAccountConfig(accountConfigId);
|
|
19
18
|
const inEffect = invitationSenderInEffect(accounts, chosen);
|
|
20
|
-
const
|
|
21
|
-
|
|
22
|
-
|
|
19
|
+
const resolved = await readKnownClassification(deps, accountConfigId);
|
|
20
|
+
if (resolved.state === "Unrunnable") {
|
|
21
|
+
throw new Error(`Classification provider ${resolved.presetId} cannot run: ${resolved.reason}`);
|
|
22
|
+
}
|
|
23
|
+
const usage = resolved.state === "Configured"
|
|
24
|
+
? await readClassificationUsage(deps.accountSetting, accountConfigId, accounts.map((account) => account.accountId), {
|
|
25
|
+
presetId: resolved.presetId.value,
|
|
26
|
+
dailyLimit: resolved.dailyLimit.value,
|
|
27
|
+
}, Date.now())
|
|
23
28
|
: [];
|
|
24
29
|
return {
|
|
25
30
|
...(inEffect && { defaultInvitationSenderAccountId: inEffect.accountId }),
|
|
26
31
|
defaultInvitationSenderChosen: inEffect !== undefined && inEffect.accountId === chosen,
|
|
27
32
|
classificationPresets: classificationPresetsResponse(),
|
|
28
|
-
classification: publicClassification(
|
|
33
|
+
classification: publicClassification(resolved, usage),
|
|
29
34
|
};
|
|
30
35
|
};
|
|
31
36
|
export const updateUserSettingsFor = async (deps, accountConfigId, input) => {
|
|
32
|
-
const classification = decideClassificationChange(input, await
|
|
37
|
+
const classification = decideClassificationChange(input, await readKnownClassification(deps, accountConfigId));
|
|
33
38
|
if (!classification.ok)
|
|
34
39
|
return classification;
|
|
35
40
|
const sender = input.defaultInvitationSenderAccountId;
|
|
@@ -54,6 +59,8 @@ export const updateUserSettingsFor = async (deps, accountConfigId, input) => {
|
|
|
54
59
|
};
|
|
55
60
|
const depsOf = (ctx) => ({
|
|
56
61
|
accountSetting: ctx.client.accountSetting,
|
|
62
|
+
accountConfig: ctx.client.accountConfig,
|
|
63
|
+
classificationDefault: ctx.client.classificationDefault,
|
|
57
64
|
accounts: ctx.client.account,
|
|
58
65
|
secrets: ctx.client.secrets,
|
|
59
66
|
label: ctx.client.label,
|
|
@@ -79,6 +86,8 @@ export const UserSettingsOperations = {
|
|
|
79
86
|
UserSettingsOperations_testClassificationProvider: async (context, ctx) => {
|
|
80
87
|
const tested = await testClassificationProvider({
|
|
81
88
|
accountSetting: ctx.client.accountSetting,
|
|
89
|
+
accountConfig: ctx.client.accountConfig,
|
|
90
|
+
classificationDefault: ctx.client.classificationDefault,
|
|
82
91
|
secrets: ctx.client.secrets,
|
|
83
92
|
logger,
|
|
84
93
|
}, ctx.identity.accountConfigId, context.request.requestBody);
|
|
@@ -161,6 +161,8 @@ export const composeReadPathServices = (core, queues, sync) => ({
|
|
|
161
161
|
accountService: core.account,
|
|
162
162
|
}, {
|
|
163
163
|
accountSetting: core.accountSetting,
|
|
164
|
+
accountConfig: core.accountConfig,
|
|
165
|
+
classificationDefault: core.classificationDefault,
|
|
164
166
|
secrets: core.secrets,
|
|
165
167
|
labelService: core.label,
|
|
166
168
|
messageLabelService: core.messageLabel,
|