@sprucelabs/sprucebot-llm 5.0.689 → 6.0.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.
@@ -1,6 +1,6 @@
1
1
  import { AbstractEventEmitter } from '@sprucelabs/mercury-event-emitter';
2
2
  import { Schema, SchemaValues } from '@sprucelabs/schema';
3
- import { BotOptions, LlmAdapter, LlmEventContract, LlmMessage, SerializedBot, SprucebotLlmBot, SprucebotLLmSkill } from '../llm.types';
3
+ import { BotOptions, LlmAdapter, LlmEventContract, LlmMessage, MessageResponseCallback, SerializedBot, SprucebotLlmBot, SprucebotLLmSkill } from '../llm.types';
4
4
  export default class SprucebotLlmBotImpl<StateSchema extends Schema = Schema, State extends SchemaValues<StateSchema> = SchemaValues<StateSchema>> extends AbstractEventEmitter<LlmEventContract> implements SprucebotLlmBot<StateSchema, State> {
5
5
  static messageMemoryLimit: number;
6
6
  protected adapter: LlmAdapter;
@@ -15,7 +15,7 @@ export default class SprucebotLlmBotImpl<StateSchema extends Schema = Schema, St
15
15
  markAsDone(): void;
16
16
  getIsDone(): boolean;
17
17
  serialize(): SerializedBot<StateSchema, State>;
18
- sendMessage(message: string): Promise<string>;
18
+ sendMessage(message: string, cb?: MessageResponseCallback): Promise<string>;
19
19
  private trackMessage;
20
20
  updateState(newState: Partial<State>): Promise<void>;
21
21
  setSkill(skill: SprucebotLLmSkill<any>): void;
@@ -43,7 +43,7 @@ class SprucebotLlmBotImpl extends mercury_event_emitter_1.AbstractEventEmitter {
43
43
  skill,
44
44
  };
45
45
  }
46
- async sendMessage(message) {
46
+ async sendMessage(message, cb) {
47
47
  (0, schema_1.assertOptions)({ message }, ['message']);
48
48
  this.trackMessage({
49
49
  from: 'Me',
@@ -55,7 +55,7 @@ class SprucebotLlmBotImpl extends mercury_event_emitter_1.AbstractEventEmitter {
55
55
  promptTemplate: serializedSkill?.promptTemplate,
56
56
  });
57
57
  const parser = ResponseParser_1.default.getInstance();
58
- const { isDone, message: parsedResponse, state, } = await parser.parse(response, serializedSkill?.callbacks);
58
+ const { isDone, message: parsedResponse, state, callbackResults, } = await parser.parse(response, serializedSkill?.callbacks);
59
59
  this.isDone = isDone;
60
60
  if (this.stateSchema && state) {
61
61
  await this.updateState(state);
@@ -67,6 +67,10 @@ class SprucebotLlmBotImpl extends mercury_event_emitter_1.AbstractEventEmitter {
67
67
  from: 'You',
68
68
  message: parsedResponse,
69
69
  });
70
+ cb?.(parsedResponse);
71
+ if (callbackResults) {
72
+ await this.sendMessage(`API Results: ${callbackResults}`, cb);
73
+ }
70
74
  return parsedResponse;
71
75
  }
72
76
  trackMessage(m) {
@@ -19,8 +19,9 @@ class OpenAiAdapter {
19
19
  messages,
20
20
  model: options?.model ?? 'gpt-4o',
21
21
  });
22
- return (response.choices?.[0]?.message?.content?.trim() ??
23
- exports.MESSAGE_RESPONSE_ERROR_MESSAGE);
22
+ const message = response.choices?.[0]?.message?.content?.trim() ??
23
+ exports.MESSAGE_RESPONSE_ERROR_MESSAGE;
24
+ return message;
24
25
  }
25
26
  }
26
27
  exports.OpenAiAdapter = OpenAiAdapter;
