@fonoster/autopilot 0.12.9 → 0.12.11

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.
@@ -25,13 +25,14 @@ const logger = (0, logger_1.getLogger)({ service: "autopilot", filePath: __filen
25
25
  exports.doProcessUserRequest = (0, xstate_1.fromPromise)(async ({ input }) => {
26
26
  const { context } = input;
27
27
  logger.verbose("called processUserRequest actor", {
28
- speechBuffer: context.speechBuffer
28
+ speechBuffer: context.speechBuffer,
29
+ isReentry: context.isReentry
29
30
  });
30
31
  // Stop any speech that might be playing
31
32
  await context.voice.stopSpeech();
32
33
  const languageModel = context.languageModel;
33
34
  const speech = context.speechBuffer.trim();
34
- const response = await languageModel.invoke(speech);
35
+ const response = await languageModel.invoke(speech, context.isReentry);
35
36
  try {
36
37
  if (response.type === "say" && !response.content) {
37
38
  logger.warn("ignoring say response with no content");
@@ -45,5 +45,7 @@ declare const context: ({ input }: {
45
45
  sessionStartTime: number;
46
46
  maxSessionDuration: number;
47
47
  initialDtmf: string;
48
+ previousState: any;
49
+ isReentry: boolean;
48
50
  };
49
51
  export { context };
@@ -20,6 +20,8 @@ const context = ({ input }) => ({
20
20
  allowUserBargeIn: input.conversationSettings.allowUserBargeIn,
21
21
  sessionStartTime: Date.now(),
22
22
  maxSessionDuration: input.conversationSettings.maxSessionDuration,
23
- initialDtmf: input.conversationSettings.initialDtmf
23
+ initialDtmf: input.conversationSettings.initialDtmf,
24
+ previousState: null,
25
+ isReentry: false
24
26
  });
25
27
  exports.context = context;
@@ -63,7 +63,7 @@ declare const machine: import("xstate").StateMachine<any, import("./types").Auto
63
63
  languageModel: import("..").LanguageModel;
64
64
  voice: import("..").Voice;
65
65
  }, {}, import("xstate").EventObject, import("xstate").MetaObject, {
66
- /** @xstate-layout N4IgpgJg5mDOIC5QDMB2BBAkgYgB6wBcBDAsAOiOVICcAKAWXQA0B9AZQFE23MB5AORYARAKoAldABU+-AJTY0WANoAGALqJQABwD2sAJYF9O1JpC5EAWgDMADgDsZFQDYATAFYAnLevuAjPYALIF+ADQgAJ6IHoFkrp5B9s6ezn6pzu7uAL5Z4YqYZFDUYGBGqFDYqhpIILoGRiZmFgh+tn5k1vYp9tauti7Obc7hUQievmQJzm5efq6uvvY5eRgF+hAANmDYbAAKHBwAwgAS7JLoYpJVZnWGxqY1zZ4qjrbuC-Z9gT3O9vYjiHstlckxcnXsKlaXUCrmWIHyZHWWzwhBI5EoNFomCEABkOCxpPQOLwRJJ5AikWBrjVbg0HqBmu5AipJu42t5PM8VNYMgCWtY-O05p4-F5XMyVIF3NY4RTNtt8MRSBQqGA6Ni8QTMESSWSFKtEfKlH5qto9HdGo9ENZArYyAF-P5fra+q4+YKFmQgv13EF-DypbKDRt9IQwKh9OVJDoRLA1Tt9kdTmIuCIcVd1DdzXSmohnLayLZnNZPIFi5zkkE+TyWZz7Ez4ipJX5mTLcvDg6HSBGozG49QUUr0ar1bj8YTiaTyZ2wz2oNHY2rqWb6vdcy1BXa-r5gYFnh56+6VMCvX4VF5uf7Pm2VlgyAB3Ih3coAMR01DYWhKAGMABaSfQAFswB0ABXAgEwOE4zguDNTVqbM1ytFoIQ6ZsgU5BZXC6YZIkQLx3ELIJXBUV0-kCIM70fZ8oDfD8vzAP8AOAsCIL2KDk1TdNlwQ1dLQZKwpUmOxviSFxmUFdw+Q+JxvilWw90WB1KIKaiylo99Px-f8gJA8DBzRFVMUYVh2KTFgAHUsEkLUdSnfUqKfdS6K0xidJY8CeNpJCBI3Wwtw+NJxl+YE3TwhBxRBbxbG8dxpji7xWhUsgtGoHRvzgAxykXagxDAABHUC4AgiATHISMADcdAAa3IBFUvSzLIygHK8sK4qEEq9KSHuKovMQ-jzEQOZ3g6QY-HGc9xjaPkLzIZk+m3ToQiCZKGoy2Aspa-s2qKwhIPMthzkufq+PpIb+R5e1rDsYtRR6SVPD5DCyCdYJnEhSFkgo9t6rSjattagq9rYxNoJTNg0zgrMzvXAIFK9CFBlLcTj2rPd7WvTpOm8Y9SxydtUB0CA4DMfIYYtc7mksQIJhcDxvF8AJgjCcKW08V65Old4eWdG8OzvIoSnUimc2QyxfjiSsSJLEii0GPlPFcZx5r+JXJW5etfGSylRZ8i7fRBVI0niVJgghaxFeV1WuhI1steyX6Z27ZqF37PXBuaBwWXZaxzwFYtMkCWbUkLfpjx5aa2WStTmpchimN01iPap4bJdSIs+g+5Wy2D8KSw5z4QmV8Z7DPX5HdvApfyIcpQK0FP10sEjCLcAURVIxIwtGPwbvaXxDdtOKEp+quUv+prsp24Hisb5DRWlMbBSBDJSJu2aHDDpsFJ5aUlaWAmgA */
66
+ /** @xstate-layout N4IgpgJg5mDOIC5QDMB2BBAkgYgFQG0AGAXUVAAcB7WASwBcbLUyQAPRAWgFYBmARgB0ADi58A7CMIA2HgBYuATj4AaEAE9EPAEwDZYsQqljZQnoT18pUgL7XVaLNlaw6AQzpgBr5B4BOACgBZdAANAH0AZQBRCIjMAHkAOTCAEQBVACV0ABUExIBKbAdMIlIkECpaBiYWdgQOHiExAWktRVNRY1kVdUQ22QEtBWMxKUNLSy4uW3sMTAEoXzAwBlQobFKWSvpGZnK6viFBHgMjbSFpKSOpVQ0EBR4uAWGrNqUtLUexGZBigRoIAAbMDYCIABSiUQAwgAJSLZdAZbKbcrbap7UB1BSEZoiT5iLRCPQ8IxiW6ICQ6bEyfSEQ4GWRaH5-AHApwudyebx+fyYFIAGSiYVygSi8TS2UKLKBYBRFGoOxq+0QXFkhGeXCOCiECmxhBJXHJCD4-EEfCGfEUWjV5l4zLm-xl7LcHi8PjAAT5guFmFF4slRQdrNlfDK8qqu1qmhMAnEolERhMhK0Rr45p4AmMFy4xlEJPk9qwAkBNBcYFQNDW2UoaVgHtBEOhcIyMTS-ORJC2CvRUYQUhjQhkClkQ8Mw1kRpJ6t1YlVQ0I5j4ap4hfmJbLFarNbrvmdnLdPK9QpFYolUod648m6g1drHrlFW7keVxrTQkzYkeQmt2Las9ThDfpmfCEIo+p5gSK52L8DoAO6uDsawAGKUL4ETkMsADGAAW2Q0AAtmAlAAK50A2kKwvCiIdmGj4RkqmKIOI6pmN0Ei6p8WinEaihPE0jKEMm+iyKuAjwYhUAoWhGFgDheGESRZHghRzatu2D5os+jH1PIzyNHoozSGqaaGr0CD4i0ejyESDw5nGoniaskmoehWG4QRRGkXurrch6QShJEjaUQA6lg2Q+n6Z6BkWjmVs50lufJnl0BpT4MWwTGHO+kFiJYDxGN+KZmdaVJCDqIhWFwYxHEIonkL4lCYXAtBrHevgZGAACOxFwGREBMJ4lYAG6UAA1p4fz1Y1zVxW1HXdb1CDDY17i7KUqX0RiGXGm0OgklqZiKI0PR3GBuiCU0n4nGxInQZNDVNbALVQHNXU9S45FNlRSIbYqW0HDwJKxoDg78DmrEKEa7ECAmsgjnSdJjLdsxFlNj3Pa9C0fcpX0thEbY0V2m29uIRIftIOpqv2gGTrICixpBJwnNqgHDrY0GoJQEBwCwxRE39vYcLIjwtFIbwdOIcMnUxdMw1ZVxDDqXD6qJizLE5-M9i+HBGIMYwEvqChaIBVhCEaRtSLo+hG+Y+qzo8onBprWnbTmOhSGmYvjHDYj6ubWiWwZNvLvb0x3RepZXnFt47s76V1E06oHaB-AyFME5mVVghlQuphjI0moOQhTlSa5snuQppFx-9TG6x7g6ElIgn9v2k66pm1rmvnuXSLOonYa4azEeQ1eC8bTxi-wSiCSMRV3Ca-ACI8bsmFVVVs+HqMPTNrU7vN710KPL6WrwS8e6TUjK58PA8U0wgXIBwuXzwRvfOzQA */
67
67
  readonly context: ({ input }: {
68
68
  input: {
69
69
  voice: import("..").Voice;
@@ -90,6 +90,8 @@ declare const machine: import("xstate").StateMachine<any, import("./types").Auto
90
90
  sessionStartTime: number;
91
91
  maxSessionDuration: number;
92
92
  initialDtmf: string;
93
+ previousState: any;
94
+ isReentry: boolean;
93
95
  };
94
96
  readonly id: "fnAI";
95
97
  readonly initial: "greeting";
@@ -208,7 +210,17 @@ declare const machine: import("xstate").StateMachine<any, import("./types").Auto
208
210
  readonly type: "interruptPlayback";
209
211
  }, {
210
212
  readonly type: "appendSpeech";
211
- }];
213
+ }, import("xstate").ActionFunction<any, {
214
+ type: "SPEECH_RESULT";
215
+ speech: string;
216
+ responseTime: number;
217
+ }, import("./types").AutopilotEvents, undefined, {
218
+ src: "doProcessUserRequest";
219
+ logic: import("xstate").PromiseActorLogic<void, {
220
+ context: import("./types").AutopilotContext;
221
+ }, import("xstate").EventObject>;
222
+ id: string;
223
+ }, never, never, never, never>];
212
224
  readonly reenter: true;
213
225
  };
214
226
  };
@@ -20,10 +20,11 @@ exports.machine = void 0;
20
20
  * limitations under the License.
21
21
  */
22
22
  const xstate_1 = require("xstate");
23
+ const xstate_2 = require("xstate");
23
24
  const context_1 = require("./context");
24
25
  const setup_1 = require("./setup");
25
26
  const machine = setup_1.machineSetup.createMachine({
26
- /** @xstate-layout N4IgpgJg5mDOIC5QDMB2BBAkgYgB6wBcBDAsAOiOVICcAKAWXQA0B9AZQFE23MB5AORYARAKoAldABU+-AJTY0WANoAGALqJQABwD2sAJYF9O1JpC5EAWgDMADgDsZFQDYATAFYAnLevuAjPYALIF+ADQgAJ6IHoFkrp5B9s6ezn6pzu7uAL5Z4YqYZFDUYGBGqFDYqhpIILoGRiZmFgh+tn5k1vYp9tauti7Obc7hUQievmQJzm5efq6uvvY5eRgF+hAANmDYbAAKHBwAwgAS7JLoYpJVZnWGxqY1zZ4qjrbuC-Z9gT3O9vYjiHstlckxcnXsKlaXUCrmWIHyZHWWzwhBI5EoNFomCEABkOCxpPQOLwRJJ5AikWBrjVbg0HqBmu5AipJu42t5PM8VNYMgCWtY-O05p4-F5XMyVIF3NY4RTNtt8MRSBQqGA6Ni8QTMESSWSFKtEfKlH5qto9HdGo9ENZArYyAF-P5fra+q4+YKFmQgv13EF-DypbKDRt9IQwKh9OVJDoRLA1Tt9kdTmIuCIcVd1DdzXSmohnLayLZnNZPIFi5zkkE+TyWZz7Ez4ipJX5mTLcvDg6HSBGozG49QUUr0ar1bj8YTiaTyZ2wz2oNHY2rqWb6vdcy1BXa-r5gYFnh56+6VMCvX4VF5uf7Pm2VlgyAB3Ih3coAMR01DYWhKAGMABaSfQAFswB0ABXAgEwOE4zguDNTVqbM1ytFoIQ6ZsgU5BZXC6YZIkQLx3ELIJXBUV0-kCIM70fZ8oDfD8vzAP8AOAsCIL2KDk1TdNlwQ1dLQZKwpUmOxviSFxmUFdw+Q+JxvilWw90WB1KIKaiylo99Px-f8gJA8DBzRFVMUYVh2KTFgAHUsEkLUdSnfUqKfdS6K0xidJY8CeNpJCBI3Wwtw+NJxl+YE3TwhBxRBbxbG8dxpji7xWhUsgtGoHRvzgAxykXagxDAABHUC4AgiATHISMADcdAAa3IBFUvSzLIygHK8sK4qEEq9KSHuKovMQ-jzEQOZ3g6QY-HGc9xjaPkLzIZk+m3ToQiCZKGoy2Aspa-s2qKwhIPMthzkufq+PpIb+R5e1rDsYtRR6SVPD5DCyCdYJnEhSFkgo9t6rSjattagq9rYxNoJTNg0zgrMzvXAIFK9CFBlLcTj2rPd7WvTpOm8Y9SxydtUB0CA4DMfIYYtc7mksQIJhcDxvF8AJgjCcKW08V65Old4eWdG8OzvIoSnUimc2QyxfjiSsSJLEii0GPlPFcZx5r+JXJW5etfGSylRZ8i7fRBVI0niVJgghaxFeV1WuhI1steyX6Z27ZqF37PXBuaBwWXZaxzwFYtMkCWbUkLfpjx5aa2WStTmpchimN01iPap4bJdSIs+g+5Wy2D8KSw5z4QmV8Z7DPX5HdvApfyIcpQK0FP10sEjCLcAURVIxIwtGPwbvaXxDdtOKEp+quUv+prsp24Hisb5DRWlMbBSBDJSJu2aHDDpsFJ5aUlaWAmgA */
27
+ /** @xstate-layout N4IgpgJg5mDOIC5QDMB2BBAkgYgFQG0AGAXUVAAcB7WASwBcbLUyQAPRAWgFYBmARgB0ADi58A7CMIA2HgBYuATj4AaEAE9EPAEwDZYsQqljZQnoT18pUgL7XVaLNlaw6AQzpgBr5B4BOACgBZdAANAH0AZQBRCIjMAHkAOTCAEQBVACV0ABUExIBKbAdMIlIkECpaBiYWdgQOHiExAWktRVNRY1kVdUQ22QEtBWMxKUNLSy4uW3sMTAEoXzAwBlQobFKWSvpGZnK6viFBHgMjbSFpKSOpVQ0EBR4uAWGrNqUtLUexGZBigRoIAAbMDYCIABSiUQAwgAJSLZdAZbKbcrbap7UB1BSEZoiT5iLRCPQ8IxiW6ICQ6bEyfSEQ4GWRaH5-AHApwudyebx+fyYFIAGSiYVygSi8TS2UKLKBYBRFGoOxq+0QXFkhGeXCOCiECmxhBJXHJCD4-EEfCGfEUWjV5l4zLm-xl7LcHi8PjAAT5guFmFF4slRQdrNlfDK8qqu1qmhMAnEolERhMhK0Rr45p4AmMFy4xlEJPk9qwAkBNBcYFQNDW2UoaVgHtBEOhcIyMTS-ORJC2CvRUYQUhjQhkClkQ8Mw1kRpJ6t1YlVQ0I5j4ap4hfmJbLFarNbrvmdnLdPK9QpFYolUod648m6g1drHrlFW7keVxrTQkzYkeQmt2Las9ThDfpmfCEIo+p5gSK52L8DoAO6uDsawAGKUL4ETkMsADGAAW2Q0AAtmAlAAK50A2kKwvCiIdmGj4RkqmKIOI6pmN0Ei6p8WinEaihPE0jKEMm+iyKuAjwYhUAoWhGFgDheGESRZHghRzatu2D5os+jH1PIzyNHoozSGqaaGr0CD4i0ejyESDw5nGoniaskmoehWG4QRRGkXurrch6QShJEjaUQA6lg2Q+n6Z6BkWjmVs50lufJnl0BpT4MWwTGHO+kFiJYDxGN+KZmdaVJCDqIhWFwYxHEIonkL4lCYXAtBrHevgZGAACOxFwGREBMJ4lYAG6UAA1p4fz1Y1zVxW1HXdb1CDDY17i7KUqX0RiGXGm0OgklqZiKI0PR3GBuiCU0n4nGxInQZNDVNbALVQHNXU9S45FNlRSIbYqW0HDwJKxoDg78DmrEKEa7ECAmsgjnSdJjLdsxFlNj3Pa9C0fcpX0thEbY0V2m29uIRIftIOpqv2gGTrICixpBJwnNqgHDrY0GoJQEBwCwxRE39vYcLIjwtFIbwdOIcMnUxdMw1ZVxDDqXD6qJizLE5-M9i+HBGIMYwEvqChaIBVhCEaRtSLo+hG+Y+qzo8onBprWnbTmOhSGmYvjHDYj6ubWiWwZNvLvb0x3RepZXnFt47s76V1E06oHaB-AyFME5mVVghlQuphjI0moOQhTlSa5snuQppFx-9TG6x7g6ElIgn9v2k66pm1rmvnuXSLOonYa4azEeQ1eC8bTxi-wSiCSMRV3Ca-ACI8bsmFVVVs+HqMPTNrU7vN710KPL6WrwS8e6TUjK58PA8U0wgXIBwuXzwRvfOzQA */
27
28
  context: context_1.context,
28
29
  id: "fnAI",
29
30
  initial: "greeting",
@@ -122,10 +123,19 @@ const machine = setup_1.machineSetup.createMachine({
122
123
  actions: [{ type: "cleanSpeech" }]
123
124
  },
124
125
  SPEECH_RESULT: {
125
- // This makes sure result that
126
126
  target: "processingUserRequest",
127
127
  description: "Append speech and go back to listening.",
128
- actions: [{ type: "interruptPlayback" }, { type: "appendSpeech" }],
128
+ actions: [
129
+ { type: "interruptPlayback" },
130
+ { type: "appendSpeech" },
131
+ (0, xstate_2.assign)(({ context, self }) => {
132
+ const isReentry = self.getSnapshot().value === context.previousState;
133
+ return {
134
+ previousState: self.getSnapshot().value,
135
+ isReentry
136
+ };
137
+ })
138
+ ],
129
139
  reenter: true
130
140
  }
131
141
  },
@@ -37,6 +37,8 @@ type AutopilotContext = {
37
37
  speechResponseTime: number;
38
38
  knowledgeBaseSourceUrl?: string;
39
39
  initialDtmf?: string;
40
+ previousState: string | null;
41
+ isReentry: boolean;
40
42
  };
41
43
  type AutopilotEvents = {
42
44
  type: "SPEECH_START";
@@ -9,7 +9,7 @@ declare abstract class AbstractLanguageModel implements LanguageModel {
9
9
  private readonly firstMessage;
10
10
  private readonly transferOptions;
11
11
  constructor(params: LanguageModelParams, voice: Voice, telephonyContext: TelephonyContext);
12
- invoke(text: string): Promise<InvocationResult>;
12
+ invoke(text: string, isReentry: boolean): Promise<InvocationResult>;
13
13
  getChatHistoryMessages(): Promise<import("@langchain/core/messages").BaseMessage[]>;
14
14
  }
15
15
  export { AbstractLanguageModel };
@@ -41,7 +41,7 @@ class AbstractLanguageModel {
41
41
  });
42
42
  this.chain = (0, createChain_1.createChain)(model, knowledgeBase, promptTemplate, this.chatHistory);
43
43
  }
44
- async invoke(text) {
44
+ async invoke(text, isReentry) {
45
45
  const { chain, chatHistory, toolsCatalog } = this;
46
46
  const response = (await chain.invoke({ text }));
47
47
  let isFirstTool = true;
@@ -51,6 +51,13 @@ class AbstractLanguageModel {
51
51
  hasTools: (response.tool_calls?.length ?? 0) > 0,
52
52
  tools: response.tool_calls?.map((tool) => tool.name)
53
53
  });
54
+ // This handles late speech recognition
55
+ if (isReentry) {
56
+ logger.verbose("xxx reentry detected, discarding last conversation turn");
57
+ const messages = await chatHistory.getMessages();
58
+ messages.pop(); // Last AI message
59
+ messages.pop(); // Last user message
60
+ }
54
61
  // Begin the conversation with the first message
55
62
  if ((await chatHistory.getMessages()).length === 0 && this.firstMessage) {
56
63
  await chatHistory.addAIMessage(this.firstMessage);
@@ -22,7 +22,7 @@ import { ToolCall } from "@langchain/core/messages/tool";
22
22
  import { KnowledgeBase } from "../knowledge";
23
23
  import { Tool } from "../tools/types";
24
24
  type LanguageModel = {
25
- invoke: (text: string) => Promise<InvocationResult>;
25
+ invoke: (text: string, isReentry?: boolean) => Promise<InvocationResult>;
26
26
  };
27
27
  type BaseModelParams = {
28
28
  firstMessage?: string;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@fonoster/autopilot",
3
- "version": "0.12.9",
3
+ "version": "0.12.11",
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",
@@ -59,5 +59,5 @@
59
59
  "xstate": "^5.17.3",
60
60
  "zod": "^3.23.8"
61
61
  },
62
- "gitHead": "082170cc2626a2c21ce1b2d657203aef88e7f043"
62
+ "gitHead": "e17be36f4b161521bb87df754975f33a8a7b8aa9"
63
63
  }