@sprucelabs/sprucebot-llm 15.1.10 → 15.1.12

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.
@@ -13,6 +13,7 @@ export default class MessageBuilder {
13
13
  private shouldRememberImages;
14
14
  private buildFirstMessage;
15
15
  private buildSkillMessages;
16
+ private buildFunctionSyntaxMessage;
16
17
  private buildCallbacksMessage;
17
18
  private buildPleaseKeepInMindMessage;
18
19
  private buildStateMessage;
@@ -102,6 +102,9 @@ class MessageBuilder {
102
102
  }
103
103
  const messages = [];
104
104
  messages.push(this.buildYourJobMessage(skill.yourJobIfYouChooseToAcceptItIs));
105
+ if (skill.callbacks || skill.state || skill.stateSchema) {
106
+ messages.push(this.buildFunctionSyntaxMessage());
107
+ }
105
108
  if (skill.stateSchema) {
106
109
  messages.push(this.buildStateSchemaMessage(skill.stateSchema));
107
110
  }
@@ -120,6 +123,12 @@ class MessageBuilder {
120
123
  }
121
124
  return messages;
122
125
  }
126
+ buildFunctionSyntaxMessage() {
127
+ return {
128
+ role: 'system',
129
+ content: `Throughout this conversation, all function calls and state updates use JavaScript-style syntax: @functionName({ "key": "value" }). The function name follows the @ symbol, and the single argument is a JSON object wrapped in parentheses. All JSON must be on a single line.`,
130
+ };
131
+ }
123
132
  buildCallbacksMessage(callbacks) {
124
133
  const keys = Object.keys(callbacks);
125
134
  const descriptions = [];
@@ -13,6 +13,7 @@ export default class MessageBuilder {
13
13
  private shouldRememberImages;
14
14
  private buildFirstMessage;
15
15
  private buildSkillMessages;
16
+ private buildFunctionSyntaxMessage;
16
17
  private buildCallbacksMessage;
17
18
  private buildPleaseKeepInMindMessage;
18
19
  private buildStateMessage;
@@ -98,6 +98,9 @@ export default class MessageBuilder {
98
98
  }
99
99
  const messages = [];
100
100
  messages.push(this.buildYourJobMessage(skill.yourJobIfYouChooseToAcceptItIs));
101
+ if (skill.callbacks || skill.state || skill.stateSchema) {
102
+ messages.push(this.buildFunctionSyntaxMessage());
103
+ }
101
104
  if (skill.stateSchema) {
102
105
  messages.push(this.buildStateSchemaMessage(skill.stateSchema));
103
106
  }
@@ -116,6 +119,12 @@ export default class MessageBuilder {
116
119
  }
117
120
  return messages;
118
121
  }
122
+ buildFunctionSyntaxMessage() {
123
+ return {
124
+ role: 'system',
125
+ content: `Throughout this conversation, all function calls and state updates use JavaScript-style syntax: @functionName({ "key": "value" }). The function name follows the @ symbol, and the single argument is a JSON object wrapped in parentheses. All JSON must be on a single line.`,
126
+ };
127
+ }
119
128
  buildCallbacksMessage(callbacks) {
120
129
  const keys = Object.keys(callbacks);
121
130
  const descriptions = [];
@@ -15,9 +15,8 @@ export default class ResponseParserV2 {
15
15
  let message = response.replace(DONE_TOKEN, '').trim();
16
16
  let state = undefined;
17
17
  let callbackResults = undefined;
18
- const hasCallbacks = message.includes('@callback') ||
19
- (callbacks != null &&
20
- Object.keys(callbacks).some((name) => message.includes(`@${name}`)));
18
+ const hasCallbacks = callbacks != null &&
19
+ Object.keys(callbacks).some((name) => message.includes(`@${name}(`));
21
20
  if (hasCallbacks) {
22
21
  const { callbackResults: c, message: m } = yield this.invokeCallbacks(message, callbacks);
23
22
  callbackResults = c;
@@ -25,8 +24,10 @@ export default class ResponseParserV2 {
25
24
  }
26
25
  const hasState = response.includes('@updateState');
27
26
  if (hasState && message) {
28
- const stateMatch = message.match(/@updateState\s+({[\s\S]*?})\n?/);
29
- if (stateMatch && stateMatch[1]) {
27
+ const stateMatches = [
28
+ ...message.matchAll(/@updateState\(({[\s\S]*?})\)\n?/g),
29
+ ];
30
+ for (const stateMatch of stateMatches) {
30
31
  try {
31
32
  state = JSON.parse(stateMatch[1]);
32
33
  }
@@ -55,17 +56,16 @@ export default class ResponseParserV2 {
55
56
  let callbackStrippedMessage = message;
56
57
  let callbackResults = '';
57
58
  const reserved = new Set(['updateState', 'results']);
58
- const matches = [...message.matchAll(/^@(\w+)\s+({.*})$/gm)];
59
+ const matches = [...message.matchAll(/^@(\w+)\(({.*})\)$/gm)];
59
60
  for (const match of matches) {
60
61
  if (reserved.has(match[1])) {
61
62
  continue;
62
63
  }
63
64
  const parsed = JSON.parse(match[2]);
64
- const isCanonical = match[1] === 'callback';
65
- const name = isCanonical ? parsed.name : match[1];
66
- const options = isCanonical ? parsed.options : parsed;
65
+ const name = match[1];
66
+ const options = parsed;
67
67
  const callback = callbacks === null || callbacks === void 0 ? void 0 : callbacks[name];
68
- if (!isCanonical && !callback) {
68
+ if (!callback) {
69
69
  continue;
70
70
  }
71
71
  const parts = callbackStrippedMessage.split(match[0]);
@@ -101,32 +101,31 @@ export default class ResponseParserV2 {
101
101
  }
102
102
  getStateUpdateInstructions() {
103
103
  return `Updating state works similar to all function calls. Use the following syntax:
104
- @updateState { "field1": "value1", "field2": "value2" }
104
+ @updateState({ "field1": "value1", "field2": "value2" })
105
105
  Make sure to json encode only the fields you want to change. You can update state once and do it at the end of any messages you send. IMPORTANT: JSON must be on a single line. Do NOT use multi-line or formatted JSON.
106
106
  Your user-facing message is always sent to the user, even if @updateState fails. If @updateState fails later, do not repeat the same message. Only send the specific @updateState needed to fix the missing state change.
107
107
  Good example:
108
- @updateState { "favoriteColor": "blue", "firstName": "Taylor" }
108
+ @updateState({ "favoriteColor": "blue", "firstName": "Taylor" })
109
109
  Bad examples:
110
110
  @updateState
111
111
  { "favoriteColor": "blue" }
112
- @updateState {
112
+ @updateState({
113
113
  "favoriteColor": "blue"
114
- }
115
- @updateState { favoriteColor: "blue" }`;
114
+ })
115
+ @updateState({ favoriteColor: "blue" })`;
116
116
  }
117
117
  getFunctionCallInstructions() {
118
118
  return `A function call is done using the following syntax:
119
- @callback { "name": "callbackName", "options": {} }
120
- Make sure to json encode the options and include the name of the callback you want to call. You can call as many callbacks as you want in a single response by including multiple @callback lines. IMPORTANT: JSON must be on a single line. Do NOT use multi-line or formatted JSON. Also, do NOT call something like @myCallback. You would call it like this: @callback { "name": "myCallback", "options": {} }
119
+ @callbackName({ "key": "value" })
120
+ Make sure to json encode the options. You can call as many callbacks as you want in a single response by including multiple @functionName() lines. IMPORTANT: JSON must be on a single line. Do NOT use multi-line or formatted JSON.
121
121
  Your user-facing message is always sent to the user, even if a callback fails. Successful callbacks have already run successfully. If a callback fails later, do not repeat the same message and do not repeat successful callbacks. Only call the specific callback needed to fix the failed gap.
122
122
  Good example:
123
- @callback { "name": "lookupWeather", "options": { "zip": "80524" } }
123
+ @lookupWeather({ "zip": "80524" })
124
124
  Bad examples:
125
125
  @lookupWeather { "zip": "80524" }
126
- @callback
127
- { "name": "lookupWeather", "options": { "zip": "80524" } }
128
- @callback { "name": "lookupWeather", "options": {
129
- "zip": "80524"
130
- } }`;
126
+ @lookupWeather(
127
+ { "zip": "80524" }
128
+ )
129
+ @lookupWeather({ zip: "80524" })`;
131
130
  }
132
131
  }
@@ -10,9 +10,8 @@ class ResponseParserV2 {
10
10
  let message = response.replace(templates_1.DONE_TOKEN, '').trim();
11
11
  let state = undefined;
12
12
  let callbackResults = undefined;
13
- const hasCallbacks = message.includes('@callback') ||
14
- (callbacks != null &&
15
- Object.keys(callbacks).some((name) => message.includes(`@${name}`)));
13
+ const hasCallbacks = callbacks != null &&
14
+ Object.keys(callbacks).some((name) => message.includes(`@${name}(`));
16
15
  if (hasCallbacks) {
17
16
  const { callbackResults: c, message: m } = await this.invokeCallbacks(message, callbacks);
18
17
  callbackResults = c;
@@ -20,8 +19,10 @@ class ResponseParserV2 {
20
19
  }
21
20
  const hasState = response.includes('@updateState');
22
21
  if (hasState && message) {
23
- const stateMatch = message.match(/@updateState\s+({[\s\S]*?})\n?/);
24
- if (stateMatch && stateMatch[1]) {
22
+ const stateMatches = [
23
+ ...message.matchAll(/@updateState\(({[\s\S]*?})\)\n?/g),
24
+ ];
25
+ for (const stateMatch of stateMatches) {
25
26
  try {
26
27
  state = JSON.parse(stateMatch[1]);
27
28
  }
@@ -48,17 +49,16 @@ class ResponseParserV2 {
48
49
  let callbackStrippedMessage = message;
49
50
  let callbackResults = '';
50
51
  const reserved = new Set(['updateState', 'results']);
51
- const matches = [...message.matchAll(/^@(\w+)\s+({.*})$/gm)];
52
+ const matches = [...message.matchAll(/^@(\w+)\(({.*})\)$/gm)];
52
53
  for (const match of matches) {
53
54
  if (reserved.has(match[1])) {
54
55
  continue;
55
56
  }
56
57
  const parsed = JSON.parse(match[2]);
57
- const isCanonical = match[1] === 'callback';
58
- const name = isCanonical ? parsed.name : match[1];
59
- const options = isCanonical ? parsed.options : parsed;
58
+ const name = match[1];
59
+ const options = parsed;
60
60
  const callback = callbacks?.[name];
61
- if (!isCanonical && !callback) {
61
+ if (!callback) {
62
62
  continue;
63
63
  }
64
64
  const parts = callbackStrippedMessage.split(match[0]);
@@ -93,33 +93,32 @@ class ResponseParserV2 {
93
93
  }
94
94
  getStateUpdateInstructions() {
95
95
  return `Updating state works similar to all function calls. Use the following syntax:
96
- @updateState { "field1": "value1", "field2": "value2" }
96
+ @updateState({ "field1": "value1", "field2": "value2" })
97
97
  Make sure to json encode only the fields you want to change. You can update state once and do it at the end of any messages you send. IMPORTANT: JSON must be on a single line. Do NOT use multi-line or formatted JSON.
98
98
  Your user-facing message is always sent to the user, even if @updateState fails. If @updateState fails later, do not repeat the same message. Only send the specific @updateState needed to fix the missing state change.
99
99
  Good example:
100
- @updateState { "favoriteColor": "blue", "firstName": "Taylor" }
100
+ @updateState({ "favoriteColor": "blue", "firstName": "Taylor" })
101
101
  Bad examples:
102
102
  @updateState
103
103
  { "favoriteColor": "blue" }
104
- @updateState {
104
+ @updateState({
105
105
  "favoriteColor": "blue"
106
- }
107
- @updateState { favoriteColor: "blue" }`;
106
+ })
107
+ @updateState({ favoriteColor: "blue" })`;
108
108
  }
109
109
  getFunctionCallInstructions() {
110
110
  return `A function call is done using the following syntax:
111
- @callback { "name": "callbackName", "options": {} }
112
- Make sure to json encode the options and include the name of the callback you want to call. You can call as many callbacks as you want in a single response by including multiple @callback lines. IMPORTANT: JSON must be on a single line. Do NOT use multi-line or formatted JSON. Also, do NOT call something like @myCallback. You would call it like this: @callback { "name": "myCallback", "options": {} }
111
+ @callbackName({ "key": "value" })
112
+ Make sure to json encode the options. You can call as many callbacks as you want in a single response by including multiple @functionName() lines. IMPORTANT: JSON must be on a single line. Do NOT use multi-line or formatted JSON.
113
113
  Your user-facing message is always sent to the user, even if a callback fails. Successful callbacks have already run successfully. If a callback fails later, do not repeat the same message and do not repeat successful callbacks. Only call the specific callback needed to fix the failed gap.
114
114
  Good example:
115
- @callback { "name": "lookupWeather", "options": { "zip": "80524" } }
115
+ @lookupWeather({ "zip": "80524" })
116
116
  Bad examples:
117
117
  @lookupWeather { "zip": "80524" }
118
- @callback
119
- { "name": "lookupWeather", "options": { "zip": "80524" } }
120
- @callback { "name": "lookupWeather", "options": {
121
- "zip": "80524"
122
- } }`;
118
+ @lookupWeather(
119
+ { "zip": "80524" }
120
+ )
121
+ @lookupWeather({ zip: "80524" })`;
123
122
  }
124
123
  }
125
124
  exports.default = ResponseParserV2;
package/package.json CHANGED
@@ -8,7 +8,7 @@
8
8
  "eta"
9
9
  ]
10
10
  },
11
- "version": "15.1.10",
11
+ "version": "15.1.12",
12
12
  "files": [
13
13
  "build"
14
14
  ],