@@ -78,7 +78,7 @@ class OpenAiMessageBuilder {
78
78
  const api = `<APIReference>\n\n${descriptions.join('\n\n')}</APIReference>`;
79
79
  return {
80
80
  role: 'system',
81
- content: `You have an API available to you to lookup answers. To use it, respond with a message in handlebars format like this: {{ FunctionName }} and I'll respond with the response of the api call in my next message. If the api call has parameters, call it like this: {{ FunctionName parameter1="value1" parameter2="value2" }}. It'll work the same way. After I respond with the api's response and you can use it going forward. The api is as follows (in xml format):\n\n${api}`,
81
+ content: `You have an API available to you to lookup answers. When you need the response of the function call to proceed, you can call a function using a custom markup we created that looks like this: << FunctionName />>. The API will respond with the results and then you can continue the conversation with your new knowledge. If the api call has parameters, call it like this: << FunctionName >>parameters json encoded<</ FunctionName >>. Make sure to json encode the data and drop it between the function tags. I will respond with the api's response and you can use it going forward. The api is as follows (in xml format):\n\n${api}`,
82
82
  };
83
83
  }
84
84
  buildPleaseKeepInMindMessage(pleaseKeepInMindThat) {
package/build/chat.js CHANGED
@@ -61,13 +61,12 @@ void (async () => {
61
61
  };
62
62
  const bot = bots.Bot({
63
63
  adapter,
64
- skill: skills.profile,
64
+ skill: skills.callbacks,
65
65
  youAre: "a bot named Sprucebot that is in test mode. At the start of every conversation, you introduce yourself and announce that you are in test mode so I don't get confused! You are both hip and adorable. You say things like, 'Jeepers' and 'Golly' or even 'Jeezey peezy'!",
66
66
  });
67
67
  do {
68
68
  const input = await rl.question('Message > ');
69
- const response = await bot.sendMessage(input);
70
- console.log('>', response);
69
+ await bot.sendMessage(input, (message) => console.log('>', message));
71
70
  } while (!bot.getIsDone());
72
71
  console.log('Signing off...');
73
72
  rl.close();
@@ -1,6 +1,6 @@
1
1
  import { AbstractEventEmitter } from '@sprucelabs/mercury-event-emitter';
2
2
  import { Schema, SchemaValues } from '@sprucelabs/schema';
3
- import { BotOptions, LlmAdapter, LlmEventContract, LlmMessage, SerializedBot, SprucebotLlmBot, SprucebotLLmSkill } from '../llm.types';
3
+ import { BotOptions, LlmAdapter, LlmEventContract, LlmMessage, MessageResponseCallback, SerializedBot, SprucebotLlmBot, SprucebotLLmSkill } from '../llm.types';
4
4
  export default class SprucebotLlmBotImpl<StateSchema extends Schema = Schema, State extends SchemaValues<StateSchema> = SchemaValues<StateSchema>> extends AbstractEventEmitter<LlmEventContract> implements SprucebotLlmBot<StateSchema, State> {
5
5
  static messageMemoryLimit: number;
6
6
  protected adapter: LlmAdapter;
@@ -15,7 +15,7 @@ export default class SprucebotLlmBotImpl<StateSchema extends Schema = Schema, St
15
15
  markAsDone(): void;
16
16
  getIsDone(): boolean;
17
17
  serialize(): SerializedBot<StateSchema, State>;
18
- sendMessage(message: string): Promise<string>;
18
+ sendMessage(message: string, cb?: MessageResponseCallback): Promise<string>;
19
19
  private trackMessage;
20
20
  updateState(newState: Partial<State>): Promise<void>;
21
21
  setSkill(skill: SprucebotLLmSkill<any>): void;
@@ -45,7 +45,7 @@ class SprucebotLlmBotImpl extends AbstractEventEmitter {
45
45
  skill,
46
46
  };
47
47
  }
48
- sendMessage(message) {
48
+ sendMessage(message, cb) {
49
49
  return __awaiter(this, void 0, void 0, function* () {
50
50
  var _a, _b;
51
51
  assertOptions({ message }, ['message']);
@@ -59,7 +59,7 @@ class SprucebotLlmBotImpl extends AbstractEventEmitter {
59
59
  promptTemplate: serializedSkill === null || serializedSkill === void 0 ? void 0 : serializedSkill.promptTemplate,
60
60
  });
61
61
  const parser = ResponseParser.getInstance();
62
- const { isDone, message: parsedResponse, state, } = yield parser.parse(response, serializedSkill === null || serializedSkill === void 0 ? void 0 : serializedSkill.callbacks);
62
+ const { isDone, message: parsedResponse, state, callbackResults, } = yield parser.parse(response, serializedSkill === null || serializedSkill === void 0 ? void 0 : serializedSkill.callbacks);
63
63
  this.isDone = isDone;
64
64
  if (this.stateSchema && state) {
65
65
  yield this.updateState(state);
@@ -71,6 +71,10 @@ class SprucebotLlmBotImpl extends AbstractEventEmitter {
71
71
  from: 'You',
72
72
  message: parsedResponse,
73
73
  });
74
+ cb === null || cb === void 0 ? void 0 : cb(parsedResponse);
75
+ if (callbackResults) {
76
+ yield this.sendMessage(`API Results: ${callbackResults}`, cb);
77
+ }
74
78
  return parsedResponse;
75
79
  });
76
80
  }
@@ -24,7 +24,8 @@ export class OpenAiAdapter {
24
24
  messages,
25
25
  model: (_a = options === null || options === void 0 ? void 0 : options.model) !== null && _a !== void 0 ? _a : 'gpt-4o',
26
26
  });
27
- return ((_f = (_e = (_d = (_c = (_b = response.choices) === null || _b === void 0 ? void 0 : _b[0]) === null || _c === void 0 ? void 0 : _c.message) === null || _d === void 0 ? void 0 : _d.content) === null || _e === void 0 ? void 0 : _e.trim()) !== null && _f !== void 0 ? _f : MESSAGE_RESPONSE_ERROR_MESSAGE);
27
+ const message = (_f = (_e = (_d = (_c = (_b = response.choices) === null || _b === void 0 ? void 0 : _b[0]) === null || _c === void 0 ? void 0 : _c.message) === null || _d === void 0 ? void 0 : _d.content) === null || _e === void 0 ? void 0 : _e.trim()) !== null && _f !== void 0 ? _f : MESSAGE_RESPONSE_ERROR_MESSAGE;
28
+ return message;
28
29
  });
