@fonoster/autopilot 0.25.0 → 0.26.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 +27 -0
- package/dist/Autopilot.d.ts +1 -0
- package/dist/Autopilot.js +21 -0
- package/dist/assistants/loadAndValidateAssistant.d.ts +1 -1
- package/dist/assistants/loadAndValidateAssistant.js +9 -6
- package/dist/handleVoiceRequest.js +1 -0
- package/dist/models/evaluations/createEvalEffectiveConfig.d.ts +4 -0
- package/dist/types.d.ts +2 -0
- package/dist/voice/Voice.d.ts +20 -0
- package/dist/voice/Voice.js +3 -0
- package/dist/voice/types.d.ts +2 -0
- package/package.json +7 -7
package/README.md
CHANGED
|
@@ -201,4 +201,31 @@ Please use the following environment variables to configure the Autopilot:
|
|
|
201
201
|
|
|
202
202
|
The Autopilot is still under heavy development. The next steps are to add support for Retrieval-Augmented Generation(RAG), improve the language model integration, and improve the Finite State Machine (FSM) that powers the conversations. If you have any suggestions or feedback, please let us know.
|
|
203
203
|
|
|
204
|
+
## Audio filters
|
|
205
|
+
|
|
206
|
+
Background voices reach speech recognition and the voice activity detector, and the assistant
|
|
207
|
+
answers them. An assistant can ask the Media Server to filter the caller's audio first, by adding
|
|
208
|
+
`audioFilters` to its configuration — the same block whether the assistant comes from a file or
|
|
209
|
+
from an Application:
|
|
210
|
+
|
|
211
|
+
```yaml
|
|
212
|
+
audioFilters:
|
|
213
|
+
- name: aiCoustics
|
|
214
|
+
options:
|
|
215
|
+
enhancementLevel: 0.8 # 0 to 1; higher also suppresses competing voices
|
|
216
|
+
|
|
217
|
+
conversationSettings:
|
|
218
|
+
firstMessage: "Hola, ¿en qué puedo ayudarte?"
|
|
219
|
+
# ...
|
|
220
|
+
```
|
|
221
|
+
|
|
222
|
+
For an Application, the same block goes inside `intelligence.config`, and is applied with:
|
|
223
|
+
|
|
224
|
+
```sh
|
|
225
|
+
fonoster applications:create -f app.yaml
|
|
226
|
+
```
|
|
204
227
|
|
|
228
|
+
Assistant files may be YAML or JSON. Filtering needs the optional `@ai-coustics/aic-sdk` package
|
|
229
|
+
and an `AIC_SDK_LICENSE` key on the apiserver; the key never appears in assistant configuration,
|
|
230
|
+
so these files stay safe to commit. If the filter cannot be applied, the call continues with
|
|
231
|
+
unfiltered audio and the reason is logged.
|
package/dist/Autopilot.d.ts
CHANGED
package/dist/Autopilot.js
CHANGED
|
@@ -41,6 +41,7 @@ class Autopilot {
|
|
|
41
41
|
});
|
|
42
42
|
}
|
|
43
43
|
async start() {
|
|
44
|
+
await this.setupAudioFilters();
|
|
44
45
|
const vadParams = this.params.conversationSettings.vad;
|
|
45
46
|
const sileroVad = new vad_1.SileroVad({
|
|
46
47
|
pathToModel: vadParams.pathToModel ||
|
|
@@ -63,6 +64,26 @@ class Autopilot {
|
|
|
63
64
|
logger.verbose("stopping autopilot");
|
|
64
65
|
this.actor.stop();
|
|
65
66
|
}
|
|
67
|
+
// Asked for before the audio stream is opened, so voice activity detection
|
|
68
|
+
// and speech recognition both see filtered audio from the first frame. A
|
|
69
|
+
// refusal is logged and the call continues unfiltered.
|
|
70
|
+
async setupAudioFilters() {
|
|
71
|
+
const { voice, audioFilters } = this.params;
|
|
72
|
+
if (!audioFilters?.length) {
|
|
73
|
+
return;
|
|
74
|
+
}
|
|
75
|
+
try {
|
|
76
|
+
await voice.setAudioFilters(audioFilters);
|
|
77
|
+
logger.verbose("audio filters requested", {
|
|
78
|
+
filters: audioFilters.map((filter) => filter.name)
|
|
79
|
+
});
|
|
80
|
+
}
|
|
81
|
+
catch (err) {
|
|
82
|
+
logger.warn("could not set audio filters; continuing unfiltered", {
|
|
83
|
+
error: err?.message
|
|
84
|
+
});
|
|
85
|
+
}
|
|
86
|
+
}
|
|
66
87
|
async setupVoiceStream() {
|
|
67
88
|
const { voice } = this.params;
|
|
68
89
|
const stream = await voice.stream();
|
|
@@ -23,22 +23,25 @@ exports.loadAndValidateAssistant = loadAndValidateAssistant;
|
|
|
23
23
|
* limitations under the License.
|
|
24
24
|
*/
|
|
25
25
|
const fs_1 = __importDefault(require("fs"));
|
|
26
|
+
const path_1 = __importDefault(require("path"));
|
|
26
27
|
const common_1 = require("@fonoster/common");
|
|
27
28
|
const logger_1 = require("@fonoster/logger");
|
|
29
|
+
const js_yaml_1 = require("js-yaml");
|
|
28
30
|
const logger = (0, logger_1.getLogger)({ service: "autopilot", filePath: __filename });
|
|
29
|
-
function loadAndValidateAssistant(
|
|
30
|
-
if (!fs_1.default.existsSync(
|
|
31
|
-
logger.error("assistant file not found", { path });
|
|
31
|
+
function loadAndValidateAssistant(assistantPath) {
|
|
32
|
+
if (!fs_1.default.existsSync(assistantPath)) {
|
|
33
|
+
logger.error("assistant file not found", { path: assistantPath });
|
|
32
34
|
process.exit(1);
|
|
33
35
|
}
|
|
34
36
|
try {
|
|
35
|
-
const fileContent = fs_1.default.readFileSync(
|
|
36
|
-
const
|
|
37
|
+
const fileContent = fs_1.default.readFileSync(assistantPath, "utf8");
|
|
38
|
+
const isYaml = [".yaml", ".yml"].includes(path_1.default.extname(assistantPath).toLowerCase());
|
|
39
|
+
const assistant = (isYaml ? (0, js_yaml_1.load)(fileContent) : JSON.parse(fileContent));
|
|
37
40
|
return common_1.assistantSchema.parse(assistant);
|
|
38
41
|
}
|
|
39
42
|
catch (e) {
|
|
40
43
|
logger.error("error parsing or validating assistant file", {
|
|
41
|
-
path,
|
|
44
|
+
path: assistantPath,
|
|
42
45
|
error: e
|
|
43
46
|
});
|
|
44
47
|
process.exit(1);
|
|
@@ -120,6 +120,7 @@ async function handleVoiceRequest(req, res) {
|
|
|
120
120
|
const { conversationSettings } = assistantConfig;
|
|
121
121
|
try {
|
|
122
122
|
const autopilot = new _1.default({
|
|
123
|
+
audioFilters: assistantConfig.audioFilters,
|
|
123
124
|
conversationSettings: conversationSettings,
|
|
124
125
|
voice: voice,
|
|
125
126
|
languageModel: languageModel
|
|
@@ -86,6 +86,10 @@ export declare function createEvalEffectiveConfig(config: AssistantConfig, crede
|
|
|
86
86
|
}[];
|
|
87
87
|
}[];
|
|
88
88
|
};
|
|
89
|
+
audioFilters?: {
|
|
90
|
+
options?: Record<string, unknown>;
|
|
91
|
+
name?: string;
|
|
92
|
+
}[];
|
|
89
93
|
conversationSettings?: {
|
|
90
94
|
firstMessage?: string;
|
|
91
95
|
systemPrompt?: string;
|
package/dist/types.d.ts
CHANGED
|
@@ -16,6 +16,7 @@
|
|
|
16
16
|
* See the License for the specific language governing permissions and
|
|
17
17
|
* limitations under the License.
|
|
18
18
|
*/
|
|
19
|
+
import { AudioFilterConfig } from "@fonoster/common";
|
|
19
20
|
import { Application } from "@fonoster/types";
|
|
20
21
|
import { AssistantConfig, ConversationSettings } from "./assistants";
|
|
21
22
|
import { LanguageModel } from "./models";
|
|
@@ -28,6 +29,7 @@ type AutopilotParams = {
|
|
|
28
29
|
voice: Voice;
|
|
29
30
|
conversationSettings: ConversationSettings;
|
|
30
31
|
languageModel: LanguageModel;
|
|
32
|
+
audioFilters?: AudioFilterConfig[];
|
|
31
33
|
};
|
|
32
34
|
type AutopilotApplication = Application & {
|
|
33
35
|
intelligence: {
|
package/dist/voice/Voice.d.ts
CHANGED
|
@@ -1,3 +1,22 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright (C) 2025 by Fonoster Inc (https://fonoster.com)
|
|
3
|
+
* http://github.com/fonoster/fonoster
|
|
4
|
+
*
|
|
5
|
+
* This file is part of Fonoster
|
|
6
|
+
*
|
|
7
|
+
* Licensed under the MIT License (the "License");
|
|
8
|
+
* you may not use this file except in compliance with
|
|
9
|
+
* the License. You may obtain a copy of the License at
|
|
10
|
+
*
|
|
11
|
+
* https://opensource.org/licenses/MIT
|
|
12
|
+
*
|
|
13
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
14
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
15
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
16
|
+
* See the License for the specific language governing permissions and
|
|
17
|
+
* limitations under the License.
|
|
18
|
+
*/
|
|
19
|
+
import { AudioFilterConfig } from "@fonoster/common";
|
|
1
20
|
import { VoiceResponse } from "@fonoster/voice";
|
|
2
21
|
import { Voice } from "./types";
|
|
3
22
|
declare class VoiceImpl implements Voice {
|
|
@@ -17,6 +36,7 @@ declare class VoiceImpl implements Voice {
|
|
|
17
36
|
};
|
|
18
37
|
constructor(mediaSessionRef: string, voice: VoiceResponse);
|
|
19
38
|
answer(): Promise<void>;
|
|
39
|
+
setAudioFilters(filters: AudioFilterConfig[]): Promise<void>;
|
|
20
40
|
hangup(): Promise<void>;
|
|
21
41
|
say(text: string): Promise<void>;
|
|
22
42
|
playDtmf(dtmf: string): Promise<void>;
|
package/dist/voice/Voice.js
CHANGED
package/dist/voice/types.d.ts
CHANGED
|
@@ -16,6 +16,7 @@
|
|
|
16
16
|
* See the License for the specific language governing permissions and
|
|
17
17
|
* limitations under the License.
|
|
18
18
|
*/
|
|
19
|
+
import { AudioFilterConfig } from "@fonoster/common";
|
|
19
20
|
type GatherStream = {
|
|
20
21
|
stop: () => Promise<void>;
|
|
21
22
|
onData: (cb: (payload: {
|
|
@@ -34,6 +35,7 @@ type TransferOptions = {
|
|
|
34
35
|
type Voice = {
|
|
35
36
|
mediaSessionRef: string;
|
|
36
37
|
answer: () => Promise<void>;
|
|
38
|
+
setAudioFilters: (filters: AudioFilterConfig[]) => Promise<void>;
|
|
37
39
|
hangup: () => Promise<void>;
|
|
38
40
|
say: (text: string) => Promise<void>;
|
|
39
41
|
playDtmf: (dtmf: string) => Promise<void>;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@fonoster/autopilot",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.26.0",
|
|
4
4
|
"description": "Voice AI for the Fonoster platform",
|
|
5
5
|
"author": "Pedro Sanders <psanders@fonoster.com>",
|
|
6
6
|
"homepage": "https://github.com/fonoster/fonoster#readme",
|
|
@@ -35,11 +35,11 @@
|
|
|
35
35
|
"dependencies": {
|
|
36
36
|
"@aws-sdk/client-s3": "^3.958.0",
|
|
37
37
|
"@dmitryrechkin/json-schema-to-zod": "^1.0.1",
|
|
38
|
-
"@fonoster/common": "^0.
|
|
39
|
-
"@fonoster/logger": "^0.
|
|
40
|
-
"@fonoster/sdk": "^0.
|
|
41
|
-
"@fonoster/types": "^0.
|
|
42
|
-
"@fonoster/voice": "^0.
|
|
38
|
+
"@fonoster/common": "^0.26.0",
|
|
39
|
+
"@fonoster/logger": "^0.26.0",
|
|
40
|
+
"@fonoster/sdk": "^0.26.0",
|
|
41
|
+
"@fonoster/types": "^0.26.0",
|
|
42
|
+
"@fonoster/voice": "^0.26.0",
|
|
43
43
|
"@langchain/anthropic": "^1.3.3",
|
|
44
44
|
"@langchain/community": "^1.1.1",
|
|
45
45
|
"@langchain/core": "^1.1.8",
|
|
@@ -59,5 +59,5 @@
|
|
|
59
59
|
"xstate": "^5.17.3",
|
|
60
60
|
"zod": "^3.25.76"
|
|
61
61
|
},
|
|
62
|
-
"gitHead": "
|
|
62
|
+
"gitHead": "d14f414d791a33f92e8c4225f7e462abc440a142"
|
|
63
63
|
}
|