@fonoster/autopilot 0.8.56 → 0.8.64

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.
@@ -64,9 +64,10 @@ const loadAssistantConfigFromFile_1 = require("./loadAssistantConfigFromFile");
64
64
  const _1 = __importStar(require("."));
65
65
  const loadAssistantFromAPI_1 = require("./loadAssistantFromAPI");
66
66
  const fs_1 = __importDefault(require("fs"));
67
+ const sendConversationEndedEvent_1 = require("./sendConversationEndedEvent");
67
68
  const logger = (0, logger_1.getLogger)({ service: "autopilot", filePath: __filename });
68
69
  async function handleVoiceRequest(req, res) {
69
- const { accessKeyId, ingressNumber, sessionRef, appRef, callDirection } = req;
70
+ const { accessKeyId, callerNumber, ingressNumber, sessionRef, appRef, callDirection } = req;
70
71
  logger.verbose("voice request", {
71
72
  accessKeyId,
72
73
  ingressNumber,
@@ -99,7 +100,6 @@ async function handleVoiceRequest(req, res) {
99
100
  logger.verbose("knowledge base loaded");
100
101
  });
101
102
  const voice = new _1.VoiceImpl(sessionRef, res);
102
- const { ingressNumber, callerNumber, callDirection } = req;
103
103
  const languageModel = (0, createLanguageModel_1.createLanguageModel)({
104
104
  voice,
105
105
  assistantConfig,
@@ -117,7 +117,21 @@ async function handleVoiceRequest(req, res) {
117
117
  languageModel
118
118
  });
119
119
  autopilot.start();
120
- res.on(common_1.StreamEvent.END, () => {
120
+ res.on(common_1.StreamEvent.END, async () => {
121
121
  autopilot.stop();
122
+ const rawChatHistory = await languageModel.getChatHistoryMessages();
123
+ const chatHistory = rawChatHistory
124
+ .filter((msg) => !msg.content?.toString().startsWith("tool result:")) // FIXME: Hardcoded filter
125
+ .map((msg) => {
126
+ if (msg.constructor.name === "HumanMessage") {
127
+ return { human: msg.content };
128
+ }
129
+ else if (msg.constructor.name === "AIMessage") {
130
+ return { ai: msg.content };
131
+ }
132
+ return null;
133
+ })
134
+ .filter(Boolean);
135
+ await (0, sendConversationEndedEvent_1.sendConversationEndedEvent)(assistantConfig.eventsHook, chatHistory);
122
136
  });
123
137
  }
@@ -7,5 +7,6 @@ declare abstract class AbstractLanguageModel implements LanguageModel {
7
7
  private readonly voice;
8
8
  constructor(params: LanguageModelParams, voice: Voice, telephonyContext: TelephonyContext);
9
9
  invoke(text: string): Promise<InvocationResult>;
10
+ getChatHistoryMessages(): Promise<import("@langchain/core/messages").BaseMessage[]>;
10
11
  }
11
12
  export { AbstractLanguageModel };
@@ -101,5 +101,8 @@ class AbstractLanguageModel {
101
101
  toolCalls: response.tool_calls
102
102
  };
103
103
  }
104
+ async getChatHistoryMessages() {
105
+ return this.chatHistory.getMessages();
106
+ }
104
107
  }
105
108
  exports.AbstractLanguageModel = AbstractLanguageModel;
@@ -1 +1 @@
1
- export declare const textSimilaryPrompt = "\nYou are a text similarity evaluator for a Voice Assistant application. \n\nGive Text1 and Text2, you use the following process to evaluate the similarity between the two texts:\n\n- Take the first text and determmine the intent of the text.\n- Take the second text and determine the intent of the text.\n- Compare the intents of the two texts ignoring the actual text content and the entities and the length of the text.\n\n## Example 1\n\nText1: \"You're welcome. Have a great day!\"\nText2: \"You're welcome [name]. Your appointment is confirmed. Goodbye!\"\n\nAnswer: true\n\n=== \n\nAre the intents of the two texts the same? Respond with true.\n";
1
+ export declare const textSimilaryPrompt = "\nYou are a text similarity evaluator for a Voice Assistant application. \n\nGive Text1 and Text2, you use the following process to evaluate the similarity between the two texts:\n\n- Take the first text and determmine the intent of the text.\n- Take the second text and determine the intent of the text.\n- Compare the intents of the two texts ignoring the actual text content, the entities, and length of the text.\n\n## Example 1\n\nText1: \"You're welcome. Have a great day!\"\nText2: \"You're welcome [name]. Your appointment is confirmed. Goodbye!\"\n\nAnswer: true\n\n=== \n\nAre the intents of the two texts the same? Respond with true.\n";
@@ -26,7 +26,7 @@ Give Text1 and Text2, you use the following process to evaluate the similarity b
26
26
 
27
27
  - Take the first text and determmine the intent of the text.
28
28
  - Take the second text and determine the intent of the text.
29
- - Compare the intents of the two texts ignoring the actual text content and the entities and the length of the text.
29
+ - Compare the intents of the two texts ignoring the actual text content, the entities, and length of the text.
30
30
 
31
31
  ## Example 1
32
32
 
@@ -0,0 +1,8 @@
1
+ import { EventsHookAllowedEvents } from "@fonoster/common";
2
+ type EventsHook = {
3
+ url: string;
4
+ events: EventsHookAllowedEvents[];
5
+ headers?: Record<string, string>;
6
+ };
7
+ export declare function sendConversationEndedEvent(eventsHook: EventsHook, chatHistory: Record<string, string>[]): Promise<void>;
8
+ export {};
@@ -0,0 +1,47 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.sendConversationEndedEvent = sendConversationEndedEvent;
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 common_1 = require("@fonoster/common");
23
+ const logger_1 = require("@fonoster/logger");
24
+ const logger = (0, logger_1.getLogger)({ service: "autopilot", filePath: __filename });
25
+ async function sendConversationEndedEvent(eventsHook, chatHistory) {
26
+ if (!eventsHook?.events.includes(common_1.EventsHookAllowedEvents.CONVERSATION_ENDED) &&
27
+ !eventsHook?.events.includes(common_1.EventsHookAllowedEvents.ALL)) {
28
+ return;
29
+ }
30
+ const parsedEventsHook = common_1.eventsHookSchema.parse(eventsHook);
31
+ const body = {
32
+ eventType: common_1.EventsHookAllowedEvents.CONVERSATION_ENDED,
33
+ chatHistory
34
+ };
35
+ try {
36
+ await (0, common_1.sendHttpRequest)({
37
+ url: parsedEventsHook.url,
38
+ method: common_1.AllowedHttpMethod.POST,
39
+ headers: parsedEventsHook.headers,
40
+ waitForResponse: false,
41
+ body
42
+ });
43
+ }
44
+ catch (e) {
45
+ logger.error("error sending event", e);
46
+ }
47
+ }
@@ -20,7 +20,7 @@ exports.ToolsCatalog = void 0;
20
20
  * limitations under the License.
21
21
  */
22
22
  const logger_1 = require("@fonoster/logger");
23
- const sendRequest_1 = require("./sendRequest");
23
+ const common_1 = require("@fonoster/common");
24
24
  const logger = (0, logger_1.getLogger)({ service: "autopilot", filePath: __filename });
25
25
  class ToolsCatalog {
26
26
  constructor(tools) {
@@ -35,11 +35,13 @@ class ToolsCatalog {
35
35
  if (!tool) {
36
36
  throw new Error(`Tool '${toolName}' not found in the catalog`);
37
37
  }
38
- return await (0, sendRequest_1.sendRequest)({
39
- method: tool.operation.type,
40
- url: tool.operation.url,
41
- waitForResponse: tool.operation.waitForResponse,
42
- headers: tool.operation.headers,
38
+ const parsedTool = common_1.toolSchema.parse(tool);
39
+ // FIXME: We shouldn't have to check the nullability of the operation
40
+ return await (0, common_1.sendHttpRequest)({
41
+ method: parsedTool.operation?.method,
42
+ url: parsedTool.operation?.url,
43
+ waitForResponse: parsedTool.operation?.waitForResponse,
44
+ headers: parsedTool.operation?.headers,
43
45
  body: args
44
46
  });
45
47
  }
@@ -1,3 +1,3 @@
1
- import { Tool } from "../type";
2
- declare const hangupToolDefinition: Tool;
1
+ import { BuiltInTool } from "../type";
2
+ declare const hangupToolDefinition: BuiltInTool;
3
3
  export { hangupToolDefinition };
@@ -1,25 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.hangupToolDefinition = 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 ToolSchema_1 = require("../ToolSchema");
23
4
  const hangupToolDefinition = {
24
5
  name: "hangup",
25
6
  description: "Hangup the call and end the conversation",
@@ -27,9 +8,6 @@ const hangupToolDefinition = {
27
8
  type: "object",
28
9
  properties: {},
29
10
  required: []
30
- },
31
- operation: {
32
- type: ToolSchema_1.AllowedOperations.BUILT_IN
33
11
  }
34
12
  };
35
13
  exports.hangupToolDefinition = hangupToolDefinition;
@@ -1,3 +1,3 @@
1
- import { Tool } from "../type";
2
- declare const transferToolDefinition: Tool;
1
+ import { BuiltInTool } from "../type";
2
+ declare const transferToolDefinition: BuiltInTool;
3
3
  export { transferToolDefinition };
@@ -1,25 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.transferToolDefinition = 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 ToolSchema_1 = require("../ToolSchema");
23
4
  const transferToolDefinition = {
24
5
  name: "transfer",
25
6
  description: "Transfer the call to a live agent",
@@ -27,9 +8,6 @@ const transferToolDefinition = {
27
8
  type: "object",
28
9
  properties: {},
29
10
  required: []
30
- },
31
- operation: {
32
- type: ToolSchema_1.AllowedOperations.BUILT_IN
33
11
  }
34
12
  };
35
13
  exports.transferToolDefinition = transferToolDefinition;
@@ -1,8 +1,9 @@
1
1
  import { z } from "zod";
2
- import { toolSchema } from "./ToolSchema";
2
+ import { toolSchema } from "@fonoster/common";
3
3
  type Tool = z.infer<typeof toolSchema>;
4
+ type BuiltInTool = Omit<Tool, "operation">;
4
5
  type OpenAITool = {
5
6
  type: "function";
6
7
  function: Omit<Tool, "operation">;
7
8
  };
8
- export { OpenAITool, Tool };
9
+ export { OpenAITool, Tool, BuiltInTool };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@fonoster/autopilot",
3
- "version": "0.8.56",
3
+ "version": "0.8.64",
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",
@@ -33,11 +33,11 @@
33
33
  },
34
34
  "dependencies": {
35
35
  "@aws-sdk/client-s3": "^3.712.0",
36
- "@fonoster/common": "^0.8.56",
37
- "@fonoster/logger": "^0.8.56",
38
- "@fonoster/sdk": "^0.8.56",
39
- "@fonoster/types": "^0.8.56",
40
- "@fonoster/voice": "^0.8.56",
36
+ "@fonoster/common": "^0.8.64",
37
+ "@fonoster/logger": "^0.8.59",
38
+ "@fonoster/sdk": "^0.8.64",
39
+ "@fonoster/types": "^0.8.59",
40
+ "@fonoster/voice": "^0.8.64",
41
41
  "@langchain/community": "^0.3.29",
42
42
  "@langchain/core": "^0.3.39",
43
43
  "@langchain/groq": "^0.1.3",
@@ -57,5 +57,5 @@
57
57
  "devDependencies": {
58
58
  "typescript": "^5.5.4"
59
59
  },
60
- "gitHead": "eb95cb72d964068fe3d525b696f17c4d4b0522a2"
60
+ "gitHead": "5f7a501c597926252e5fca8aa12818e2db70de16"
61
61
  }
@@ -1,117 +0,0 @@
1
- import { z } from "zod";
2
- declare enum AllowedOperations {
3
- GET = "get",
4
- POST = "post",
5
- BUILT_IN = "built-in"
6
- }
7
- declare const toolSchema: z.ZodObject<{
8
- name: z.ZodString;
9
- description: z.ZodString;
10
- parameters: z.ZodObject<{
11
- type: z.ZodEnum<["object", "array"]>;
12
- properties: z.ZodRecord<z.ZodString, z.ZodEffects<z.ZodObject<{
13
- type: z.ZodString;
14
- format: z.ZodOptional<z.ZodString>;
15
- pattern: z.ZodOptional<z.ZodString>;
16
- }, "strip", z.ZodTypeAny, {
17
- type: string;
18
- format?: string | undefined;
19
- pattern?: string | undefined;
20
- }, {
21
- type: string;
22
- format?: string | undefined;
23
- pattern?: string | undefined;
24
- }>, {
25
- type: string;
26
- format?: string | undefined;
27
- pattern?: string | undefined;
28
- }, {
29
- type: string;
30
- format?: string | undefined;
31
- pattern?: string | undefined;
32
- }>>;
33
- required: z.ZodOptional<z.ZodArray<z.ZodString, "many">>;
34
- }, "strip", z.ZodTypeAny, {
35
- type: "object" | "array";
36
- properties: Record<string, {
37
- type: string;
38
- format?: string | undefined;
39
- pattern?: string | undefined;
40
- }>;
41
- required?: string[] | undefined;
42
- }, {
43
- type: "object" | "array";
44
- properties: Record<string, {
45
- type: string;
46
- format?: string | undefined;
47
- pattern?: string | undefined;
48
- }>;
49
- required?: string[] | undefined;
50
- }>;
51
- requestStartMessage: z.ZodOptional<z.ZodString>;
52
- operation: z.ZodEffects<z.ZodObject<{
53
- type: z.ZodNativeEnum<typeof AllowedOperations>;
54
- url: z.ZodOptional<z.ZodString>;
55
- waitForResponse: z.ZodOptional<z.ZodBoolean>;
56
- headers: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodString>>;
57
- }, "strip", z.ZodTypeAny, {
58
- type: AllowedOperations;
59
- url?: string | undefined;
60
- waitForResponse?: boolean | undefined;
61
- headers?: Record<string, string> | undefined;
62
- }, {
63
- type: AllowedOperations;
64
- url?: string | undefined;
65
- waitForResponse?: boolean | undefined;
66
- headers?: Record<string, string> | undefined;
67
- }>, {
68
- type: AllowedOperations;
69
- url?: string | undefined;
70
- waitForResponse?: boolean | undefined;
71
- headers?: Record<string, string> | undefined;
72
- }, {
73
- type: AllowedOperations;
74
- url?: string | undefined;
75
- waitForResponse?: boolean | undefined;
76
- headers?: Record<string, string> | undefined;
77
- }>;
78
- }, "strip", z.ZodTypeAny, {
79
- name: string;
80
- description: string;
81
- parameters: {
82
- type: "object" | "array";
83
- properties: Record<string, {
84
- type: string;
85
- format?: string | undefined;
86
- pattern?: string | undefined;
87
- }>;
88
- required?: string[] | undefined;
89
- };
90
- operation: {
91
- type: AllowedOperations;
92
- url?: string | undefined;
93
- waitForResponse?: boolean | undefined;
94
- headers?: Record<string, string> | undefined;
95
- };
96
- requestStartMessage?: string | undefined;
97
- }, {
98
- name: string;
99
- description: string;
100
- parameters: {
101
- type: "object" | "array";
102
- properties: Record<string, {
103
- type: string;
104
- format?: string | undefined;
105
- pattern?: string | undefined;
106
- }>;
107
- required?: string[] | undefined;
108
- };
109
- operation: {
110
- type: AllowedOperations;
111
- url?: string | undefined;
112
- waitForResponse?: boolean | undefined;
113
- headers?: Record<string, string> | undefined;
114
- };
115
- requestStartMessage?: string | undefined;
116
- }>;
117
- export { AllowedOperations, toolSchema };
@@ -1,69 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.toolSchema = exports.AllowedOperations = 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 common_1 = require("@fonoster/common");
23
- const zod_1 = require("zod");
24
- var AllowedOperations;
25
- (function (AllowedOperations) {
26
- AllowedOperations["GET"] = "get";
27
- AllowedOperations["POST"] = "post";
28
- AllowedOperations["BUILT_IN"] = "built-in";
29
- })(AllowedOperations || (exports.AllowedOperations = AllowedOperations = {}));
30
- const propertySchema = zod_1.z
31
- .object({
32
- type: zod_1.z.string(),
33
- format: zod_1.z.string().optional(),
34
- pattern: zod_1.z.string().optional()
35
- })
36
- .refine((data) => {
37
- return !("format" in data && "pattern" in data);
38
- }, {
39
- message: "Property can only have either 'format' or 'pattern', not both."
40
- });
41
- const toolSchema = zod_1.z.object({
42
- name: zod_1.z.string(),
43
- description: zod_1.z.string(),
44
- parameters: zod_1.z.object({
45
- type: zod_1.z.enum(["object", "array"]),
46
- properties: zod_1.z.record(propertySchema),
47
- required: zod_1.z.array(zod_1.z.string()).optional()
48
- }),
49
- requestStartMessage: zod_1.z.string().optional(),
50
- operation: zod_1.z
51
- .object({
52
- type: zod_1.z.nativeEnum(AllowedOperations, {
53
- message: "Invalid operation type"
54
- }),
55
- // Make url required if operation type is not built-in
56
- url: zod_1.z.string().url({ message: common_1.Messages.VALID_URL }).optional(),
57
- waitForResponse: zod_1.z.boolean().optional(),
58
- headers: zod_1.z.record(zod_1.z.string()).optional()
59
- })
60
- .superRefine(({ url, type }, ctx) => {
61
- if (type !== AllowedOperations.BUILT_IN && !url) {
62
- ctx.addIssue({
63
- code: zod_1.z.ZodIssueCode.custom,
64
- message: "Url is required for non built-in operations."
65
- });
66
- }
67
- })
68
- });
69
- exports.toolSchema = toolSchema;
@@ -1,11 +0,0 @@
1
- import { AllowedOperations } from "./ToolSchema";
2
- declare function sendRequest(input: {
3
- method: AllowedOperations;
4
- url: string;
5
- waitForResponse: boolean;
6
- headers?: Record<string, string>;
7
- body?: Record<string, unknown>;
8
- }): Promise<{
9
- result: string;
10
- }>;
11
- export { sendRequest };
@@ -1,54 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.sendRequest = sendRequest;
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 logger_1 = require("@fonoster/logger");
23
- const zod_1 = require("zod");
24
- const ToolSchema_1 = require("./ToolSchema");
25
- const responseSchema = zod_1.z.object({
26
- result: zod_1.z.string()
27
- });
28
- const logger = (0, logger_1.getLogger)({ service: "autopilot", filePath: __filename });
29
- async function sendRequest(input) {
30
- const { url, method, body, headers, waitForResponse } = input;
31
- const options = {
32
- method,
33
- headers: {
34
- "Content-Type": "application/json",
35
- ...headers
36
- },
37
- body: method === ToolSchema_1.AllowedOperations.POST ? JSON.stringify(body) : undefined
38
- };
39
- logger.verbose(`sending request to ${url}`, { body, method });
40
- if (waitForResponse && method === ToolSchema_1.AllowedOperations.POST) {
41
- setTimeout(() => fetch(url, options), 0);
42
- return { result: "request sent" };
43
- }
44
- else {
45
- const response = await fetch(url, options);
46
- const data = await response.json();
47
- try {
48
- return responseSchema.parse(data);
49
- }
50
- catch (error) {
51
- throw new Error(`Invalid response: expected ${JSON.stringify(responseSchema, null, 2)}, got ${JSON.stringify(data, null, 2)}`);
52
- }
53
- }
54
- }