29
30
  }
30
31
  }
@@ -76,7 +76,7 @@ export default class OpenAiMessageBuilder {
76
76
  const api = `<APIReference>\n\n${descriptions.join('\n\n')}</APIReference>`;
77
77
  return {
78
78
  role: 'system',
79
- content: `You have an API available to you to lookup answers. To use it, respond with a message in handlebars format like this: {{ FunctionName }} and I'll respond with the response of the api call in my next message. If the api call has parameters, call it like this: {{ FunctionName parameter1="value1" parameter2="value2" }}. It'll work the same way. After I respond with the api's response and you can use it going forward. The api is as follows (in xml format):\n\n${api}`,
79
+ content: `You have an API available to you to lookup answers. When you need the response of the function call to proceed, you can call a function using a custom markup we created that looks like this: << FunctionName />>. The API will respond with the results and then you can continue the conversation with your new knowledge. If the api call has parameters, call it like this: << FunctionName >>parameters json encoded<</ FunctionName >>. Make sure to json encode the data and drop it between the function tags. I will respond with the api's response and you can use it going forward. The api is as follows (in xml format):\n\n${api}`,
80
80
  };
81
81
  }
82
82
  buildPleaseKeepInMindMessage(pleaseKeepInMindThat) {
@@ -1,4 +1,4 @@
1
- import renderPlaceholder from '../parsingResponses/renderPlaceholder.js';
1
+ import renderLegacyPlaceholder from '../parsingResponses/renderPlaceholder.js';
2
2
  export const STATE_BOUNDARY = '*****';
3
3
  export const DONE_TOKEN = `DONE_DONE_DONE`;
4
4
  export const CALLBACK_BOUNDARY = 'xxxxx';
@@ -37,7 +37,7 @@ When asking me about a "select" field, make sure I only pick a valid choice by s
37
37
 
38
38
  Your primary objective for this conversation is <%= it.skill.yourJobIfYouChooseToAcceptItIs %>
39
39
  <% if (it.skill.callbacks) { %>
40
- While we are talking, there are going to be things I don't want you to answer, but instead to respond with a placeholder in the form of ${renderPlaceholder('example')}.
40
+ While we are talking, there are going to be things I don't want you to answer, but instead to respond with a placeholder in the form of ${renderLegacyPlaceholder('example')}.
41
41
 
42
42
  Here are the placeholders we will be using:
43
43
 
package/build/esm/chat.js CHANGED
@@ -32,13 +32,12 @@ void (() => __awaiter(void 0, void 0, void 0, function* () {
32
32
  };
33
33
  const bot = bots.Bot({
34
34
  adapter,
35
- skill: skills.profile,
35
+ skill: skills.callbacks,
36
36
  youAre: "a bot named Sprucebot that is in test mode. At the start of every conversation, you introduce yourself and announce that you are in test mode so I don't get confused! You are both hip and adorable. You say things like, 'Jeepers' and 'Golly' or even 'Jeezey peezy'!",
37
37
  });
38
38
  do {
39
39
  const input = yield rl.question('Message > ');
40
- const response = yield bot.sendMessage(input);
41
- console.log('>', response);
40
+ yield bot.sendMessage(input, (message) => console.log('>', message));
42
41
  } while (!bot.getIsDone());
43
42
  console.log('Signing off...');
44
43
  rl.close();
@@ -10,7 +10,7 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
10
10
  export default function buildCallbackSkill(bots) {
11
11
  return bots.Skill({
12
12
  weAreDoneWhen: 'the appointment is booked!',
13
- yourJobIfYouChooseToAcceptItIs: "to be be the best appointment taker on the planet. You have a many years of experience. You are going to ask me only 2 questions for this practice run. First, you'll ask me to pick an available time. Then, you'll ask me to pick my favorite color:",
13
+ yourJobIfYouChooseToAcceptItIs: "to be be the best appointment taker on the planet. You have a many years of experience. You are going to ask me only 2 questions for this practice run. First, you'll ask me to pick an available time. Then, you'll ask me to pick my favorite color (make sure to call the api to see what times and colors i can choose from). After all is said and done, make sure to actually book the appointment!:",
14
14
  pleaseKeepInMindThat: [
15
15
  "getting a service is really important, so if i don't like any of the time, as to check another day or if i want to see a different provider",
16
16
  ],
@@ -35,6 +35,25 @@ export default function buildCallbackSkill(bots) {
35
35
  }),
36
36
  useThisWhenever: 'your are showing what colors i can pick from.',
37
37
  },
38
+ book: {
39
+ cb: (options) => __awaiter(this, void 0, void 0, function* () {
40
+ console.log('BOOKING OPTIONS', options);
41
+ return 'Appointment booked!';
42
+ }),
43
+ useThisWhenever: 'You are ready to book an appointment!',
44
+ parameters: [
45
+ {
46
+ name: 'time',
47
+ isRequired: true,
48
+ type: 'string',
49
+ },
50
+ {
51
+ name: 'color',
52
+ isRequired: true,
53
+ type: 'string',
54
+ },
55
+ ],
56
+ },
38
57
  },
39
58
  });
40
59
  }
@@ -8,7 +8,7 @@ export interface BotOptions<StateSchema extends Schema = Schema, State extends S
8
8
  export interface SprucebotLlmBot<StateSchema extends Schema = Schema, State extends SchemaValues<StateSchema> = SchemaValues<StateSchema>> extends MercuryEventEmitter<LlmEventContract> {
9
9
  markAsDone(): void;
10
10
  getIsDone(): boolean;
11
- sendMessage(message: string): Promise<string>;
11
+ sendMessage(message: string, responseCb?: MessageResponseCallback): Promise<string>;
12
12
  serialize(): SerializedBot<StateSchema, State>;
13
13
  updateState(state: Partial<State>): Promise<void>;
14
14
  setSkill(skill: SprucebotLLmSkill<any>): void;
@@ -62,7 +62,7 @@ export interface SerializedSkill<StateSchema extends Schema = Schema, State exte
62
62
  }
63
63
  export type LlmCallbackMap = Record<string, LlmCallback>;
64
64
  export interface LlmCallback {
65
- cb: () => string | Promise<string>;
65
+ cb: (options?: Record<string, any>) => string | Promise<string>;
66
66
  useThisWhenever: string;
67
67
  parameters?: LlmCallbackParameter[];
68
68
  }
@@ -72,3 +72,4 @@ export interface LlmCallbackParameter {
72
72
  isRequired?: boolean;
73
73
  description?: string;
74
74
  }
75
+ export type MessageResponseCallback = (message: string) => any;
@@ -4,7 +4,7 @@ export default class ResponseParser {
4
4
  static setInstance(parser: ResponseParser): void;
5
5
  static getInstance(): ResponseParser;
6
6
  parse(response: string, callbacks?: LlmCallbackMap): Promise<ParsedResponse>;
7
- private invokeCallback;
7
+ private invokeCallbackAndDropInLegacyResults;
8
8
  private doesIncludeDoneToken;
9
9
  private parseState;
10
10
  }
@@ -12,4 +12,5 @@ export interface ParsedResponse {
12
12
  isDone: boolean;
13
13
  state?: Record<string, any>;
14
14
  message: string;
15
+ callbackResults?: string;
15
16
  }
@@ -8,7 +8,7 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
8
8
  });
