@fonoster/autopilot 0.26.0 → 0.30.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 +14 -51
- package/dist/index.d.ts +0 -5
- package/dist/index.js +0 -5
- package/dist/knowledge/AbstractKnowledgeBase.d.ts +3 -2
- package/dist/knowledge/AbstractKnowledgeBase.js +2 -7
- package/dist/knowledge/FilesKnowledgeBase.d.ts +1 -1
- package/dist/knowledge/types.d.ts +1 -1
- package/dist/models/LanguageModelFactory.d.ts +5 -24
- package/dist/models/LanguageModelFactory.js +0 -1
- package/dist/models/createLanguageModel.d.ts +2 -2
- package/dist/models/evaluations/createEvalEffectiveConfig.d.ts +10 -106
- package/dist/models/evaluations/createEvalEffectiveConfig.js +47 -7
- package/dist/models/evaluations/createEvaluateIntelligence.d.ts +10 -20
- package/dist/models/evaluations/createEvaluateIntelligence.js +32 -23
- package/dist/models/evaluations/evalTestCases.d.ts +2 -2
- package/dist/models/evaluations/extractAiResponse.d.ts +2 -3
- package/dist/models/evaluations/index.d.ts +1 -0
- package/dist/models/evaluations/index.js +1 -0
- package/dist/models/evaluations/runEval.d.ts +2 -20
- package/dist/models/evaluations/session/EvalSession.d.ts +73 -0
- package/dist/models/evaluations/session/EvalSession.js +134 -0
- package/dist/models/evaluations/session/evaluateSessionScenario.d.ts +58 -0
- package/dist/models/evaluations/session/evaluateSessionScenario.js +182 -0
- package/dist/{vad → models/evaluations/session}/index.d.ts +3 -2
- package/dist/{vad → models/evaluations/session}/index.js +3 -2
- package/dist/models/evaluations/session/runSessionEval.d.ts +17 -0
- package/dist/models/evaluations/session/runSessionEval.js +59 -0
- package/dist/models/evaluations/types.d.ts +7 -6
- package/dist/models/types.d.ts +19 -1
- package/dist/session/Agent.d.ts +138 -0
- package/dist/session/Agent.js +474 -0
- package/dist/{loadAssistantFromAPI.d.ts → session/AsyncQueue.d.ts} +12 -7
- package/dist/session/AsyncQueue.js +67 -0
- package/dist/{machine/guards/index.d.ts → session/config.d.ts} +2 -2
- package/dist/session/config.js +27 -0
- package/dist/session/convertLegacyAssistant.d.ts +77 -0
- package/dist/session/convertLegacyAssistant.js +118 -0
- package/dist/session/createAutopilot.d.ts +47 -0
- package/dist/session/createAutopilot.js +119 -0
- package/dist/session/hosted.d.ts +456 -0
- package/dist/session/hosted.js +506 -0
- package/dist/{assistants → session}/index.d.ts +11 -2
- package/dist/{machine/actors → session}/index.js +11 -3
- package/dist/{machine/actions/appendSpeech.js → session/knowledge.d.ts} +14 -17
- package/dist/session/knowledge.js +10 -0
- package/dist/session/llm.d.ts +86 -0
- package/dist/session/llm.js +83 -0
- package/dist/session/machine.d.ts +206 -0
- package/dist/session/machine.js +126 -0
- package/dist/session/main.js +44 -0
- package/dist/session/sentences.d.ts +5 -0
- package/dist/session/sentences.js +53 -0
- package/dist/{machine/actors/index.d.ts → session/think.d.ts} +2 -3
- package/dist/{machine/actions/announceSystemError.js → session/think.js} +8 -10
- package/dist/session/tools.d.ts +22 -0
- package/dist/session/tools.js +83 -0
- package/dist/tools/builtin/transferToolDefinition.js +1 -1
- package/dist/voice/index.d.ts +0 -1
- package/dist/voice/index.js +0 -1
- package/dist/voice/types.d.ts +5 -18
- package/package.json +13 -14
- package/dist/Autopilot.d.ts +0 -14
- package/dist/Autopilot.js +0 -133
- package/dist/assistants/index.js +0 -36
- package/dist/assistants/loadAndValidateAssistant.d.ts +0 -3
- package/dist/assistants/loadAndValidateAssistant.js +0 -49
- package/dist/assistants/types.d.ts +0 -24
- package/dist/assistants/types.js +0 -2
- package/dist/envs.d.ts +0 -15
- package/dist/envs.js +0 -78
- package/dist/handleVoiceRequest.d.ts +0 -3
- package/dist/handleVoiceRequest.js +0 -167
- package/dist/loadAssistantConfigFromFile.d.ts +0 -3
- package/dist/loadAssistantConfigFromFile.js +0 -68
- package/dist/loadAssistantFromAPI.js +0 -98
- package/dist/machine/actions/announceSystemError.d.ts +0 -4
- package/dist/machine/actions/appendSpeech.d.ts +0 -1
- package/dist/machine/actions/cleanSpeech.d.ts +0 -1
- package/dist/machine/actions/cleanSpeech.js +0 -23
- package/dist/machine/actions/goodbye.d.ts +0 -4
- package/dist/machine/actions/goodbye.js +0 -31
- package/dist/machine/actions/increaseIdleTimeoutCount.d.ts +0 -1
- package/dist/machine/actions/increaseIdleTimeoutCount.js +0 -31
- package/dist/machine/actions/index.d.ts +0 -26
- package/dist/machine/actions/index.js +0 -37
- package/dist/machine/actions/interruptPlayback.d.ts +0 -4
- package/dist/machine/actions/interruptPlayback.js +0 -43
- package/dist/machine/actions/resetIdleTimeoutCount.d.ts +0 -1
- package/dist/machine/actions/resetIdleTimeoutCount.js +0 -31
- package/dist/machine/actions/resetState.d.ts +0 -1
- package/dist/machine/actions/resetState.js +0 -32
- package/dist/machine/actors/doAnnounceIdleTimeout.d.ts +0 -4
- package/dist/machine/actors/doAnnounceIdleTimeout.js +0 -41
- package/dist/machine/actors/doGreetUser.d.ts +0 -4
- package/dist/machine/actors/doGreetUser.js +0 -45
- package/dist/machine/actors/doProcessUserRequest.d.ts +0 -4
- package/dist/machine/actors/doProcessUserRequest.js +0 -75
- package/dist/machine/context.d.ts +0 -52
- package/dist/machine/context.js +0 -28
- package/dist/machine/delays.d.ts +0 -30
- package/dist/machine/delays.js +0 -37
- package/dist/machine/guards/hasSpeechResult.d.ts +0 -4
- package/dist/machine/guards/hasSpeechResult.js +0 -30
- package/dist/machine/guards/idleTimeoutCountExceedsMax.d.ts +0 -4
- package/dist/machine/guards/idleTimeoutCountExceedsMax.js +0 -31
- package/dist/machine/guards/index.js +0 -25
- package/dist/machine/index.d.ts +0 -19
- package/dist/machine/index.js +0 -23
- package/dist/machine/machine.d.ts +0 -408
- package/dist/machine/machine.js +0 -206
- package/dist/machine/setup.d.ts +0 -328
- package/dist/machine/setup.js +0 -116
- package/dist/machine/types.d.ts +0 -54
- package/dist/machine/types.js +0 -2
- package/dist/sendConversationEndedEvent.d.ts +0 -32
- package/dist/sendConversationEndedEvent.js +0 -66
- package/dist/sendConversationStartedEvent.d.ts +0 -6
- package/dist/sendConversationStartedEvent.js +0 -63
- package/dist/server.d.ts +0 -2
- package/dist/server.js +0 -25
- package/dist/types.d.ts +0 -40
- package/dist/types.js +0 -8
- package/dist/vad/SileroVad.d.ts +0 -18
- package/dist/vad/SileroVad.js +0 -40
- package/dist/vad/SileroVadModel.d.ts +0 -14
- package/dist/vad/SileroVadModel.js +0 -83
- package/dist/vad/chunkToFloat32Array.d.ts +0 -20
- package/dist/vad/chunkToFloat32Array.js +0 -34
- package/dist/vad/createVad.d.ts +0 -3
- package/dist/vad/createVad.js +0 -126
- package/dist/vad/types.d.ts +0 -60
- package/dist/vad/types.js +0 -2
- package/dist/vadWorker.js +0 -36
- package/dist/voice/Voice.d.ts +0 -61
- package/dist/voice/Voice.js +0 -96
- package/dist/voiceServerSetup.d.ts +0 -2
- package/dist/voiceServerSetup.js +0 -29
- /package/dist/{vadWorker.d.ts → session/main.d.ts} +0 -0
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.RegistryLlmResolver = exports.LlmResolutionError = exports.OPENAI_COMPATIBLE = exports.LayeredCredentials = exports.ConfigCredentials = void 0;
|
|
4
|
+
const community_1 = require("@fonoster/community");
|
|
5
|
+
const openai_1 = require("@fonoster/community/openai");
|
|
6
|
+
Object.defineProperty(exports, "OPENAI_COMPATIBLE", { enumerable: true, get: function () { return openai_1.OPENAI_COMPATIBLE; } });
|
|
7
|
+
/**
|
|
8
|
+
* Platform credentials from operator config: `{ "<credentialRef>": { env } | { value } }`. The
|
|
9
|
+
* variable names are operator choices; Autopilot never reads a vendor variable by itself.
|
|
10
|
+
*/
|
|
11
|
+
class ConfigCredentials {
|
|
12
|
+
constructor(refs) {
|
|
13
|
+
this.refs = refs;
|
|
14
|
+
}
|
|
15
|
+
get(ref) {
|
|
16
|
+
const c = this.refs[ref];
|
|
17
|
+
return c?.value ?? (c?.env ? process.env[c.env] : undefined);
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
exports.ConfigCredentials = ConfigCredentials;
|
|
21
|
+
/** Workspace Secrets (BYOK) in front of platform credentials. */
|
|
22
|
+
class LayeredCredentials {
|
|
23
|
+
constructor(sources) {
|
|
24
|
+
this.sources = sources;
|
|
25
|
+
}
|
|
26
|
+
async get(ref, scope) {
|
|
27
|
+
for (const s of this.sources) {
|
|
28
|
+
const v = await s.get(ref, scope);
|
|
29
|
+
if (v)
|
|
30
|
+
return v;
|
|
31
|
+
}
|
|
32
|
+
return undefined;
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
exports.LayeredCredentials = LayeredCredentials;
|
|
36
|
+
class LlmResolutionError extends Error {
|
|
37
|
+
}
|
|
38
|
+
exports.LlmResolutionError = LlmResolutionError;
|
|
39
|
+
class RegistryLlmResolver {
|
|
40
|
+
constructor(options) {
|
|
41
|
+
this.options = options;
|
|
42
|
+
}
|
|
43
|
+
async resolve(catalogId, settings, scope = {}) {
|
|
44
|
+
const { registry } = this.options;
|
|
45
|
+
const resolution = registry.resolve(catalogId);
|
|
46
|
+
const id = resolution.id;
|
|
47
|
+
const entries = registry.list();
|
|
48
|
+
const model = entries.find((e) => e.id === id);
|
|
49
|
+
if (!model || model.kind !== "model" || model.type !== "llm")
|
|
50
|
+
throw new LlmResolutionError(`${catalogId} is not a language model in the catalog`);
|
|
51
|
+
const providerId = id.slice(0, id.indexOf("/"));
|
|
52
|
+
const provider = entries.find((e) => e.id === providerId);
|
|
53
|
+
if (!provider || provider.kind !== "provider")
|
|
54
|
+
throw new LlmResolutionError(`no provider ${providerId} for ${id}`);
|
|
55
|
+
if (provider.state === "retired")
|
|
56
|
+
throw new LlmResolutionError(`provider ${providerId} is retired`);
|
|
57
|
+
const llm = (0, community_1.adapterFor)(provider.adapter)?.llm;
|
|
58
|
+
if (!llm)
|
|
59
|
+
throw new LlmResolutionError(`adapter ${provider.adapter} has no language model implementation in Autopilot yet`);
|
|
60
|
+
const baseUrl = this.options.endpoints?.[provider.id] ??
|
|
61
|
+
this.options.endpoints?.[provider.adapter] ??
|
|
62
|
+
llm.defaultEndpoint;
|
|
63
|
+
if (!baseUrl)
|
|
64
|
+
throw new LlmResolutionError(`no endpoint for provider ${provider.id} (adapter ${provider.adapter})`);
|
|
65
|
+
const apiKey = await this.options.credentials.get(provider.credentialRef, scope);
|
|
66
|
+
if (!apiKey && !(0, community_1.adapterFor)(provider.adapter)?.keyless)
|
|
67
|
+
throw new LlmResolutionError(`no credential ${provider.credentialRef} for provider ${provider.id}`);
|
|
68
|
+
return {
|
|
69
|
+
id,
|
|
70
|
+
think: llm.create({ baseUrl, apiKey }, id.slice(providerId.length + 1), settings),
|
|
71
|
+
...(resolution.event
|
|
72
|
+
? {
|
|
73
|
+
event: {
|
|
74
|
+
type: resolution.event,
|
|
75
|
+
id: catalogId,
|
|
76
|
+
replacement: resolution.replacement
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
: {})
|
|
80
|
+
};
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
exports.RegistryLlmResolver = RegistryLlmResolver;
|
|
@@ -0,0 +1,206 @@
|
|
|
1
|
+
import { Agent, AgentEvent } from "./Agent";
|
|
2
|
+
/**
|
|
3
|
+
* Autopilot's states (rework-autopilot A4). The machine decides; the Agent acts on the session.
|
|
4
|
+
* The idle clock runs only in listening.
|
|
5
|
+
*/
|
|
6
|
+
export declare const autopilotMachine: import("xstate").StateMachine<{
|
|
7
|
+
agent: Agent;
|
|
8
|
+
}, AgentEvent, {}, never, import("xstate").Values<{
|
|
9
|
+
transfer: {
|
|
10
|
+
type: "transfer";
|
|
11
|
+
params: unknown;
|
|
12
|
+
};
|
|
13
|
+
stop: {
|
|
14
|
+
type: "stop";
|
|
15
|
+
params: unknown;
|
|
16
|
+
};
|
|
17
|
+
prepare: {
|
|
18
|
+
type: "prepare";
|
|
19
|
+
params: unknown;
|
|
20
|
+
};
|
|
21
|
+
greet: {
|
|
22
|
+
type: "greet";
|
|
23
|
+
params: unknown;
|
|
24
|
+
};
|
|
25
|
+
armIdle: {
|
|
26
|
+
type: "armIdle";
|
|
27
|
+
params: unknown;
|
|
28
|
+
};
|
|
29
|
+
disarmIdle: {
|
|
30
|
+
type: "disarmIdle";
|
|
31
|
+
params: unknown;
|
|
32
|
+
};
|
|
33
|
+
sayIdle: {
|
|
34
|
+
type: "sayIdle";
|
|
35
|
+
params: unknown;
|
|
36
|
+
};
|
|
37
|
+
resumed: {
|
|
38
|
+
type: "resumed";
|
|
39
|
+
params: unknown;
|
|
40
|
+
};
|
|
41
|
+
answer: {
|
|
42
|
+
type: "answer";
|
|
43
|
+
params: unknown;
|
|
44
|
+
};
|
|
45
|
+
abortReply: {
|
|
46
|
+
type: "abortReply";
|
|
47
|
+
params: unknown;
|
|
48
|
+
};
|
|
49
|
+
clear: {
|
|
50
|
+
type: "clear";
|
|
51
|
+
params: unknown;
|
|
52
|
+
};
|
|
53
|
+
goodbye: {
|
|
54
|
+
type: "goodbye";
|
|
55
|
+
params: unknown;
|
|
56
|
+
};
|
|
57
|
+
}>, import("xstate").Values<{
|
|
58
|
+
spokenFrom: {
|
|
59
|
+
type: "spokenFrom";
|
|
60
|
+
params: string[];
|
|
61
|
+
};
|
|
62
|
+
bargeIn: {
|
|
63
|
+
type: "bargeIn";
|
|
64
|
+
params: unknown;
|
|
65
|
+
};
|
|
66
|
+
idleExhausted: {
|
|
67
|
+
type: "idleExhausted";
|
|
68
|
+
params: unknown;
|
|
69
|
+
};
|
|
70
|
+
}>, never, "greeting" | "listening" | "thinking" | "ending" | "speaking" | "transferring" | "ended", string, {
|
|
71
|
+
agent: Agent;
|
|
72
|
+
}, {}, import("xstate").EventObject, import("xstate").MetaObject, {
|
|
73
|
+
readonly id: "autopilot";
|
|
74
|
+
readonly initial: "greeting";
|
|
75
|
+
readonly context: ({ input }: {
|
|
76
|
+
spawn: {
|
|
77
|
+
<TSrc extends never>(logic: TSrc, ...[options]: never): import("xstate").ActorRefFromLogic<never>;
|
|
78
|
+
<TLogic extends import("xstate").AnyActorLogic>(src: TLogic, options?: {
|
|
79
|
+
id?: never;
|
|
80
|
+
systemId?: string;
|
|
81
|
+
input?: import("xstate").InputFrom<TLogic>;
|
|
82
|
+
syncSnapshot?: boolean;
|
|
83
|
+
}): import("xstate").ActorRefFromLogic<TLogic>;
|
|
84
|
+
};
|
|
85
|
+
input: {
|
|
86
|
+
agent: Agent;
|
|
87
|
+
};
|
|
88
|
+
self: import("xstate").ActorRef<import("xstate").MachineSnapshot<{
|
|
89
|
+
agent: Agent;
|
|
90
|
+
}, AgentEvent, Record<string, import("xstate").AnyActorRef>, import("xstate").StateValue, string, unknown, any, any>, AgentEvent, import("xstate").AnyEventObject>;
|
|
91
|
+
}) => {
|
|
92
|
+
agent: Agent;
|
|
93
|
+
};
|
|
94
|
+
readonly on: {
|
|
95
|
+
readonly SESSION_END: ".ended";
|
|
96
|
+
readonly MAX_DURATION: ".ending";
|
|
97
|
+
readonly HANGUP_REQUESTED: ".ending";
|
|
98
|
+
readonly TRANSFER_REQUESTED: ".transferring";
|
|
99
|
+
readonly TURN_BACKCHANNEL: {};
|
|
100
|
+
};
|
|
101
|
+
readonly states: {
|
|
102
|
+
readonly greeting: {
|
|
103
|
+
readonly entry: "greet";
|
|
104
|
+
readonly on: {
|
|
105
|
+
readonly SPOKEN: {
|
|
106
|
+
readonly guard: {
|
|
107
|
+
readonly type: "spokenFrom";
|
|
108
|
+
readonly params: ["greeting"];
|
|
109
|
+
};
|
|
110
|
+
readonly target: "listening";
|
|
111
|
+
};
|
|
112
|
+
readonly TURN_USER_STARTED: {
|
|
113
|
+
readonly guard: "bargeIn";
|
|
114
|
+
readonly actions: "clear";
|
|
115
|
+
};
|
|
116
|
+
readonly TURN_LIKELY_ENDED: {
|
|
117
|
+
readonly actions: "prepare";
|
|
118
|
+
};
|
|
119
|
+
readonly TURN_RESUMED: {
|
|
120
|
+
readonly actions: "resumed";
|
|
121
|
+
};
|
|
122
|
+
readonly TURN_ENDED: "thinking";
|
|
123
|
+
};
|
|
124
|
+
};
|
|
125
|
+
readonly listening: {
|
|
126
|
+
readonly entry: "armIdle";
|
|
127
|
+
readonly exit: "disarmIdle";
|
|
128
|
+
readonly on: {
|
|
129
|
+
readonly TURN_LIKELY_ENDED: {
|
|
130
|
+
readonly actions: "prepare";
|
|
131
|
+
};
|
|
132
|
+
readonly TURN_RESUMED: {
|
|
133
|
+
readonly actions: "resumed";
|
|
134
|
+
};
|
|
135
|
+
readonly TURN_ENDED: "thinking";
|
|
136
|
+
readonly IDLE: readonly [{
|
|
137
|
+
readonly guard: "idleExhausted";
|
|
138
|
+
readonly target: "ending";
|
|
139
|
+
}, {
|
|
140
|
+
readonly target: "speaking";
|
|
141
|
+
readonly actions: "sayIdle";
|
|
142
|
+
}];
|
|
143
|
+
};
|
|
144
|
+
};
|
|
145
|
+
readonly thinking: {
|
|
146
|
+
readonly entry: "answer";
|
|
147
|
+
readonly on: {
|
|
148
|
+
readonly REPLY_SPEAKING: "speaking";
|
|
149
|
+
readonly SPOKEN: {
|
|
150
|
+
readonly guard: {
|
|
151
|
+
readonly type: "spokenFrom";
|
|
152
|
+
readonly params: ["reply", "error"];
|
|
153
|
+
};
|
|
154
|
+
readonly target: "listening";
|
|
155
|
+
};
|
|
156
|
+
readonly TURN_USER_STARTED: {
|
|
157
|
+
readonly guard: "bargeIn";
|
|
158
|
+
readonly actions: "clear";
|
|
159
|
+
};
|
|
160
|
+
readonly TURN_ENDED: {
|
|
161
|
+
readonly target: "thinking";
|
|
162
|
+
readonly reenter: true;
|
|
163
|
+
readonly actions: "abortReply";
|
|
164
|
+
};
|
|
165
|
+
};
|
|
166
|
+
};
|
|
167
|
+
readonly speaking: {
|
|
168
|
+
readonly on: {
|
|
169
|
+
readonly SPOKEN: {
|
|
170
|
+
readonly guard: {
|
|
171
|
+
readonly type: "spokenFrom";
|
|
172
|
+
readonly params: ["reply", "idle", "error"];
|
|
173
|
+
};
|
|
174
|
+
readonly target: "listening";
|
|
175
|
+
};
|
|
176
|
+
readonly TURN_USER_STARTED: {
|
|
177
|
+
readonly guard: "bargeIn";
|
|
178
|
+
readonly actions: "clear";
|
|
179
|
+
};
|
|
180
|
+
readonly TURN_ENDED: {
|
|
181
|
+
readonly target: "thinking";
|
|
182
|
+
readonly actions: "abortReply";
|
|
183
|
+
};
|
|
184
|
+
};
|
|
185
|
+
};
|
|
186
|
+
readonly transferring: {
|
|
187
|
+
readonly entry: "transfer";
|
|
188
|
+
readonly on: {
|
|
189
|
+
readonly SPOKEN: {
|
|
190
|
+
readonly guard: {
|
|
191
|
+
readonly type: "spokenFrom";
|
|
192
|
+
readonly params: ["error"];
|
|
193
|
+
};
|
|
194
|
+
readonly target: "listening";
|
|
195
|
+
};
|
|
196
|
+
};
|
|
197
|
+
};
|
|
198
|
+
readonly ending: {
|
|
199
|
+
readonly entry: "goodbye";
|
|
200
|
+
};
|
|
201
|
+
readonly ended: {
|
|
202
|
+
readonly type: "final";
|
|
203
|
+
readonly entry: "stop";
|
|
204
|
+
};
|
|
205
|
+
};
|
|
206
|
+
}>;
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.autopilotMachine = void 0;
|
|
4
|
+
/**
|
|
5
|
+
* Copyright (C) 2025 by Fonoster Inc (https://fonoster.com)
|
|
6
|
+
* http://github.com/fonoster/fonoster
|
|
7
|
+
*
|
|
8
|
+
* This file is part of Fonoster
|
|
9
|
+
*
|
|
10
|
+
* Licensed under the MIT License (the "License");
|
|
11
|
+
* you may not use this file except in compliance with
|
|
12
|
+
* the License. You may obtain a copy of the License at
|
|
13
|
+
*
|
|
14
|
+
* https://opensource.org/licenses/MIT
|
|
15
|
+
*
|
|
16
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
17
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
18
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
19
|
+
* See the License for the specific language governing permissions and
|
|
20
|
+
* limitations under the License.
|
|
21
|
+
*/
|
|
22
|
+
const xstate_1 = require("xstate");
|
|
23
|
+
/**
|
|
24
|
+
* Autopilot's states (rework-autopilot A4). The machine decides; the Agent acts on the session.
|
|
25
|
+
* The idle clock runs only in listening.
|
|
26
|
+
*/
|
|
27
|
+
exports.autopilotMachine = (0, xstate_1.setup)({
|
|
28
|
+
types: {
|
|
29
|
+
context: {},
|
|
30
|
+
input: {},
|
|
31
|
+
events: {}
|
|
32
|
+
},
|
|
33
|
+
guards: {
|
|
34
|
+
spokenFrom: ({ event }, sources) => event.type === "SPOKEN" && sources.includes(event.source),
|
|
35
|
+
bargeIn: ({ context }) => context.agent.settings.allowUserBargeIn,
|
|
36
|
+
idleExhausted: ({ context }) => context.agent.idleExhausted()
|
|
37
|
+
},
|
|
38
|
+
actions: {
|
|
39
|
+
greet: ({ context }) => void context.agent.greet(),
|
|
40
|
+
armIdle: ({ context }) => context.agent.armIdle(),
|
|
41
|
+
disarmIdle: ({ context }) => context.agent.disarmIdle(),
|
|
42
|
+
sayIdle: ({ context }) => void context.agent.sayIdle(),
|
|
43
|
+
prepare: ({ context, event }) => context.agent.prepare(event.text),
|
|
44
|
+
resumed: ({ context }) => context.agent.resumed(),
|
|
45
|
+
answer: ({ context, event }) => void context.agent.answer(event.text),
|
|
46
|
+
abortReply: ({ context }) => context.agent.abortReply(),
|
|
47
|
+
clear: ({ context }) => void context.agent.clear(),
|
|
48
|
+
goodbye: ({ context }) => void context.agent.goodbye(),
|
|
49
|
+
transfer: ({ context }) => void context.agent.transfer(),
|
|
50
|
+
stop: ({ context }) => context.agent.stop()
|
|
51
|
+
}
|
|
52
|
+
}).createMachine({
|
|
53
|
+
id: "autopilot",
|
|
54
|
+
initial: "greeting",
|
|
55
|
+
context: ({ input }) => ({ agent: input.agent }),
|
|
56
|
+
on: {
|
|
57
|
+
SESSION_END: ".ended",
|
|
58
|
+
MAX_DURATION: ".ending",
|
|
59
|
+
HANGUP_REQUESTED: ".ending",
|
|
60
|
+
TRANSFER_REQUESTED: ".transferring",
|
|
61
|
+
// Backchannels are not interruptions: nothing happens in any state.
|
|
62
|
+
TURN_BACKCHANNEL: {}
|
|
63
|
+
},
|
|
64
|
+
states: {
|
|
65
|
+
greeting: {
|
|
66
|
+
entry: "greet",
|
|
67
|
+
on: {
|
|
68
|
+
SPOKEN: {
|
|
69
|
+
guard: { type: "spokenFrom", params: ["greeting"] },
|
|
70
|
+
target: "listening"
|
|
71
|
+
},
|
|
72
|
+
TURN_USER_STARTED: { guard: "bargeIn", actions: "clear" },
|
|
73
|
+
TURN_LIKELY_ENDED: { actions: "prepare" },
|
|
74
|
+
TURN_RESUMED: { actions: "resumed" },
|
|
75
|
+
TURN_ENDED: "thinking"
|
|
76
|
+
}
|
|
77
|
+
},
|
|
78
|
+
listening: {
|
|
79
|
+
entry: "armIdle",
|
|
80
|
+
exit: "disarmIdle",
|
|
81
|
+
on: {
|
|
82
|
+
TURN_LIKELY_ENDED: { actions: "prepare" },
|
|
83
|
+
TURN_RESUMED: { actions: "resumed" },
|
|
84
|
+
TURN_ENDED: "thinking",
|
|
85
|
+
IDLE: [
|
|
86
|
+
{ guard: "idleExhausted", target: "ending" },
|
|
87
|
+
{ target: "speaking", actions: "sayIdle" }
|
|
88
|
+
]
|
|
89
|
+
}
|
|
90
|
+
},
|
|
91
|
+
thinking: {
|
|
92
|
+
entry: "answer",
|
|
93
|
+
on: {
|
|
94
|
+
REPLY_SPEAKING: "speaking",
|
|
95
|
+
SPOKEN: {
|
|
96
|
+
guard: { type: "spokenFrom", params: ["reply", "error"] },
|
|
97
|
+
target: "listening"
|
|
98
|
+
},
|
|
99
|
+
TURN_USER_STARTED: { guard: "bargeIn", actions: "clear" },
|
|
100
|
+
// A new turn cancels the reply in flight; its user text stays in the history.
|
|
101
|
+
TURN_ENDED: { target: "thinking", reenter: true, actions: "abortReply" }
|
|
102
|
+
}
|
|
103
|
+
},
|
|
104
|
+
speaking: {
|
|
105
|
+
on: {
|
|
106
|
+
SPOKEN: {
|
|
107
|
+
guard: { type: "spokenFrom", params: ["reply", "idle", "error"] },
|
|
108
|
+
target: "listening"
|
|
109
|
+
},
|
|
110
|
+
TURN_USER_STARTED: { guard: "bargeIn", actions: "clear" },
|
|
111
|
+
TURN_ENDED: { target: "thinking", actions: "abortReply" }
|
|
112
|
+
}
|
|
113
|
+
},
|
|
114
|
+
transferring: {
|
|
115
|
+
entry: "transfer",
|
|
116
|
+
on: {
|
|
117
|
+
SPOKEN: {
|
|
118
|
+
guard: { type: "spokenFrom", params: ["error"] },
|
|
119
|
+
target: "listening"
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
},
|
|
123
|
+
ending: { entry: "goodbye" },
|
|
124
|
+
ended: { type: "final", entry: "stop" }
|
|
125
|
+
}
|
|
126
|
+
});
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
/**
|
|
4
|
+
* Copyright (C) 2025 by Fonoster Inc (https://fonoster.com)
|
|
5
|
+
* http://github.com/fonoster/fonoster
|
|
6
|
+
*
|
|
7
|
+
* This file is part of Fonoster
|
|
8
|
+
*
|
|
9
|
+
* Licensed under the MIT License (the "License");
|
|
10
|
+
* you may not use this file except in compliance with
|
|
11
|
+
* the License. You may obtain a copy of the License at
|
|
12
|
+
*
|
|
13
|
+
* https://opensource.org/licenses/MIT
|
|
14
|
+
*
|
|
15
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
16
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
17
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
18
|
+
* See the License for the specific language governing permissions and
|
|
19
|
+
* limitations under the License.
|
|
20
|
+
*/
|
|
21
|
+
// Autopilot's entry point (rework-autopilot task 4.3; the only engine since cleanbreak CB-B5):
|
|
22
|
+
//
|
|
23
|
+
// node dist/session/main.js --config /config/autopilot.yaml
|
|
24
|
+
const common_1 = require("@fonoster/common");
|
|
25
|
+
const logger_1 = require("@fonoster/logger");
|
|
26
|
+
const hosted_1 = require("./hosted");
|
|
27
|
+
const logger = (0, logger_1.getLogger)({ service: "autopilot", filePath: __filename });
|
|
28
|
+
(0, common_1.logUnhandledRejections)(logger);
|
|
29
|
+
// --config, AUTOPILOT_SESSION_CONFIG, else /config/autopilot.yaml (or an older location).
|
|
30
|
+
const at = process.argv.indexOf("--config");
|
|
31
|
+
const file = at >= 0
|
|
32
|
+
? process.argv[at + 1]
|
|
33
|
+
: (process.env.AUTOPILOT_SESSION_CONFIG ?? (0, hosted_1.findHostedConfig)());
|
|
34
|
+
if (!file) {
|
|
35
|
+
logger.error("no Autopilot config file: mount /config/autopilot.yaml or pass --config <file>");
|
|
36
|
+
process.exit(1);
|
|
37
|
+
}
|
|
38
|
+
const config = (0, hosted_1.loadHostedConfig)(file);
|
|
39
|
+
// Off unless an OTLP endpoint is configured (telemetry.otlpEndpoint or OTEL_EXPORTER_OTLP_*).
|
|
40
|
+
(0, common_1.initTelemetry)("fonoster-autopilot", undefined, (0, common_1.telemetryEnv)(config.telemetry));
|
|
41
|
+
(0, hosted_1.startHostedAutopilot)(config).catch((e) => {
|
|
42
|
+
logger.error(`hosted autopilot failed to start: ${e.message}`);
|
|
43
|
+
process.exit(1);
|
|
44
|
+
});
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Groups streamed tokens into sentence-sized pieces, so each speak piece is a natural unit for TTS
|
|
3
|
+
* while the first sentence still starts before the reply is complete (rework-autopilot A5).
|
|
4
|
+
*/
|
|
5
|
+
export declare function sentences(tokens: AsyncIterable<string>): AsyncIterable<string>;
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.sentences = sentences;
|
|
4
|
+
/**
|
|
5
|
+
* Copyright (C) 2025 by Fonoster Inc (https://fonoster.com)
|
|
6
|
+
* http://github.com/fonoster/fonoster
|
|
7
|
+
*
|
|
8
|
+
* This file is part of Fonoster
|
|
9
|
+
*
|
|
10
|
+
* Licensed under the MIT License (the "License");
|
|
11
|
+
* you may not use this file except in compliance with
|
|
12
|
+
* the License. You may obtain a copy of the License at
|
|
13
|
+
*
|
|
14
|
+
* https://opensource.org/licenses/MIT
|
|
15
|
+
*
|
|
16
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
17
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
18
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
19
|
+
* See the License for the specific language governing permissions and
|
|
20
|
+
* limitations under the License.
|
|
21
|
+
*/
|
|
22
|
+
const SENTENCE_END = /[.!?](["')\]]*)\s+/;
|
|
23
|
+
const MAX_PIECE = 160;
|
|
24
|
+
/**
|
|
25
|
+
* Groups streamed tokens into sentence-sized pieces, so each speak piece is a natural unit for TTS
|
|
26
|
+
* while the first sentence still starts before the reply is complete (rework-autopilot A5).
|
|
27
|
+
*/
|
|
28
|
+
async function* sentences(tokens) {
|
|
29
|
+
let buffer = "";
|
|
30
|
+
for await (const token of tokens) {
|
|
31
|
+
buffer += token;
|
|
32
|
+
for (;;) {
|
|
33
|
+
const m = SENTENCE_END.exec(buffer);
|
|
34
|
+
if (m) {
|
|
35
|
+
const end = m.index + m[0].length;
|
|
36
|
+
yield buffer.slice(0, end);
|
|
37
|
+
buffer = buffer.slice(end);
|
|
38
|
+
continue;
|
|
39
|
+
}
|
|
40
|
+
if (buffer.length > MAX_PIECE) {
|
|
41
|
+
const cut = buffer.lastIndexOf(" ", MAX_PIECE);
|
|
42
|
+
if (cut > 0) {
|
|
43
|
+
yield buffer.slice(0, cut + 1);
|
|
44
|
+
buffer = buffer.slice(cut + 1);
|
|
45
|
+
continue;
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
break;
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
if (buffer.trim())
|
|
52
|
+
yield buffer;
|
|
53
|
+
}
|
|
@@ -16,6 +16,5 @@
|
|
|
16
16
|
* See the License for the specific language governing permissions and
|
|
17
17
|
* limitations under the License.
|
|
18
18
|
*/
|
|
19
|
-
export
|
|
20
|
-
export
|
|
21
|
-
export * from "./doAnnounceIdleTimeout";
|
|
19
|
+
export { ChatMessage, Think, ThinkChunk, ThinkError, ThinkProvider, ToolCall, ToolSpec } from "@fonoster/community";
|
|
20
|
+
export { OpenAiCompatibleThink, resolveThink } from "@fonoster/community/openai";
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.
|
|
3
|
+
exports.resolveThink = exports.OpenAiCompatibleThink = exports.ThinkError = void 0;
|
|
4
4
|
/**
|
|
5
5
|
* Copyright (C) 2025 by Fonoster Inc (https://fonoster.com)
|
|
6
6
|
* http://github.com/fonoster/fonoster
|
|
@@ -19,12 +19,10 @@ exports.announceSystemError = void 0;
|
|
|
19
19
|
* See the License for the specific language governing permissions and
|
|
20
20
|
* limitations under the License.
|
|
21
21
|
*/
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
};
|
|
30
|
-
exports.announceSystemError = announceSystemError;
|
|
22
|
+
// The LLM adapter interface and the OpenAI-compatible adapter live in @fonoster/community with
|
|
23
|
+
// the other vendor adapters (registry 3.1).
|
|
24
|
+
var community_1 = require("@fonoster/community");
|
|
25
|
+
Object.defineProperty(exports, "ThinkError", { enumerable: true, get: function () { return community_1.ThinkError; } });
|
|
26
|
+
var openai_1 = require("@fonoster/community/openai");
|
|
27
|
+
Object.defineProperty(exports, "OpenAiCompatibleThink", { enumerable: true, get: function () { return openai_1.OpenAiCompatibleThink; } });
|
|
28
|
+
Object.defineProperty(exports, "resolveThink", { enumerable: true, get: function () { return openai_1.resolveThink; } });
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { DestinationPolicy } from "@fonoster/common";
|
|
2
|
+
import { ToolDefinition } from "./config";
|
|
3
|
+
export type ToolOutcome = {
|
|
4
|
+
ok: boolean;
|
|
5
|
+
result?: unknown;
|
|
6
|
+
error?: string;
|
|
7
|
+
};
|
|
8
|
+
/** The tool port (rework-autopilot A7): one operation, run a tool with inputs. */
|
|
9
|
+
export interface ToolPort {
|
|
10
|
+
run(name: string, args: Record<string, unknown>, signal: AbortSignal): Promise<ToolOutcome>;
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* HTTP tools from the assistant config (`operation.method`, `url`, `headers`). Tool URLs are
|
|
14
|
+
* tenant input: they go through safeFetch (public destinations only, redirects re-checked,
|
|
15
|
+
* 10 s timeout; RV-B2).
|
|
16
|
+
*/
|
|
17
|
+
export declare class HttpToolPort implements ToolPort {
|
|
18
|
+
private readonly tools;
|
|
19
|
+
private readonly policy?;
|
|
20
|
+
constructor(tools: ToolDefinition[], policy?: DestinationPolicy);
|
|
21
|
+
run(name: string, args: Record<string, unknown>, signal: AbortSignal): Promise<ToolOutcome>;
|
|
22
|
+
}
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.HttpToolPort = void 0;
|
|
4
|
+
/**
|
|
5
|
+
* Copyright (C) 2025 by Fonoster Inc (https://fonoster.com)
|
|
6
|
+
* http://github.com/fonoster/fonoster
|
|
7
|
+
*
|
|
8
|
+
* This file is part of Fonoster
|
|
9
|
+
*
|
|
10
|
+
* Licensed under the MIT License (the "License");
|
|
11
|
+
* you may not use this file except in compliance with
|
|
12
|
+
* the License. You may obtain a copy of the License at
|
|
13
|
+
*
|
|
14
|
+
* https://opensource.org/licenses/MIT
|
|
15
|
+
*
|
|
16
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
17
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
18
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
19
|
+
* See the License for the specific language governing permissions and
|
|
20
|
+
* limitations under the License.
|
|
21
|
+
*/
|
|
22
|
+
const url_1 = require("url");
|
|
23
|
+
const common_1 = require("@fonoster/common");
|
|
24
|
+
/**
|
|
25
|
+
* HTTP tools from the assistant config (`operation.method`, `url`, `headers`). Tool URLs are
|
|
26
|
+
* tenant input: they go through safeFetch (public destinations only, redirects re-checked,
|
|
27
|
+
* 10 s timeout; RV-B2).
|
|
28
|
+
*/
|
|
29
|
+
class HttpToolPort {
|
|
30
|
+
constructor(tools, policy) {
|
|
31
|
+
this.tools = tools;
|
|
32
|
+
this.policy = policy;
|
|
33
|
+
}
|
|
34
|
+
async run(name, args, signal) {
|
|
35
|
+
const tool = this.tools.find((t) => t.name === name);
|
|
36
|
+
if (!tool)
|
|
37
|
+
return { ok: false, error: `unknown tool ${name}` };
|
|
38
|
+
const method = String(tool.operation.method ?? "get").toUpperCase();
|
|
39
|
+
const url = new url_1.URL(tool.operation.url);
|
|
40
|
+
let body;
|
|
41
|
+
if (method === "GET") {
|
|
42
|
+
for (const [k, v] of Object.entries(args))
|
|
43
|
+
url.searchParams.set(k, String(v));
|
|
44
|
+
}
|
|
45
|
+
else {
|
|
46
|
+
body = JSON.stringify(args);
|
|
47
|
+
}
|
|
48
|
+
try {
|
|
49
|
+
const request = (0, common_1.safeFetch)(url, {
|
|
50
|
+
method,
|
|
51
|
+
headers: {
|
|
52
|
+
accept: "application/json",
|
|
53
|
+
"content-type": "application/json",
|
|
54
|
+
...(tool.operation.headers ?? {})
|
|
55
|
+
},
|
|
56
|
+
body,
|
|
57
|
+
signal,
|
|
58
|
+
policy: this.policy?.allow ? this.policy : undefined
|
|
59
|
+
});
|
|
60
|
+
if (tool.operation.waitForResponse === false) {
|
|
61
|
+
request.catch(() => undefined);
|
|
62
|
+
return { ok: true, result: { status: "sent" } };
|
|
63
|
+
}
|
|
64
|
+
const res = await request;
|
|
65
|
+
const text = await res.text();
|
|
66
|
+
if (!res.ok)
|
|
67
|
+
return {
|
|
68
|
+
ok: false,
|
|
69
|
+
error: `HTTP ${res.status}: ${text.slice(0, 200)}`
|
|
70
|
+
};
|
|
71
|
+
try {
|
|
72
|
+
return { ok: true, result: JSON.parse(text) };
|
|
73
|
+
}
|
|
74
|
+
catch {
|
|
75
|
+
return { ok: true, result: text };
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
catch (e) {
|
|
79
|
+
return { ok: false, error: e.message };
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
exports.HttpToolPort = HttpToolPort;
|
|
@@ -3,7 +3,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
exports.transferToolDefinition = void 0;
|
|
4
4
|
const transferToolDefinition = {
|
|
5
5
|
name: "transfer",
|
|
6
|
-
description: "Transfer the call to a
|
|
6
|
+
description: "Transfer the call to an operator (a person)",
|
|
7
7
|
parameters: {
|
|
8
8
|
type: "object",
|
|
9
9
|
properties: {
|