@fonoster/autopilot 0.30.0 → 0.31.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/README.md +5 -5
- package/dist/knowledge/MeteredEmbeddings.d.ts +47 -0
- package/dist/knowledge/MeteredEmbeddings.js +58 -0
- package/dist/models/createLanguageModel.d.ts +2 -2
- package/dist/models/createLanguageModel.js +2 -2
- package/dist/models/evaluations/createEvalEffectiveConfig.d.ts +6 -6
- package/dist/models/evaluations/createEvalEffectiveConfig.js +7 -7
- package/dist/models/evaluations/createEvaluateIntelligence.d.ts +15 -4
- package/dist/models/evaluations/createEvaluateIntelligence.js +33 -12
- package/dist/models/evaluations/createTestTextSimilarity.d.ts +7 -1
- package/dist/models/evaluations/createTestTextSimilarity.js +19 -1
- package/dist/models/evaluations/evalTestCases.d.ts +2 -2
- package/dist/models/evaluations/evalTestCases.js +7 -7
- package/dist/models/evaluations/evaluateScenario.js +2 -2
- package/dist/models/evaluations/evaluateStep.d.ts +1 -1
- package/dist/models/evaluations/evaluateStep.js +2 -2
- package/dist/models/evaluations/extractAiResponse.d.ts +2 -2
- package/dist/models/evaluations/extractAiResponse.js +3 -3
- package/dist/models/evaluations/runEval.d.ts +2 -2
- package/dist/models/evaluations/runEval.js +7 -7
- package/dist/models/evaluations/session/EvalSession.d.ts +1 -1
- package/dist/models/evaluations/session/EvalSession.js +1 -1
- package/dist/models/evaluations/session/evaluateSessionScenario.d.ts +3 -3
- package/dist/models/evaluations/session/evaluateSessionScenario.js +4 -4
- package/dist/models/evaluations/session/runSessionEval.d.ts +7 -4
- package/dist/models/evaluations/session/runSessionEval.js +7 -7
- package/dist/models/evaluations/textSimilaryPrompt.d.ts +1 -1
- package/dist/models/evaluations/textSimilaryPrompt.js +1 -1
- package/dist/models/evaluations/types.d.ts +5 -5
- package/dist/models/types.d.ts +3 -3
- package/dist/session/Agent.d.ts +38 -3
- package/dist/session/Agent.js +90 -16
- package/dist/session/config.d.ts +2 -2
- package/dist/session/config.js +4 -4
- package/dist/session/createAutopilot.d.ts +12 -11
- package/dist/session/createAutopilot.js +48 -16
- package/dist/session/hosted.d.ts +23 -25
- package/dist/session/hosted.js +43 -48
- package/dist/session/index.d.ts +1 -1
- package/dist/session/index.js +1 -1
- package/dist/session/knowledge.d.ts +1 -1
- package/dist/session/llm.d.ts +28 -1
- package/dist/session/llm.js +50 -6
- package/dist/session/main.js +2 -1
- package/dist/session/meteredThink.d.ts +45 -0
- package/dist/session/meteredThink.js +125 -0
- package/dist/session/telemetry.d.ts +34 -0
- package/dist/session/telemetry.js +55 -0
- package/dist/session/tools.d.ts +1 -1
- package/dist/session/tools.js +1 -1
- package/package.json +9 -9
- package/dist/session/convertLegacyAssistant.d.ts +0 -77
- package/dist/session/convertLegacyAssistant.js +0 -118
|
@@ -23,7 +23,10 @@ const logger_1 = require("@fonoster/logger");
|
|
|
23
23
|
const xstate_1 = require("xstate");
|
|
24
24
|
const Agent_1 = require("./Agent");
|
|
25
25
|
const config_1 = require("./config");
|
|
26
|
+
const llm_1 = require("./llm");
|
|
26
27
|
const machine_1 = require("./machine");
|
|
28
|
+
const meteredThink_1 = require("./meteredThink");
|
|
29
|
+
const telemetry_1 = require("./telemetry");
|
|
27
30
|
const think_1 = require("./think");
|
|
28
31
|
const tools_1 = require("./tools");
|
|
29
32
|
const logger = (0, logger_1.getLogger)({ service: "autopilot", filePath: __filename });
|
|
@@ -32,8 +35,26 @@ const logger = (0, logger_1.getLogger)({ service: "autopilot", filePath: __filen
|
|
|
32
35
|
* runs the state machine on the session's public messages only.
|
|
33
36
|
*/
|
|
34
37
|
function createAutopilotHandler(options) {
|
|
38
|
+
const run = createSessionRunner(options);
|
|
35
39
|
return async (session) => {
|
|
36
|
-
//
|
|
40
|
+
// The session span joins the call's trace when AppBridge sent a traceparent.
|
|
41
|
+
const span = telemetry_1.telemetry.startCall(session.callRef, "autopilot.session", {
|
|
42
|
+
"fonoster.app_ref": session.appRef,
|
|
43
|
+
"fonoster.session_ref": session.sessionRef
|
|
44
|
+
}, session.traceparent);
|
|
45
|
+
try {
|
|
46
|
+
await run(session);
|
|
47
|
+
span.end();
|
|
48
|
+
}
|
|
49
|
+
catch (e) {
|
|
50
|
+
span.end({}, e.message);
|
|
51
|
+
throw e;
|
|
52
|
+
}
|
|
53
|
+
};
|
|
54
|
+
}
|
|
55
|
+
function createSessionRunner(options) {
|
|
56
|
+
return async (session) => {
|
|
57
|
+
// Listen before any await: turn events that arrive while the agent and model load are
|
|
37
58
|
// held and delivered once the machine runs.
|
|
38
59
|
const early = [];
|
|
39
60
|
let deliver = (event) => early.push(event);
|
|
@@ -47,37 +68,48 @@ function createAutopilotHandler(options) {
|
|
|
47
68
|
deliver({ type: "TURN_ENDED", text: e.text });
|
|
48
69
|
});
|
|
49
70
|
session.on("turn.backchannel", () => deliver({ type: "TURN_BACKCHANNEL" }));
|
|
71
|
+
// Open drains of interrupted replies stop with the session: their usage can no longer
|
|
72
|
+
// reach the call (add-usage-metering D6).
|
|
73
|
+
const closed = new AbortController();
|
|
50
74
|
const ended = new Promise((resolve) => session.on("end", () => {
|
|
75
|
+
closed.abort();
|
|
51
76
|
deliver({ type: "SESSION_END" });
|
|
52
77
|
resolve();
|
|
53
78
|
}));
|
|
54
79
|
const loaded = options.load
|
|
55
80
|
? await options.load(session)
|
|
56
81
|
: {
|
|
57
|
-
|
|
58
|
-
? await options.
|
|
59
|
-
: options.
|
|
82
|
+
agent: typeof options.agent === "function"
|
|
83
|
+
? await options.agent(session)
|
|
84
|
+
: options.agent
|
|
60
85
|
};
|
|
61
86
|
if (!loaded) {
|
|
62
|
-
logger.warn("no
|
|
87
|
+
logger.warn("no agent for this app; ending the call", {
|
|
63
88
|
appRef: session.appRef,
|
|
64
89
|
callRef: session.callRef
|
|
65
90
|
});
|
|
66
91
|
await session.hangup().catch(() => undefined);
|
|
67
92
|
return;
|
|
68
93
|
}
|
|
69
|
-
const
|
|
94
|
+
const agentConfig = config_1.sessionAgentSchema.parse(loaded.agent);
|
|
70
95
|
const scope = { workspaceRef: loaded.workspaceRef };
|
|
71
96
|
const recorder = options.recorder ?? (0, Agent_1.sessionRecorder)(session);
|
|
72
97
|
const settings = {
|
|
73
|
-
temperature:
|
|
74
|
-
maxTokens:
|
|
98
|
+
temperature: agentConfig.languageModel.temperature,
|
|
99
|
+
maxTokens: agentConfig.languageModel.maxTokens
|
|
100
|
+
};
|
|
101
|
+
const sink = {
|
|
102
|
+
usage: (lines) => recorder.usage?.(lines),
|
|
103
|
+
missing: (catalogId) => {
|
|
104
|
+
telemetry_1.telemetry.count("fonoster.usage.missing", 1, { catalog_id: catalogId });
|
|
105
|
+
recorder.note({ type: "usage_missing", catalogId });
|
|
106
|
+
}
|
|
75
107
|
};
|
|
76
108
|
let think = options.think;
|
|
77
109
|
if (!think && options.llm) {
|
|
78
110
|
try {
|
|
79
|
-
const resolved = await options.llm.resolve(
|
|
80
|
-
think = resolved.think;
|
|
111
|
+
const resolved = await options.llm.resolve(agentConfig.languageModel.model, settings, scope);
|
|
112
|
+
think = (0, meteredThink_1.meteredThink)(resolved.think, { catalogId: resolved.id, keySource: resolved.keySource }, sink, { closed: closed.signal });
|
|
81
113
|
if (resolved.event)
|
|
82
114
|
recorder.note(resolved.event);
|
|
83
115
|
}
|
|
@@ -86,10 +118,10 @@ function createAutopilotHandler(options) {
|
|
|
86
118
|
logger.error(`autopilot cannot reach its language model: ${e.message}`);
|
|
87
119
|
recorder.note({
|
|
88
120
|
type: "llm.unavailable",
|
|
89
|
-
reason: e.message
|
|
121
|
+
reason: e instanceof llm_1.KeyUnavailableError ? e.reason : e.message
|
|
90
122
|
});
|
|
91
123
|
await session
|
|
92
|
-
.say(
|
|
124
|
+
.say(agentConfig.conversationSettings.systemErrorMessage, {
|
|
93
125
|
interruptible: false
|
|
94
126
|
})
|
|
95
127
|
.catch(() => undefined);
|
|
@@ -97,11 +129,11 @@ function createAutopilotHandler(options) {
|
|
|
97
129
|
return;
|
|
98
130
|
}
|
|
99
131
|
}
|
|
100
|
-
think ?? (think = (0, think_1.resolveThink)(
|
|
101
|
-
const agent = new Agent_1.Agent(session,
|
|
102
|
-
new tools_1.HttpToolPort(
|
|
132
|
+
think ?? (think = (0, meteredThink_1.meteredThink)((0, think_1.resolveThink)(agentConfig.languageModel.model, options.providers ?? {}, settings), { catalogId: agentConfig.languageModel.model, keySource: "platform" }, sink, { closed: closed.signal }));
|
|
133
|
+
const agent = new Agent_1.Agent(session, agentConfig, think, options.tools ??
|
|
134
|
+
new tools_1.HttpToolPort(agentConfig.languageModel.tools, {
|
|
103
135
|
allow: options.allowedDestinations
|
|
104
|
-
}), recorder, options.knowledge ?? options.knowledgeFor?.(
|
|
136
|
+
}), recorder, options.knowledge ?? options.knowledgeFor?.(agentConfig, scope, sink));
|
|
105
137
|
options.onAgent?.(agent);
|
|
106
138
|
const actor = (0, xstate_1.createActor)(machine_1.autopilotMachine, { input: { agent } });
|
|
107
139
|
agent.send = (event) => actor.send(event);
|
package/dist/session/hosted.d.ts
CHANGED
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
import { CellSecrets, Registry } from "@fonoster/common";
|
|
2
2
|
import { Session, VoiceServer } from "@fonoster/voice";
|
|
3
3
|
import { z } from "zod";
|
|
4
|
-
import {
|
|
5
|
-
import { AutopilotOptions,
|
|
4
|
+
import { SessionAgent } from "./config";
|
|
5
|
+
import { AutopilotOptions, LoadedAgent } from "./createAutopilot";
|
|
6
6
|
import { KnowledgePort } from "./knowledge";
|
|
7
7
|
import { CredentialSource } from "./llm";
|
|
8
|
+
import { UsageSink } from "./meteredThink";
|
|
8
9
|
export declare const hostedConfigSchema: z.ZodObject<{
|
|
9
10
|
host: z.ZodDefault<z.ZodString>;
|
|
10
11
|
port: z.ZodDefault<z.ZodNumber>;
|
|
@@ -13,7 +14,7 @@ export declare const hostedConfigSchema: z.ZodObject<{
|
|
|
13
14
|
* FONOSTER_ALLOWED_DESTINATIONS.
|
|
14
15
|
*/
|
|
15
16
|
allowedDestinations: z.ZodOptional<z.ZodArray<z.ZodString, "many">>;
|
|
16
|
-
/** The secret
|
|
17
|
+
/** The secret AppBridge signs Autopilot sessions with (one for every Autopilot app). */
|
|
17
18
|
signingSecret: z.ZodUnion<[z.ZodObject<{
|
|
18
19
|
env: z.ZodString;
|
|
19
20
|
}, "strict", z.ZodTypeAny, {
|
|
@@ -34,10 +35,10 @@ export declare const hostedConfigSchema: z.ZodObject<{
|
|
|
34
35
|
*/
|
|
35
36
|
publicUrl: z.ZodUnion<[z.ZodString, z.ZodArray<z.ZodString, "many">]>;
|
|
36
37
|
/**
|
|
37
|
-
* Apps and their
|
|
38
|
+
* Apps and their agents: a file `{apps: {<appRef>: {workspaceRef?, agent}}}` (JSON or
|
|
38
39
|
* YAML), or `database`: the apps' Intelligence rows, cached for `cacheMs` (CP-B11).
|
|
39
40
|
*/
|
|
40
|
-
|
|
41
|
+
agents: z.ZodUnion<[z.ZodObject<{
|
|
41
42
|
file: z.ZodString;
|
|
42
43
|
}, "strict", z.ZodTypeAny, {
|
|
43
44
|
file?: string;
|
|
@@ -142,7 +143,7 @@ export declare const hostedConfigSchema: z.ZodObject<{
|
|
|
142
143
|
url?: string;
|
|
143
144
|
clientModule?: string;
|
|
144
145
|
}>>;
|
|
145
|
-
/** Endpoint overrides by
|
|
146
|
+
/** Endpoint overrides by provider or adapter id (e.g. a local ollama); the catalog's otherwise. */
|
|
146
147
|
endpoints: z.ZodDefault<z.ZodRecord<z.ZodString, z.ZodString>>;
|
|
147
148
|
/** The S3 + Unstructured knowledge bases; keys by credentialRef (task 4.1). */
|
|
148
149
|
knowledge: z.ZodOptional<z.ZodObject<{
|
|
@@ -227,7 +228,6 @@ export declare const hostedConfigSchema: z.ZodObject<{
|
|
|
227
228
|
}, {
|
|
228
229
|
otlpEndpoint?: string;
|
|
229
230
|
}>>;
|
|
230
|
-
testMode: z.ZodDefault<z.ZodBoolean>;
|
|
231
231
|
}, "strict", z.ZodTypeAny, {
|
|
232
232
|
registry?: {
|
|
233
233
|
file?: string;
|
|
@@ -244,7 +244,7 @@ export declare const hostedConfigSchema: z.ZodObject<{
|
|
|
244
244
|
value?: string;
|
|
245
245
|
};
|
|
246
246
|
publicUrl?: string | string[];
|
|
247
|
-
|
|
247
|
+
agents?: {
|
|
248
248
|
file?: string;
|
|
249
249
|
} | {
|
|
250
250
|
source?: "database";
|
|
@@ -292,7 +292,6 @@ export declare const hostedConfigSchema: z.ZodObject<{
|
|
|
292
292
|
telemetry?: {
|
|
293
293
|
otlpEndpoint?: string;
|
|
294
294
|
};
|
|
295
|
-
testMode?: boolean;
|
|
296
295
|
}, {
|
|
297
296
|
registry?: {
|
|
298
297
|
file?: string;
|
|
@@ -309,7 +308,7 @@ export declare const hostedConfigSchema: z.ZodObject<{
|
|
|
309
308
|
value?: string;
|
|
310
309
|
};
|
|
311
310
|
publicUrl?: string | string[];
|
|
312
|
-
|
|
311
|
+
agents?: {
|
|
313
312
|
file?: string;
|
|
314
313
|
} | {
|
|
315
314
|
source?: "database";
|
|
@@ -357,7 +356,6 @@ export declare const hostedConfigSchema: z.ZodObject<{
|
|
|
357
356
|
telemetry?: {
|
|
358
357
|
otlpEndpoint?: string;
|
|
359
358
|
};
|
|
360
|
-
testMode?: boolean;
|
|
361
359
|
}>;
|
|
362
360
|
export type HostedConfig = z.infer<typeof hostedConfigSchema>;
|
|
363
361
|
/** Where Autopilot looks for its file when none is named (autopilot.yaml, then the older name). */
|
|
@@ -369,17 +367,17 @@ export declare function findHostedConfig(): string | undefined;
|
|
|
369
367
|
* placeholders from the container environment, `secret://` references, strict validation.
|
|
370
368
|
*/
|
|
371
369
|
export declare function loadHostedConfig(file: string, env?: Record<string, string | undefined>): HostedConfig;
|
|
372
|
-
/** Apps and
|
|
373
|
-
export declare class
|
|
370
|
+
/** Apps and agents from a file (a stand-in until agents come from the control plane). */
|
|
371
|
+
export declare class FileAgentSource {
|
|
374
372
|
private readonly file;
|
|
375
373
|
constructor(file: string);
|
|
376
|
-
get(appRef: string): Promise<
|
|
374
|
+
get(appRef: string): Promise<LoadedAgent | undefined>;
|
|
377
375
|
}
|
|
378
376
|
/**
|
|
379
|
-
*
|
|
377
|
+
* Agents from the control plane: an Autopilot app's Intelligence row holds its agent
|
|
380
378
|
* (CP-B3). Each app is cached for `cacheMs` (misses too), so edits show up within seconds.
|
|
381
379
|
*/
|
|
382
|
-
export declare class
|
|
380
|
+
export declare class PrismaAgentSource {
|
|
383
381
|
private readonly prisma;
|
|
384
382
|
private readonly options;
|
|
385
383
|
private cache;
|
|
@@ -387,7 +385,7 @@ export declare class PrismaAssistantSource {
|
|
|
387
385
|
cacheMs?: number;
|
|
388
386
|
now?: () => number;
|
|
389
387
|
});
|
|
390
|
-
get(appRef: string): Promise<
|
|
388
|
+
get(appRef: string): Promise<LoadedAgent | undefined>;
|
|
391
389
|
}
|
|
392
390
|
/** Workspace vendor keys from the database, looked up by the credentialRef's providers. */
|
|
393
391
|
export declare class DatabaseWorkspaceSecrets implements CredentialSource {
|
|
@@ -424,13 +422,13 @@ type KnowledgeBaseLike = {
|
|
|
424
422
|
queryKnowledgeBase(query: string, k?: number): Promise<string>;
|
|
425
423
|
};
|
|
426
424
|
/**
|
|
427
|
-
* Builds the knowledge port for an
|
|
425
|
+
* Builds the knowledge port for an agent from the operator's S3 + Unstructured settings,
|
|
428
426
|
* with keys from the credential source (never Autopilot's environment). The bucket is the
|
|
429
427
|
* workspace's, as today. `make` is the knowledge base constructor (the S3 one by default).
|
|
430
428
|
*/
|
|
431
|
-
export declare function s3Knowledge(config: NonNullable<HostedConfig["knowledge"]>, credentials: CredentialSource, make?: (params: Record<string, unknown>) => KnowledgeBaseLike): (
|
|
429
|
+
export declare function s3Knowledge(config: NonNullable<HostedConfig["knowledge"]>, credentials: CredentialSource, make?: (params: Record<string, unknown>) => KnowledgeBaseLike): (agent: SessionAgent, scope: {
|
|
432
430
|
workspaceRef?: string;
|
|
433
|
-
}) => Promise<KnowledgePort | undefined>;
|
|
431
|
+
}, usage?: UsageSink) => Promise<KnowledgePort | undefined>;
|
|
434
432
|
/** The handler options the hosted Autopilot runs with, built from its config. */
|
|
435
433
|
export declare function hostedOptions(config: HostedConfig, deps?: {
|
|
436
434
|
prisma?: any;
|
|
@@ -440,17 +438,17 @@ export declare function hostedOptions(config: HostedConfig, deps?: {
|
|
|
440
438
|
* like any). A port that fails to build is logged and answers nothing: the eager promise always
|
|
441
439
|
* has a handler, so a failure never becomes an unhandled rejection (RV-B5).
|
|
442
440
|
*/
|
|
443
|
-
export declare function eagerKnowledge(build: (
|
|
441
|
+
export declare function eagerKnowledge(build: (agent: SessionAgent, scope: {
|
|
444
442
|
workspaceRef?: string;
|
|
445
|
-
}) => Promise<KnowledgePort | undefined>): (
|
|
443
|
+
}, usage?: UsageSink) => Promise<KnowledgePort | undefined>): (agent: SessionAgent, scope: {
|
|
446
444
|
workspaceRef?: string;
|
|
447
|
-
}) => KnowledgePort;
|
|
445
|
+
}, usage?: UsageSink) => KnowledgePort;
|
|
448
446
|
/**
|
|
449
|
-
* The app's
|
|
447
|
+
* The app's agent only when the app belongs to the workspace AppBridge signed for: one
|
|
450
448
|
* secret serves every Autopilot app, so a session is bound to its app and workspace
|
|
451
449
|
* (voice-session, "Connection requests are bound to the app").
|
|
452
450
|
*/
|
|
453
|
-
export declare function signedWorkspaceOnly(session: Pick<Session, "appRef" | "signedFor">, loaded:
|
|
451
|
+
export declare function signedWorkspaceOnly(session: Pick<Session, "appRef" | "signedFor">, loaded: LoadedAgent | undefined): LoadedAgent | undefined;
|
|
454
452
|
/** Starts the hosted Autopilot. */
|
|
455
453
|
export declare function startHostedAutopilot(config: HostedConfig): Promise<VoiceServer>;
|
|
456
454
|
export {};
|
package/dist/session/hosted.js
CHANGED
|
@@ -33,7 +33,7 @@ var __importStar = (this && this.__importStar) || (function () {
|
|
|
33
33
|
};
|
|
34
34
|
})();
|
|
35
35
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
-
exports.FileWorkspaceSecrets = exports.CellSecretCredentials = exports.DatabaseWorkspaceSecrets = exports.
|
|
36
|
+
exports.FileWorkspaceSecrets = exports.CellSecretCredentials = exports.DatabaseWorkspaceSecrets = exports.PrismaAgentSource = exports.FileAgentSource = exports.AUTOPILOT_CONFIG_DIRS = exports.hostedConfigSchema = void 0;
|
|
37
37
|
exports.findHostedConfig = findHostedConfig;
|
|
38
38
|
exports.loadHostedConfig = loadHostedConfig;
|
|
39
39
|
exports.s3Knowledge = s3Knowledge;
|
|
@@ -66,10 +66,11 @@ const logger_1 = require("@fonoster/logger");
|
|
|
66
66
|
const voice_1 = require("@fonoster/voice");
|
|
67
67
|
const yaml = __importStar(require("js-yaml"));
|
|
68
68
|
const zod_1 = require("zod");
|
|
69
|
+
const MeteredEmbeddings_1 = require("../knowledge/MeteredEmbeddings");
|
|
69
70
|
const createAutopilot_1 = require("./createAutopilot");
|
|
70
71
|
const knowledge_1 = require("./knowledge");
|
|
71
72
|
const llm_1 = require("./llm");
|
|
72
|
-
// The hosted Autopilot (rework-autopilot A2, task 4.3): an ordinary VoiceServer that
|
|
73
|
+
// The hosted Autopilot (rework-autopilot A2, task 4.3): an ordinary VoiceServer that AppBridge
|
|
73
74
|
// dials like any app. Everything it needs comes from one operator config file; no vendor key is
|
|
74
75
|
// read from Autopilot's environment by name (A8, A11), only through credential refs.
|
|
75
76
|
const logger = (0, logger_1.getLogger)({ service: "autopilot", filePath: __filename });
|
|
@@ -87,7 +88,7 @@ exports.hostedConfigSchema = zod_1.z
|
|
|
87
88
|
* FONOSTER_ALLOWED_DESTINATIONS.
|
|
88
89
|
*/
|
|
89
90
|
allowedDestinations: zod_1.z.array(zod_1.z.string()).optional(),
|
|
90
|
-
/** The secret
|
|
91
|
+
/** The secret AppBridge signs Autopilot sessions with (one for every Autopilot app). */
|
|
91
92
|
signingSecret: secretRef,
|
|
92
93
|
/**
|
|
93
94
|
* The URL Autopilot apps point to, exactly as the apps have it (the apiserver's
|
|
@@ -104,10 +105,10 @@ exports.hostedConfigSchema = zod_1.z
|
|
|
104
105
|
})
|
|
105
106
|
}),
|
|
106
107
|
/**
|
|
107
|
-
* Apps and their
|
|
108
|
+
* Apps and their agents: a file `{apps: {<appRef>: {workspaceRef?, agent}}}` (JSON or
|
|
108
109
|
* YAML), or `database`: the apps' Intelligence rows, cached for `cacheMs` (CP-B11).
|
|
109
110
|
*/
|
|
110
|
-
|
|
111
|
+
agents: zod_1.z.union([
|
|
111
112
|
zod_1.z.object({ file: zod_1.z.string() }).strict(),
|
|
112
113
|
zod_1.z
|
|
113
114
|
.object({
|
|
@@ -163,7 +164,7 @@ exports.hostedConfigSchema = zod_1.z
|
|
|
163
164
|
})
|
|
164
165
|
.strict()
|
|
165
166
|
.optional(),
|
|
166
|
-
/** Endpoint overrides by
|
|
167
|
+
/** Endpoint overrides by provider or adapter id (e.g. a local ollama); the catalog's otherwise. */
|
|
167
168
|
endpoints: zod_1.z.record(zod_1.z.string()).default({}),
|
|
168
169
|
/** The S3 + Unstructured knowledge bases; keys by credentialRef (task 4.1). */
|
|
169
170
|
knowledge: zod_1.z
|
|
@@ -187,8 +188,7 @@ exports.hostedConfigSchema = zod_1.z
|
|
|
187
188
|
.strict()
|
|
188
189
|
.optional(),
|
|
189
190
|
/** OTLP export (empty = off): `otlpEndpoint: "${OTEL_EXPORTER_OTLP_ENDPOINT:-}"`. */
|
|
190
|
-
telemetry: common_1.telemetrySchema
|
|
191
|
-
testMode: zod_1.z.boolean().default(false)
|
|
191
|
+
telemetry: common_1.telemetrySchema
|
|
192
192
|
})
|
|
193
193
|
.strict();
|
|
194
194
|
function readStructured(file) {
|
|
@@ -216,16 +216,12 @@ function loadHostedConfig(file, env = process.env) {
|
|
|
216
216
|
schema: exports.hostedConfigSchema,
|
|
217
217
|
argv: ["--config", file],
|
|
218
218
|
env,
|
|
219
|
-
interpolatePaths: ["*"]
|
|
220
|
-
// The URL no longer falls back to APISERVER_DATABASE_URL (fix-config-and-placement-gaps).
|
|
221
|
-
movedKeys: {
|
|
222
|
-
"database.urlEnv": 'database.url ("${APISERVER_DATABASE_URL}")'
|
|
223
|
-
}
|
|
219
|
+
interpolatePaths: ["*"]
|
|
224
220
|
});
|
|
225
221
|
const dir = path.dirname(file);
|
|
226
222
|
const rel = (p) => (path.isAbsolute(p) ? p : path.join(dir, p));
|
|
227
223
|
const withFile = (c) => "file" in c ? { ...c, file: rel(c.file) } : c;
|
|
228
|
-
const needsDb = !("file" in config.
|
|
224
|
+
const needsDb = !("file" in config.agents) ||
|
|
229
225
|
!("file" in config.registry) ||
|
|
230
226
|
(config.workspaceSecrets &&
|
|
231
227
|
"source" in config.workspaceSecrets &&
|
|
@@ -234,7 +230,7 @@ function loadHostedConfig(file, env = process.env) {
|
|
|
234
230
|
throw new Error("source: database needs a database section");
|
|
235
231
|
return {
|
|
236
232
|
...config,
|
|
237
|
-
|
|
233
|
+
agents: withFile(config.agents),
|
|
238
234
|
registry: withFile(config.registry),
|
|
239
235
|
...(config.workspaceSecrets
|
|
240
236
|
? { workspaceSecrets: withFile(config.workspaceSecrets) }
|
|
@@ -242,8 +238,8 @@ function loadHostedConfig(file, env = process.env) {
|
|
|
242
238
|
};
|
|
243
239
|
}
|
|
244
240
|
const resolveSecret = (s) => "value" in s && s.value !== undefined ? s.value : process.env[s.env ?? ""];
|
|
245
|
-
/** Apps and
|
|
246
|
-
class
|
|
241
|
+
/** Apps and agents from a file (a stand-in until agents come from the control plane). */
|
|
242
|
+
class FileAgentSource {
|
|
247
243
|
constructor(file) {
|
|
248
244
|
this.file = file;
|
|
249
245
|
}
|
|
@@ -251,16 +247,16 @@ class FileAssistantSource {
|
|
|
251
247
|
const data = readStructured(this.file);
|
|
252
248
|
const app = data.apps?.[appRef];
|
|
253
249
|
return app
|
|
254
|
-
? {
|
|
250
|
+
? { agent: app.agent, workspaceRef: app.workspaceRef }
|
|
255
251
|
: undefined;
|
|
256
252
|
}
|
|
257
253
|
}
|
|
258
|
-
exports.
|
|
254
|
+
exports.FileAgentSource = FileAgentSource;
|
|
259
255
|
/**
|
|
260
|
-
*
|
|
256
|
+
* Agents from the control plane: an Autopilot app's Intelligence row holds its agent
|
|
261
257
|
* (CP-B3). Each app is cached for `cacheMs` (misses too), so edits show up within seconds.
|
|
262
258
|
*/
|
|
263
|
-
class
|
|
259
|
+
class PrismaAgentSource {
|
|
264
260
|
constructor(
|
|
265
261
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
266
262
|
prisma, options = {}) {
|
|
@@ -278,13 +274,13 @@ class PrismaAssistantSource {
|
|
|
278
274
|
include: { intelligence: true }
|
|
279
275
|
});
|
|
280
276
|
const value = row && row.type === "AUTOPILOT" && row.intelligence?.config
|
|
281
|
-
? {
|
|
277
|
+
? { agent: row.intelligence.config, workspaceRef: row.accessKeyId }
|
|
282
278
|
: undefined;
|
|
283
279
|
this.cache.set(appRef, { at: now, value });
|
|
284
280
|
return value;
|
|
285
281
|
}
|
|
286
282
|
}
|
|
287
|
-
exports.
|
|
283
|
+
exports.PrismaAgentSource = PrismaAgentSource;
|
|
288
284
|
/** Workspace vendor keys from the database, looked up by the credentialRef's providers. */
|
|
289
285
|
class DatabaseWorkspaceSecrets {
|
|
290
286
|
constructor(keys, providers) {
|
|
@@ -323,13 +319,13 @@ class FileWorkspaceSecrets {
|
|
|
323
319
|
}
|
|
324
320
|
exports.FileWorkspaceSecrets = FileWorkspaceSecrets;
|
|
325
321
|
/**
|
|
326
|
-
* Builds the knowledge port for an
|
|
322
|
+
* Builds the knowledge port for an agent from the operator's S3 + Unstructured settings,
|
|
327
323
|
* with keys from the credential source (never Autopilot's environment). The bucket is the
|
|
328
324
|
* workspace's, as today. `make` is the knowledge base constructor (the S3 one by default).
|
|
329
325
|
*/
|
|
330
326
|
function s3Knowledge(config, credentials, make) {
|
|
331
|
-
return async (
|
|
332
|
-
const documents =
|
|
327
|
+
return async (agent, scope, usage) => {
|
|
328
|
+
const documents = agent.languageModel.knowledgeBase.map((d) => d.document);
|
|
333
329
|
if (!documents.length || !scope.workspaceRef)
|
|
334
330
|
return undefined;
|
|
335
331
|
const key = (ref) => credentials.get(ref, scope);
|
|
@@ -337,7 +333,7 @@ function s3Knowledge(config, credentials, make) {
|
|
|
337
333
|
key(config.s3.accessKeyIdRef),
|
|
338
334
|
key(config.s3.secretAccessKeyRef),
|
|
339
335
|
key(config.unstructured.apiKeyRef),
|
|
340
|
-
|
|
336
|
+
(0, llm_1.resolveCredential)(credentials, config.embeddings.apiKeyRef, scope)
|
|
341
337
|
]);
|
|
342
338
|
const params = {
|
|
343
339
|
bucket: scope.workspaceRef.toLowerCase(),
|
|
@@ -353,7 +349,9 @@ function s3Knowledge(config, credentials, make) {
|
|
|
353
349
|
},
|
|
354
350
|
unstructuredAPIURL: config.unstructured.url,
|
|
355
351
|
unstructuredAPIKey: unstructuredKey ?? "",
|
|
356
|
-
embeddingsApiKey: embeddingsKey ?? ""
|
|
352
|
+
embeddingsApiKey: embeddingsKey?.key ?? "",
|
|
353
|
+
embeddingsKeySource: embeddingsKey?.source ?? "platform",
|
|
354
|
+
usage
|
|
357
355
|
};
|
|
358
356
|
const kb = (make ?? defaultKnowledgeBase)(params);
|
|
359
357
|
kb.load().catch((e) => logger.warn(`knowledge base did not load: ${e.message}`));
|
|
@@ -365,11 +363,14 @@ function defaultKnowledgeBase(params) {
|
|
|
365
363
|
// port still uses.
|
|
366
364
|
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
|
367
365
|
const { S3KnowledgeBase } = require("../knowledge/S3KnowledgeBase");
|
|
368
|
-
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
|
369
|
-
const { OpenAIEmbeddings } = require("@langchain/openai");
|
|
370
366
|
return new S3KnowledgeBase({
|
|
371
367
|
...params,
|
|
372
|
-
|
|
368
|
+
// Metered: the vendor's token usage reaches the call (add-usage-metering).
|
|
369
|
+
embeddings: new MeteredEmbeddings_1.MeteredEmbeddings({
|
|
370
|
+
apiKey: String(params.embeddingsApiKey ?? ""),
|
|
371
|
+
keySource: params.embeddingsKeySource,
|
|
372
|
+
sink: params.usage
|
|
373
|
+
})
|
|
373
374
|
});
|
|
374
375
|
}
|
|
375
376
|
/** Opens the control-plane database named in the config (once). */
|
|
@@ -407,17 +408,11 @@ deps = {}) {
|
|
|
407
408
|
let prisma = deps.prisma;
|
|
408
409
|
const db = () => (prisma ?? (prisma = databaseOf(config)));
|
|
409
410
|
const store = registryStore(config, db);
|
|
410
|
-
const registry = new common_1.Registry.Registry({
|
|
411
|
-
store,
|
|
412
|
-
testMode: config.testMode
|
|
413
|
-
});
|
|
411
|
+
const registry = new common_1.Registry.Registry({ store });
|
|
414
412
|
const providers = () => store
|
|
415
413
|
.all()
|
|
416
|
-
.filter((e) => e.kind === "provider")
|
|
417
|
-
.map((e) => ({
|
|
418
|
-
id: e.id,
|
|
419
|
-
credentialRef: e.credentialRef
|
|
420
|
-
}));
|
|
414
|
+
.filter((e) => e.kind === "provider" && Boolean(e.credentialRef))
|
|
415
|
+
.map((e) => ({ id: e.id, credentialRef: e.credentialRef }));
|
|
421
416
|
const workspace = config.workspaceSecrets;
|
|
422
417
|
const credentials = new llm_1.LayeredCredentials([
|
|
423
418
|
...(workspace
|
|
@@ -431,16 +426,16 @@ deps = {}) {
|
|
|
431
426
|
: []),
|
|
432
427
|
new llm_1.ConfigCredentials(config.credentials)
|
|
433
428
|
]);
|
|
434
|
-
const
|
|
435
|
-
? new
|
|
436
|
-
: new
|
|
437
|
-
cacheMs: config.
|
|
429
|
+
const agents = "file" in config.agents
|
|
430
|
+
? new FileAgentSource(config.agents.file)
|
|
431
|
+
: new PrismaAgentSource(db(), {
|
|
432
|
+
cacheMs: config.agents.cacheMs
|
|
438
433
|
});
|
|
439
434
|
const knowledge = config.knowledge
|
|
440
435
|
? s3Knowledge(config.knowledge, credentials)
|
|
441
436
|
: undefined;
|
|
442
437
|
return {
|
|
443
|
-
load: async (session) => signedWorkspaceOnly(session, await
|
|
438
|
+
load: async (session) => signedWorkspaceOnly(session, await agents.get(session.appRef)),
|
|
444
439
|
allowedDestinations: config.allowedDestinations,
|
|
445
440
|
llm: new llm_1.RegistryLlmResolver({
|
|
446
441
|
registry,
|
|
@@ -456,9 +451,9 @@ deps = {}) {
|
|
|
456
451
|
* has a handler, so a failure never becomes an unhandled rejection (RV-B5).
|
|
457
452
|
*/
|
|
458
453
|
function eagerKnowledge(build) {
|
|
459
|
-
return (
|
|
454
|
+
return (agent, scope, usage) => {
|
|
460
455
|
const port = Promise.resolve()
|
|
461
|
-
.then(() => build(
|
|
456
|
+
.then(() => build(agent, scope, usage))
|
|
462
457
|
.catch((e) => {
|
|
463
458
|
logger.warn(`knowledge port unavailable: ${e?.message ?? e}`);
|
|
464
459
|
return undefined;
|
|
@@ -472,7 +467,7 @@ function eagerKnowledge(build) {
|
|
|
472
467
|
};
|
|
473
468
|
}
|
|
474
469
|
/**
|
|
475
|
-
* The app's
|
|
470
|
+
* The app's agent only when the app belongs to the workspace AppBridge signed for: one
|
|
476
471
|
* secret serves every Autopilot app, so a session is bound to its app and workspace
|
|
477
472
|
* (voice-session, "Connection requests are bound to the app").
|
|
478
473
|
*/
|
package/dist/session/index.d.ts
CHANGED
|
@@ -18,12 +18,12 @@
|
|
|
18
18
|
*/
|
|
19
19
|
export * from "./Agent";
|
|
20
20
|
export * from "./config";
|
|
21
|
-
export * from "./convertLegacyAssistant";
|
|
22
21
|
export * from "./createAutopilot";
|
|
23
22
|
export * from "./hosted";
|
|
24
23
|
export * from "./knowledge";
|
|
25
24
|
export * from "./llm";
|
|
26
25
|
export * from "./machine";
|
|
27
26
|
export * from "./sentences";
|
|
27
|
+
export * from "./telemetry";
|
|
28
28
|
export * from "./think";
|
|
29
29
|
export * from "./tools";
|
package/dist/session/index.js
CHANGED
|
@@ -34,12 +34,12 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
34
34
|
*/
|
|
35
35
|
__exportStar(require("./Agent"), exports);
|
|
36
36
|
__exportStar(require("./config"), exports);
|
|
37
|
-
__exportStar(require("./convertLegacyAssistant"), exports);
|
|
38
37
|
__exportStar(require("./createAutopilot"), exports);
|
|
39
38
|
__exportStar(require("./hosted"), exports);
|
|
40
39
|
__exportStar(require("./knowledge"), exports);
|
|
41
40
|
__exportStar(require("./llm"), exports);
|
|
42
41
|
__exportStar(require("./machine"), exports);
|
|
43
42
|
__exportStar(require("./sentences"), exports);
|
|
43
|
+
__exportStar(require("./telemetry"), exports);
|
|
44
44
|
__exportStar(require("./think"), exports);
|
|
45
45
|
__exportStar(require("./tools"), exports);
|
|
@@ -19,7 +19,7 @@
|
|
|
19
19
|
/**
|
|
20
20
|
* The knowledge port (rework-autopilot A8): one operation, find what is relevant to a question.
|
|
21
21
|
* Implementations (S3 + Unstructured today) are wired by the operator; their keys never live in
|
|
22
|
-
* the
|
|
22
|
+
* the agent config.
|
|
23
23
|
*/
|
|
24
24
|
export interface KnowledgePort {
|
|
25
25
|
find(question: string, signal: AbortSignal): Promise<string>;
|
package/dist/session/llm.d.ts
CHANGED
|
@@ -37,6 +37,20 @@ export declare class ConfigCredentials implements CredentialSource {
|
|
|
37
37
|
}>);
|
|
38
38
|
get(ref: string): string;
|
|
39
39
|
}
|
|
40
|
+
/**
|
|
41
|
+
* A workspace's own key could not be read (its store failed). It never falls back to the platform
|
|
42
|
+
* key (remove-platform-key-fallback): the session ends with reason key_unavailable.
|
|
43
|
+
*/
|
|
44
|
+
export declare class KeyUnavailableError extends Error {
|
|
45
|
+
readonly ref: string;
|
|
46
|
+
readonly reason = "key_unavailable";
|
|
47
|
+
constructor(ref: string, cause?: unknown);
|
|
48
|
+
}
|
|
49
|
+
/** A key and whose it is: the workspace's own (or its private region's), or the platform's. */
|
|
50
|
+
export type ResolvedCredential = {
|
|
51
|
+
key: string;
|
|
52
|
+
source: "workspace" | "platform";
|
|
53
|
+
};
|
|
40
54
|
/** Workspace Secrets (BYOK) in front of platform credentials. */
|
|
41
55
|
export declare class LayeredCredentials implements CredentialSource {
|
|
42
56
|
private readonly sources;
|
|
@@ -44,7 +58,18 @@ export declare class LayeredCredentials implements CredentialSource {
|
|
|
44
58
|
get(ref: string, scope: {
|
|
45
59
|
workspaceRef?: string;
|
|
46
60
|
}): Promise<string>;
|
|
61
|
+
/**
|
|
62
|
+
* The first key found and whose it is (add-usage-metering): the operator's config holds the
|
|
63
|
+
* platform keys; every other source holds the workspace's own.
|
|
64
|
+
*/
|
|
65
|
+
resolve(ref: string, scope: {
|
|
66
|
+
workspaceRef?: string;
|
|
67
|
+
}): Promise<ResolvedCredential | undefined>;
|
|
47
68
|
}
|
|
69
|
+
/** A credential and its source; a source that cannot tell is the platform's. */
|
|
70
|
+
export declare function resolveCredential(credentials: CredentialSource, ref: string, scope: {
|
|
71
|
+
workspaceRef?: string;
|
|
72
|
+
}): Promise<ResolvedCredential | undefined>;
|
|
48
73
|
/** Adapters whose LLMs speak OpenAI-compatible Chat Completions (moved to @fonoster/community/openai). */
|
|
49
74
|
export { OPENAI_COMPATIBLE };
|
|
50
75
|
export declare class LlmResolutionError extends Error {
|
|
@@ -53,6 +78,8 @@ export type ResolvedLlm = {
|
|
|
53
78
|
think: Think;
|
|
54
79
|
/** The id actually used (a retired id resolves to its replacement). */
|
|
55
80
|
id: string;
|
|
81
|
+
/** Whose key the model is called with; keyless providers count as the platform's. */
|
|
82
|
+
keySource: "workspace" | "platform";
|
|
56
83
|
/** For the call record: provider.deprecated_used / provider.retired_used (registry PR4). */
|
|
57
84
|
event?: {
|
|
58
85
|
type: string;
|
|
@@ -71,7 +98,7 @@ export interface LlmResolver {
|
|
|
71
98
|
export type RegistryLlmResolverOptions = {
|
|
72
99
|
registry: Registry.Registry;
|
|
73
100
|
credentials: CredentialSource;
|
|
74
|
-
/** Endpoint overrides by
|
|
101
|
+
/** Endpoint overrides by provider or adapter id (e.g. a local ollama); win over the catalog's. */
|
|
75
102
|
endpoints?: Record<string, string>;
|
|
76
103
|
};
|
|
77
104
|
export declare class RegistryLlmResolver implements LlmResolver {
|