9
9
  };
10
10
  import { DONE_TOKEN, STATE_BOUNDARY } from '../bots/templates.js';
11
- import renderPlaceholder from './renderPlaceholder.js';
11
+ import renderLegacyPlaceholder from './renderPlaceholder.js';
12
12
  class ResponseParser {
13
13
  static setInstance(parser) {
14
14
  this.instance = parser;
@@ -18,12 +18,29 @@ class ResponseParser {
18
18
  }
19
19
  parse(response, callbacks) {
20
20
  return __awaiter(this, void 0, void 0, function* () {
21
+ var _a;
21
22
  let message = response.replace(DONE_TOKEN, '').trim();
22
23
  let state;
24
+ let callbackResults;
23
25
  for (const key of Object.keys(callbacks || {})) {
24
- const match = message.match(renderPlaceholder(key));
26
+ const match = message.match(renderLegacyPlaceholder(key));
25
27
  if (match) {
26
- message = yield this.invokeCallback(callbacks, key, message);
28
+ message = yield this.invokeCallbackAndDropInLegacyResults(callbacks, key, message);
29
+ }
30
+ let simpleMatches = message.match(new RegExp(`<<\\s*${key}\\s*\/>>`, 'g'));
31
+ let data;
32
+ if (!simpleMatches) {
33
+ const matchWithJson = message
34
+ .matchAll(new RegExp(`<<\\s*${key}\\s*>>(.*?)<<\/\\s*${key}\\s*>>`, 'gs'))
35
+ .next().value;
36
+ simpleMatches = (matchWithJson === null || matchWithJson === void 0 ? void 0 : matchWithJson[0]) ? [matchWithJson === null || matchWithJson === void 0 ? void 0 : matchWithJson[0]] : null;
37
+ data = (matchWithJson === null || matchWithJson === void 0 ? void 0 : matchWithJson[1])
38
+ ? JSON.parse(matchWithJson === null || matchWithJson === void 0 ? void 0 : matchWithJson[1])
39
+ : undefined;
40
+ }
41
+ if (simpleMatches) {
42
+ callbackResults = yield ((_a = callbacks === null || callbacks === void 0 ? void 0 : callbacks[key]) === null || _a === void 0 ? void 0 : _a.cb(data));
43
+ message = message.replace(simpleMatches[0], '').trim();
27
44
  }
28
45
  }
29
46
  const { match, fullMatch } = this.parseState(message);
@@ -35,14 +52,15 @@ class ResponseParser {
35
52
  isDone: this.doesIncludeDoneToken(response),
36
53
  state,
37
54
  message,
55
+ callbackResults,
38
56
  };
39
57
  });
40
58
  }
41
- invokeCallback(callbacks, key, message) {
59
+ invokeCallbackAndDropInLegacyResults(callbacks, key, message) {
42
60
  return __awaiter(this, void 0, void 0, function* () {
43
61
  var _a;
44
62
  const v = yield ((_a = callbacks === null || callbacks === void 0 ? void 0 : callbacks[key]) === null || _a === void 0 ? void 0 : _a.cb());
45
- message = message.replace(renderPlaceholder(key), v !== null && v !== void 0 ? v : '').trim();
63
+ message = message.replace(renderLegacyPlaceholder(key), v !== null && v !== void 0 ? v : '').trim();
46
64
  return message;
47
65
  });
48
66
  }
@@ -1 +1 @@
1
- export default function renderPlaceholder(key: string): string;
1
+ export default function renderLegacyPlaceholder(key: string): string;
@@ -1,4 +1,4 @@
1
1
  import { CALLBACK_BOUNDARY } from '../bots/templates.js';
2
- export default function renderPlaceholder(key) {
2
+ export default function renderLegacyPlaceholder(key) {
3
3
  return `${CALLBACK_BOUNDARY} ${key} ${CALLBACK_BOUNDARY}`;
4
4
  }
@@ -4,7 +4,7 @@ exports.default = buildCallbackSkill;
4
4
  function buildCallbackSkill(bots) {
5
5
  return bots.Skill({
6
6
  weAreDoneWhen: 'the appointment is booked!',
7
- yourJobIfYouChooseToAcceptItIs: "to be be the best appointment taker on the planet. You have a many years of experience. You are going to ask me only 2 questions for this practice run. First, you'll ask me to pick an available time. Then, you'll ask me to pick my favorite color:",
7
+ yourJobIfYouChooseToAcceptItIs: "to be be the best appointment taker on the planet. You have a many years of experience. You are going to ask me only 2 questions for this practice run. First, you'll ask me to pick an available time. Then, you'll ask me to pick my favorite color (make sure to call the api to see what times and colors i can choose from). After all is said and done, make sure to actually book the appointment!:",
8
8
  pleaseKeepInMindThat: [
9
9
  "getting a service is really important, so if i don't like any of the time, as to check another day or if i want to see a different provider",
10
10
  ],
@@ -29,6 +29,25 @@ function buildCallbackSkill(bots) {
29
29
  },
30
30
  useThisWhenever: 'your are showing what colors i can pick from.',
31
31
  },
32
+ book: {
33
+ cb: async (options) => {
34
+ console.log('BOOKING OPTIONS', options);
35
+ return 'Appointment booked!';
36
+ },
37
+ useThisWhenever: 'You are ready to book an appointment!',
38
+ parameters: [
39
+ {
40
+ name: 'time',
41
+ isRequired: true,
42
+ type: 'string',
43
+ },
44
+ {
45
+ name: 'color',
46
+ isRequired: true,
47
+ type: 'string',
48
+ },
49
+ ],
50
+ },
32
51
  },
33
52
  });
34
53
  }
@@ -8,7 +8,7 @@ export interface BotOptions<StateSchema extends Schema = Schema, State extends S
8
8
  export interface SprucebotLlmBot<StateSchema extends Schema = Schema, State extends SchemaValues<StateSchema> = SchemaValues<StateSchema>> extends MercuryEventEmitter<LlmEventContract> {
9
9
  markAsDone(): void;
10
10
  getIsDone(): boolean;
11
- sendMessage(message: string): Promise<string>;
11
+ sendMessage(message: string, responseCb?: MessageResponseCallback): Promise<string>;
12
12
  serialize(): SerializedBot<StateSchema, State>;
13
13
  updateState(state: Partial<State>): Promise<void>;
14
14
  setSkill(skill: SprucebotLLmSkill<any>): void;
@@ -62,7 +62,7 @@ export interface SerializedSkill<StateSchema extends Schema = Schema, State exte
62
62
  }
63
63
  export type LlmCallbackMap = Record<string, LlmCallback>;
64
64
  export interface LlmCallback {
65
- cb: () => string | Promise<string>;
65
+ cb: (options?: Record<string, any>) => string | Promise<string>;
66
66
  useThisWhenever: string;
67
67
  parameters?: LlmCallbackParameter[];
68
68
  }
@@ -72,3 +72,4 @@ export interface LlmCallbackParameter {
72
72
  isRequired?: boolean;
73
73
  description?: string;
74
74
  }
75
+ export type MessageResponseCallback = (message: string) => any;
@@ -4,7 +4,7 @@ export default class ResponseParser {
4
4
  static setInstance(parser: ResponseParser): void;
5
5
  static getInstance(): ResponseParser;
6
6
  parse(response: string, callbacks?: LlmCallbackMap): Promise<ParsedResponse>;
7
- private invokeCallback;
7
+ private invokeCallbackAndDropInLegacyResults;
8
8
  private doesIncludeDoneToken;
9
9
  private parseState;
10
10
  }
@@ -12,4 +12,5 @@ export interface ParsedResponse {
12
12
  isDone: boolean;
13
13
  state?: Record<string, any>;
14
14
  message: string;
15
+ callbackResults?: string;
15
16
  }
@@ -15,10 +15,26 @@ class ResponseParser {
15
15
  async parse(response, callbacks) {
16
16
  let message = response.replace(templates_1.DONE_TOKEN, '').trim();
17
17
  let state;
18
+ let callbackResults;
18
19
  for (const key of Object.keys(callbacks || {})) {
19
20
  const match = message.match((0, renderPlaceholder_1.default)(key));
20
21
  if (match) {
21
- message = await this.invokeCallback(callbacks, key, message);
22
+ message = await this.invokeCallbackAndDropInLegacyResults(callbacks, key, message);
23
+ }
24
+ let simpleMatches = message.match(new RegExp(`<<\\s*${key}\\s*\/>>`, 'g'));
25
+ let data;
26
+ if (!simpleMatches) {
27
+ const matchWithJson = message
28
+ .matchAll(new RegExp(`<<\\s*${key}\\s*>>(.*?)<<\/\\s*${key}\\s*>>`, 'gs'))
29
+ .next().value;
30
+ simpleMatches = matchWithJson?.[0] ? [matchWithJson?.[0]] : null;
31
+ data = matchWithJson?.[1]
32
+ ? JSON.parse(matchWithJson?.[1])
33
+ : undefined;
34
+ }
35
+ if (simpleMatches) {
36
+ callbackResults = await callbacks?.[key]?.cb(data);
37
+ message = message.replace(simpleMatches[0], '').trim();
22
38
  }
23
39
  }
24
40
  const { match, fullMatch } = this.parseState(message);
@@ -30,9 +46,10 @@ class ResponseParser {
30
46
  isDone: this.doesIncludeDoneToken(response),
31
47
  state,
32
48
  message,
49
+ callbackResults,
33
50
  };
34
51
  }
35
- async invokeCallback(callbacks, key, message) {
52
+ async invokeCallbackAndDropInLegacyResults(callbacks, key, message) {
36
53
  const v = await callbacks?.[key]?.cb();
37
54
  message = message.replace((0, renderPlaceholder_1.default)(key), v ?? '').trim();
38
55
  return message;
@@ -1 +1 @@
1
- export default function renderPlaceholder(key: string): string;
1
+ export default function renderLegacyPlaceholder(key: string): string;
@@ -1,7 +1,7 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.default = renderPlaceholder;
3
+ exports.default = renderLegacyPlaceholder;
4
4
  const templates_1 = require("../bots/templates");
5
- function renderPlaceholder(key) {
5
+ function renderLegacyPlaceholder(key) {
6
6
  return `${templates_1.CALLBACK_BOUNDARY} ${key} ${templates_1.CALLBACK_BOUNDARY}`;
7
7
  }
package/package.json CHANGED
@@ -9,7 +9,7 @@
9
9
  "@sprucelabs/spruce-test-fixtures"
10
10
  ]
11
11
  },
12
- "version": "5.0.689",
12
+ "version": "6.0.0",
13
13
  "files": [
14
14
  "build"
15
15
  ],