@123toto/ai-app-assistant-server 0.2.0 → 0.2.2
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/README.md +21 -0
- package/dist/express.d.cts +1 -1
- package/dist/express.d.ts +1 -1
- package/dist/index.cjs +60 -8
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +11 -3
- package/dist/index.d.ts +11 -3
- package/dist/index.js +59 -8
- package/dist/index.js.map +1 -1
- package/dist/{managed-server-BGQ-1jkx.d.ts → managed-server-CusQWWBB.d.ts} +10 -3
- package/dist/{managed-server-CthTKEQ6.d.cts → managed-server-wXfu_9rp.d.cts} +10 -3
- package/dist/nest.d.cts +1 -1
- package/dist/nest.d.ts +1 -1
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -44,6 +44,7 @@ import { createManagedAiAppAssistantServer } from "@123toto/ai-app-assistant-ser
|
|
|
44
44
|
|
|
45
45
|
const aiAppAssistant = createManagedAiAppAssistantServer({
|
|
46
46
|
configuration: {
|
|
47
|
+
enabled: process.env.AI_APP_ASSISTANT_LLM_ENABLED === "true",
|
|
47
48
|
storage: { type: "redis", client: redis },
|
|
48
49
|
encryptionKey: process.env.AI_APP_ASSISTANT_SECRET_ENCRYPTION_KEY!,
|
|
49
50
|
defaultConfiguration: {
|
|
@@ -63,6 +64,26 @@ const aiAppAssistant = createManagedAiAppAssistantServer({
|
|
|
63
64
|
await aiAppAssistant.initialize();
|
|
64
65
|
```
|
|
65
66
|
|
|
67
|
+
For deployments exposing one provider at a time, the library maps generic
|
|
68
|
+
runtime values without requiring provider-specific environment variables:
|
|
69
|
+
|
|
70
|
+
```ts
|
|
71
|
+
const deployment = createAiAppAssistantDeploymentDefaults({
|
|
72
|
+
enabled: env.AI_APP_ASSISTANT_LLM_ENABLED,
|
|
73
|
+
provider: env.AI_APP_ASSISTANT_PROVIDER,
|
|
74
|
+
model: env.AI_APP_ASSISTANT_LLM_MODEL,
|
|
75
|
+
apiKey: env.AI_APP_ASSISTANT_API_KEY,
|
|
76
|
+
baseURL: env.AI_APP_ASSISTANT_LOCAL_LLM_BASE_URL
|
|
77
|
+
});
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
`baseURL` is required for the local `ollama` provider. API keys and encryption
|
|
81
|
+
keys must come from a runtime secret store; they must never be committed or
|
|
82
|
+
embedded in an application image.
|
|
83
|
+
|
|
84
|
+
`enabled: false` disables every provider operation while keeping the host
|
|
85
|
+
application and the read-only administration endpoints available.
|
|
86
|
+
|
|
66
87
|
Documents may also be loaded after application bootstrap with `aiAppAssistant.setDocuments(documents)`.
|
|
67
88
|
|
|
68
89
|
## Framework connectors
|
package/dist/express.d.cts
CHANGED
package/dist/express.d.ts
CHANGED
package/dist/index.cjs
CHANGED
|
@@ -20,6 +20,7 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
|
|
|
20
20
|
// src/index.ts
|
|
21
21
|
var index_exports = {};
|
|
22
22
|
__export(index_exports, {
|
|
23
|
+
AI_APP_ASSISTANT_PROVIDERS: () => AI_APP_ASSISTANT_PROVIDERS,
|
|
23
24
|
AiAppAssistantConfigurationConflictError: () => AiAppAssistantConfigurationConflictError,
|
|
24
25
|
AiAppAssistantConfigurationManager: () => AiAppAssistantConfigurationManager,
|
|
25
26
|
AiAppAssistantManagementError: () => AiAppAssistantManagementError,
|
|
@@ -1464,6 +1465,7 @@ function quotaResult(count, resetAt, maxRequests, now) {
|
|
|
1464
1465
|
}
|
|
1465
1466
|
|
|
1466
1467
|
// src/provider-catalog.ts
|
|
1468
|
+
var AI_APP_ASSISTANT_PROVIDERS = ["anthropic", "google", "mistral", "ollama", "openai"];
|
|
1467
1469
|
var PROVIDERS = Object.freeze([
|
|
1468
1470
|
{ id: "anthropic", label: "Anthropic", requiresApiKey: true, supportsModelDiscovery: true },
|
|
1469
1471
|
{ id: "google", label: "Google Gemini", requiresApiKey: true, supportsModelDiscovery: true },
|
|
@@ -1609,12 +1611,17 @@ var AiAppAssistantConfigurationManager = class {
|
|
|
1609
1611
|
this.#repository = options.repository;
|
|
1610
1612
|
this.#quotaStore = options.quotaStore ?? createMemoryAiAppAssistantQuotaStore();
|
|
1611
1613
|
}
|
|
1614
|
+
/** Returns the current state of the host-controlled global kill switch. */
|
|
1615
|
+
isEnabled() {
|
|
1616
|
+
return typeof this.#options.enabled === "function" ? this.#options.enabled() : this.#options.enabled ?? true;
|
|
1617
|
+
}
|
|
1612
1618
|
/** Returns the built-in provider catalogue; no credentials are exposed. */
|
|
1613
1619
|
listProviders() {
|
|
1614
1620
|
return listAiProviders();
|
|
1615
1621
|
}
|
|
1616
1622
|
/** Discovers models with an explicit key or the currently configured secret. */
|
|
1617
1623
|
async listModels(input) {
|
|
1624
|
+
this.assertEnabled();
|
|
1618
1625
|
const apiKey = await this.resolveApiKey(input.provider, input.apiKey);
|
|
1619
1626
|
if (this.#options.listModels) {
|
|
1620
1627
|
return this.#options.listModels({ ...input, ...apiKey ? { apiKey } : {} });
|
|
@@ -1640,7 +1647,10 @@ var AiAppAssistantConfigurationManager = class {
|
|
|
1640
1647
|
let connected = event.connectionValidated;
|
|
1641
1648
|
if (event.reloadRequired) {
|
|
1642
1649
|
this.#recentConnectionValidation = void 0;
|
|
1643
|
-
if (
|
|
1650
|
+
if (!this.isEnabled()) {
|
|
1651
|
+
connected = false;
|
|
1652
|
+
this.#runtimeConnection = { status: "disabled", checkedAt: this.now() };
|
|
1653
|
+
} else if (connected) {
|
|
1644
1654
|
const configuration = await this.getRuntimeConfiguration();
|
|
1645
1655
|
this.#runtimeConnection = configuration ? {
|
|
1646
1656
|
status: "connected",
|
|
@@ -1670,6 +1680,7 @@ var AiAppAssistantConfigurationManager = class {
|
|
|
1670
1680
|
}
|
|
1671
1681
|
/** Tests credentials and briefly caches a successful result for the next save. */
|
|
1672
1682
|
async testConnection(input) {
|
|
1683
|
+
this.assertEnabled();
|
|
1673
1684
|
const apiKey = await this.resolveApiKey(input.provider, input.apiKey);
|
|
1674
1685
|
const connection = this.#options.testConnection ? await this.#options.testConnection({ ...input, ...apiKey ? { apiKey } : {} }) : await testAiSdkConnection({
|
|
1675
1686
|
model: `${input.provider}:${input.model}`,
|
|
@@ -1698,6 +1709,10 @@ var AiAppAssistantConfigurationManager = class {
|
|
|
1698
1709
|
/** Checks the effective stored/deployment connection used by live questions. */
|
|
1699
1710
|
async validateRuntimeConnection() {
|
|
1700
1711
|
this.#lastReconnectAttempt = Date.now();
|
|
1712
|
+
if (!this.isEnabled()) {
|
|
1713
|
+
this.#runtimeConnection = { status: "disabled", checkedAt: this.now() };
|
|
1714
|
+
return false;
|
|
1715
|
+
}
|
|
1701
1716
|
const configuration = await this.getRuntimeConfiguration();
|
|
1702
1717
|
if (!configuration || configuration.provider !== "ollama" && !configuration.apiKey) {
|
|
1703
1718
|
this.#runtimeConnection = {
|
|
@@ -1740,6 +1755,7 @@ var AiAppAssistantConfigurationManager = class {
|
|
|
1740
1755
|
const initialActive = this.effectiveConfiguration(initial);
|
|
1741
1756
|
const initialApiKey = input.apiKey ?? (initialActive?.provider === input.provider ? initialActive.apiKey : void 0) ?? this.resolveDefaultApiKey(input.provider);
|
|
1742
1757
|
const initialConnectionChanged = connectionChanged(initialActive, input);
|
|
1758
|
+
if (!this.isEnabled() && initialConnectionChanged) throw disabled();
|
|
1743
1759
|
let connection = initialConnectionChanged ? await this.validateConnectionForSave(input, initialApiKey) : this.lastKnownConnection(input.provider, input.model);
|
|
1744
1760
|
if (initialConnectionChanged && !connection.success) {
|
|
1745
1761
|
return { saved: false, connection, reloadRequired: false };
|
|
@@ -1968,6 +1984,7 @@ var AiAppAssistantConfigurationManager = class {
|
|
|
1968
1984
|
}
|
|
1969
1985
|
/** Combines configuration, provider health and application access rules. */
|
|
1970
1986
|
async canUse(identity) {
|
|
1987
|
+
if (!this.isEnabled()) return false;
|
|
1971
1988
|
const configuration = await this.getRuntimeConfiguration();
|
|
1972
1989
|
if (!configuration || configuration.provider !== "ollama" && !configuration.apiKey) return false;
|
|
1973
1990
|
if (!await this.ensureRuntimeConnection()) return false;
|
|
@@ -1986,6 +2003,7 @@ var AiAppAssistantConfigurationManager = class {
|
|
|
1986
2003
|
}
|
|
1987
2004
|
/** Enforces access and quota immediately before any model call. */
|
|
1988
2005
|
async assertCanAsk(identity) {
|
|
2006
|
+
this.assertEnabled();
|
|
1989
2007
|
if (!await this.canUse(identity)) {
|
|
1990
2008
|
throw forbidden("AI assistant access is not enabled for this user");
|
|
1991
2009
|
}
|
|
@@ -2001,6 +2019,7 @@ var AiAppAssistantConfigurationManager = class {
|
|
|
2001
2019
|
}
|
|
2002
2020
|
/** Retries a failed provider lazily, with a shared backoff across requests. */
|
|
2003
2021
|
async ensureRuntimeConnection() {
|
|
2022
|
+
if (!this.isEnabled()) return false;
|
|
2004
2023
|
if (this.#runtimeConnection.status === "connected") return true;
|
|
2005
2024
|
const intervalMs = Math.max(1e3, this.#options.reconnectIntervalMs ?? 3e4);
|
|
2006
2025
|
if (Date.now() - this.#lastReconnectAttempt < intervalMs) return false;
|
|
@@ -2108,6 +2127,9 @@ var AiAppAssistantConfigurationManager = class {
|
|
|
2108
2127
|
now() {
|
|
2109
2128
|
return (this.#options.now?.() ?? /* @__PURE__ */ new Date()).toISOString();
|
|
2110
2129
|
}
|
|
2130
|
+
assertEnabled() {
|
|
2131
|
+
if (!this.isEnabled()) throw disabled();
|
|
2132
|
+
}
|
|
2111
2133
|
async publishAndEmit(event) {
|
|
2112
2134
|
await this.#options.synchronizer?.publish(event);
|
|
2113
2135
|
await this.emit(event);
|
|
@@ -2231,6 +2253,13 @@ function configurationChanges(previous, input, allowModelChangesByOthers) {
|
|
|
2231
2253
|
function forbidden(message) {
|
|
2232
2254
|
return new AiAppAssistantManagementError(403, "forbidden", message);
|
|
2233
2255
|
}
|
|
2256
|
+
function disabled() {
|
|
2257
|
+
return new AiAppAssistantManagementError(
|
|
2258
|
+
503,
|
|
2259
|
+
"assistant_disabled",
|
|
2260
|
+
"AI assistant is disabled"
|
|
2261
|
+
);
|
|
2262
|
+
}
|
|
2234
2263
|
|
|
2235
2264
|
// src/telemetry.ts
|
|
2236
2265
|
function createMemoryAiAppAssistantTelemetryStore(options) {
|
|
@@ -2400,6 +2429,11 @@ function createManagedAiAppAssistantRuntime(options) {
|
|
|
2400
2429
|
return reloadQueue;
|
|
2401
2430
|
});
|
|
2402
2431
|
const rebuild = async (event) => {
|
|
2432
|
+
if (!options.configuration.isEnabled()) {
|
|
2433
|
+
assistant = void 0;
|
|
2434
|
+
activeGenerator = void 0;
|
|
2435
|
+
return;
|
|
2436
|
+
}
|
|
2403
2437
|
const connected = event.connectionValidated || await options.configuration.validateRuntimeConnection();
|
|
2404
2438
|
const configuration = await options.configuration.getRuntimeConfiguration();
|
|
2405
2439
|
if (!connected || !configuration) {
|
|
@@ -2786,21 +2820,38 @@ function createAiAppAssistantDeploymentDefaults(options) {
|
|
|
2786
2820
|
const apiKeys = Object.fromEntries(
|
|
2787
2821
|
Object.entries(options.apiKeys ?? {}).map(([provider2, value]) => [provider2, value?.trim() || void 0])
|
|
2788
2822
|
);
|
|
2789
|
-
const
|
|
2823
|
+
const selectedProvider = options.provider;
|
|
2824
|
+
const selectedApiKey = options.apiKey?.trim() || void 0;
|
|
2825
|
+
const resolveApiKey2 = (provider2) => provider2 === selectedProvider ? selectedApiKey ?? apiKeys[provider2] : apiKeys[provider2];
|
|
2790
2826
|
if (!options.enabled || !options.model?.trim()) return { resolveApiKey: resolveApiKey2 };
|
|
2791
|
-
const
|
|
2792
|
-
|
|
2793
|
-
const
|
|
2827
|
+
const rawModel = options.model.trim();
|
|
2828
|
+
const legacyMatch = selectedProvider ? void 0 : rawModel.match(/^([^:/]+)[:/](.+)$/);
|
|
2829
|
+
const explicitMatch = selectedProvider ? rawModel.match(/^([^:]+):(.+)$/) : void 0;
|
|
2830
|
+
if (!selectedProvider && !legacyMatch) {
|
|
2831
|
+
throw new TypeError(`Invalid AI App Assistant model identifier: ${options.model}`);
|
|
2832
|
+
}
|
|
2833
|
+
const parsedProvider = legacyMatch?.[1] ?? explicitMatch?.[1];
|
|
2834
|
+
const modelProvider = parsedProvider === "gemini" ? "google" : parsedProvider ?? selectedProvider;
|
|
2835
|
+
const provider = modelProvider;
|
|
2794
2836
|
if (!listAiProviders().some((candidate) => candidate.id === provider)) {
|
|
2795
|
-
throw new TypeError(`Unsupported AI App Assistant provider: ${
|
|
2837
|
+
throw new TypeError(`Unsupported AI App Assistant provider: ${modelProvider}`);
|
|
2796
2838
|
}
|
|
2839
|
+
if (selectedProvider && explicitMatch && provider !== selectedProvider) {
|
|
2840
|
+
throw new TypeError(
|
|
2841
|
+
`AI App Assistant provider '${selectedProvider}' does not match model identifier '${options.model}'`
|
|
2842
|
+
);
|
|
2843
|
+
}
|
|
2844
|
+
const model = legacyMatch?.[2]?.trim() ?? explicitMatch?.[2]?.trim() ?? rawModel;
|
|
2797
2845
|
const apiKey = resolveApiKey2(provider);
|
|
2798
|
-
const baseURL = options.baseURLs?.[provider]?.trim() || void 0;
|
|
2846
|
+
const baseURL = (provider === selectedProvider ? options.baseURL?.trim() : void 0) || options.baseURLs?.[provider]?.trim() || void 0;
|
|
2847
|
+
if (provider === "ollama" && !baseURL) {
|
|
2848
|
+
throw new TypeError("AI App Assistant local provider 'ollama' requires a base URL");
|
|
2849
|
+
}
|
|
2799
2850
|
return {
|
|
2800
2851
|
resolveApiKey: resolveApiKey2,
|
|
2801
2852
|
configuration: {
|
|
2802
2853
|
provider,
|
|
2803
|
-
model
|
|
2854
|
+
model,
|
|
2804
2855
|
connectionSource: "environment",
|
|
2805
2856
|
...apiKey ? { apiKey } : {},
|
|
2806
2857
|
...baseURL ? { baseURL } : {},
|
|
@@ -3010,6 +3061,7 @@ function clampInteger3(value, minimum, maximum) {
|
|
|
3010
3061
|
var import_ai_app_assistant_contracts6 = require("@123toto/ai-app-assistant-contracts");
|
|
3011
3062
|
// Annotate the CommonJS export names for ESM import in node:
|
|
3012
3063
|
0 && (module.exports = {
|
|
3064
|
+
AI_APP_ASSISTANT_PROVIDERS,
|
|
3013
3065
|
AiAppAssistantConfigurationConflictError,
|
|
3014
3066
|
AiAppAssistantConfigurationManager,
|
|
3015
3067
|
AiAppAssistantManagementError,
|