@sprucelabs/sprucebot-llm 15.1.2 → 15.1.4

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.
@@ -37,11 +37,26 @@ class AnthropicAdapter {
37
37
  async sendHandler(params, sendOptions) {
38
38
  const { messages: openAiMessages, model } = params;
39
39
  const messages = [];
40
+ const cacheMarkerIdx = openAiMessages.findIndex((msg) => msg.cache_marker);
40
41
  for (const msg of openAiMessages) {
41
- messages.push({
42
- role: msg.role === 'assistant' ? 'assistant' : 'user',
43
- content: msg.content,
44
- });
42
+ if (!msg.cache_marker) {
43
+ const m = msg;
44
+ messages.push({
45
+ role: m.role === 'assistant' ? 'assistant' : 'user',
46
+ content: m.content,
47
+ });
48
+ }
49
+ }
50
+ if (cacheMarkerIdx > -1) {
51
+ messages[cacheMarkerIdx - 1].content = [
52
+ {
53
+ type: 'text',
54
+ text: messages[cacheMarkerIdx - 1].content,
55
+ cache_control: {
56
+ type: 'ephemeral',
57
+ },
58
+ },
59
+ ];
45
60
  }
46
61
  const response = await this.api.messages.create({
47
62
  max_tokens: this.maxTokens,
@@ -51,7 +66,8 @@ class AnthropicAdapter {
51
66
  type: this.isThinkingEnabled ? 'adaptive' : 'disabled',
52
67
  },
53
68
  }, sendOptions);
54
- this.log?.info('Received response from Anthropic', JSON.stringify(response, null, 2));
69
+ const { usage } = response;
70
+ this.log?.info(`[TOKEN USAGE] input=${usage.input_tokens} cache_create=${usage.cache_creation_input_tokens ?? 0} cache_read=${usage.cache_read_input_tokens ?? 0} output=${usage.output_tokens}`);
55
71
  const text = response.content
56
72
  .filter((block) => block.type === 'text')
57
73
  ?.map((block) => block.text)
@@ -6,7 +6,7 @@ export default class MessageBuilder {
6
6
  protected constructor(bot: SprucebotLlmBot, options?: BuildMessageOptions);
7
7
  static Builder(bot: SprucebotLlmBot, options?: BuildMessageOptions): MessageBuilder;
8
8
  private get parser();
9
- buildMessages(): ChatCompletionMessageParam[];
9
+ buildMessages(): MessageBuilderMessage[];
10
10
  private buildChatHistoryMessages;
11
11
  private mapMessageToCompletion;
12
12
  private maxCharsOfPastMessages;
@@ -23,4 +23,8 @@ export default class MessageBuilder {
23
23
  interface BuildMessageOptions {
24
24
  memoryLimit?: number;
25
25
  }
26
+ export interface MessageBuilderCacheMarker {
27
+ cache_marker: true;
28
+ }
29
+ export type MessageBuilderMessage = ChatCompletionMessageParam | MessageBuilderCacheMarker;
26
30
  export {};
@@ -26,6 +26,12 @@ class MessageBuilder {
26
26
  ...this.buildSkillMessages(values.skill),
27
27
  ...this.buildChatHistoryMessages(values.messages),
28
28
  ];
29
+ if (!values.skill) {
30
+ const first = allMessages[0];
31
+ allMessages.shift();
32
+ allMessages.unshift({ cache_marker: true });
33
+ allMessages.unshift(first);
34
+ }
29
35
  return allMessages;
30
36
  }
31
37
  buildChatHistoryMessages(messages) {
@@ -99,9 +105,6 @@ class MessageBuilder {
99
105
  if (skill.stateSchema) {
100
106
  messages.push(this.buildStateSchemaMessage(skill.stateSchema));
101
107
  }
102
- if (skill.state) {
103
- messages.push(this.buildStateMessage(skill.state));
104
- }
105
108
  if (skill.weAreDoneWhen) {
106
109
  messages.push(this.buildWeAreDoneWhenMessage(skill.weAreDoneWhen));
107
110
  }
@@ -111,6 +114,10 @@ class MessageBuilder {
111
114
  if (skill.pleaseKeepInMindThat) {
112
115
  messages.push(this.buildPleaseKeepInMindMessage(skill.pleaseKeepInMindThat));
113
116
  }
117
+ messages.push({ cache_marker: true });
118
+ if (skill.state) {
119
+ messages.push(this.buildStateMessage(skill.state));
120
+ }
114
121
  return messages;
115
122
  }
116
123
  buildCallbacksMessage(callbacks) {
@@ -1,8 +1,8 @@
1
1
  import { Log } from '@sprucelabs/spruce-skill-utils';
2
- import OpenAI from 'openai';
3
2
  import { RequestOptions } from 'openai/internal/request-options';
4
- import { ReasoningEffort, ChatCompletionCreateParamsNonStreaming } from 'openai/resources';
3
+ import { ReasoningEffort } from 'openai/resources';
5
4
  import { SprucebotLlmBot, SendMessageOptions } from '../../llm.types';
5
+ import { MessageBuilderMessage } from './MessageBuilder';
6
6
  export default class MessageSenderImpl implements MessageSender {
7
7
  static AbortController: {
8
8
  new (): AbortController;
@@ -18,12 +18,16 @@ export default class MessageSenderImpl implements MessageSender {
18
18
  private send;
19
19
  }
20
20
  export type MessageSenderSendOptions = SendMessageOptions & {
21
- messages: OpenAI.Chat.Completions.ChatCompletionMessageParam[];
21
+ messages: MessageBuilderMessage[];
22
22
  reasoningEffort?: ReasoningEffort;
23
23
  model: string;
24
24
  abortController: AbortController;
25
25
  };
26
- export type MessageSenderSendHandler = (params: ChatCompletionCreateParamsNonStreaming, options: RequestOptions) => Promise<string | undefined>;
26
+ export interface MessageHandlerSendHandlerParams {
27
+ model: string;
28
+ messages: MessageBuilderMessage[];
29
+ }
30
+ export type MessageSenderSendHandler = (params: MessageHandlerSendHandlerParams, options: RequestOptions) => Promise<string | undefined>;
27
31
  export interface MessageSender {
28
32
  sendMessage(bot: SprucebotLlmBot, options: MessageSenderSendMessageOptions): Promise<string>;
29
33
  }
@@ -30,6 +30,8 @@ class OpenAiAdapter {
30
30
  });
31
31
  }
32
32
  async sendHandler(params, sendOptions) {
33
+ const { messages } = params;
34
+ params.messages = messages.filter((msg) => !msg.cache_marker);
33
35
  const response = await this.api.chat.completions.create(params, sendOptions);
34
36
  const responseMessage = response.choices?.[0]?.message?.content?.trim();
35
37
  return responseMessage;
@@ -39,14 +39,29 @@ class AnthropicAdapter {
39
39
  }
40
40
  sendHandler(params, sendOptions) {
41
41
  return __awaiter(this, void 0, void 0, function* () {
42
- var _a, _b;
42
+ var _a, _b, _c, _d;
43
43
  const { messages: openAiMessages, model } = params;
44
44
  const messages = [];
45
+ const cacheMarkerIdx = openAiMessages.findIndex((msg) => msg.cache_marker);
45
46
  for (const msg of openAiMessages) {
46
- messages.push({
47
- role: msg.role === 'assistant' ? 'assistant' : 'user',
48
- content: msg.content,
49
- });
47
+ if (!msg.cache_marker) {
48
+ const m = msg;
49
+ messages.push({
50
+ role: m.role === 'assistant' ? 'assistant' : 'user',
51
+ content: m.content,
52
+ });
53
+ }
54
+ }
55
+ if (cacheMarkerIdx > -1) {
56
+ messages[cacheMarkerIdx - 1].content = [
57
+ {
58
+ type: 'text',
59
+ text: messages[cacheMarkerIdx - 1].content,
60
+ cache_control: {
61
+ type: 'ephemeral',
62
+ },
63
+ },
64
+ ];
50
65
  }
51
66
  const response = yield this.api.messages.create({
52
67
  max_tokens: this.maxTokens,
@@ -56,9 +71,10 @@ class AnthropicAdapter {
56
71
  type: this.isThinkingEnabled ? 'adaptive' : 'disabled',
57
72
  },
58
73
  }, sendOptions);
59
- (_a = this.log) === null || _a === void 0 ? void 0 : _a.info('Received response from Anthropic', JSON.stringify(response, null, 2));
60
- const text = (_b = response.content
61
- .filter((block) => block.type === 'text')) === null || _b === void 0 ? void 0 : _b.map((block) => block.text).join('').trim();
74
+ const { usage } = response;
75
+ (_a = this.log) === null || _a === void 0 ? void 0 : _a.info(`[TOKEN USAGE] input=${usage.input_tokens} cache_create=${(_b = usage.cache_creation_input_tokens) !== null && _b !== void 0 ? _b : 0} cache_read=${(_c = usage.cache_read_input_tokens) !== null && _c !== void 0 ? _c : 0} output=${usage.output_tokens}`);
76
+ const text = (_d = response.content
77
+ .filter((block) => block.type === 'text')) === null || _d === void 0 ? void 0 : _d.map((block) => block.text).join('').trim();
62
78
  return text;
63
79
  });
64
80
  }
@@ -6,7 +6,7 @@ export default class MessageBuilder {
6
6
  protected constructor(bot: SprucebotLlmBot, options?: BuildMessageOptions);
7
7
  static Builder(bot: SprucebotLlmBot, options?: BuildMessageOptions): MessageBuilder;
8
8
  private get parser();
9
- buildMessages(): ChatCompletionMessageParam[];
9
+ buildMessages(): MessageBuilderMessage[];
10
10
  private buildChatHistoryMessages;
11
11
  private mapMessageToCompletion;
12
12
  private maxCharsOfPastMessages;
@@ -23,4 +23,8 @@ export default class MessageBuilder {
23
23
  interface BuildMessageOptions {
24
24
  memoryLimit?: number;
25
25
  }
26
+ export interface MessageBuilderCacheMarker {
27
+ cache_marker: true;
28
+ }
29
+ export type MessageBuilderMessage = ChatCompletionMessageParam | MessageBuilderCacheMarker;
26
30
  export {};
@@ -21,6 +21,12 @@ export default class MessageBuilder {
21
21
  ...this.buildSkillMessages(values.skill),
22
22
  ...this.buildChatHistoryMessages(values.messages),
23
23
  ];
24
+ if (!values.skill) {
25
+ const first = allMessages[0];
26
+ allMessages.shift();
27
+ allMessages.unshift({ cache_marker: true });
28
+ allMessages.unshift(first);
29
+ }
24
30
  return allMessages;
25
31
  }
26
32
  buildChatHistoryMessages(messages) {
@@ -95,9 +101,6 @@ export default class MessageBuilder {
95
101
  if (skill.stateSchema) {
96
102
  messages.push(this.buildStateSchemaMessage(skill.stateSchema));
97
103
  }
98
- if (skill.state) {
99
- messages.push(this.buildStateMessage(skill.state));
100
- }
101
104
  if (skill.weAreDoneWhen) {
102
105
  messages.push(this.buildWeAreDoneWhenMessage(skill.weAreDoneWhen));
103
106
  }
@@ -107,6 +110,10 @@ export default class MessageBuilder {
107
110
  if (skill.pleaseKeepInMindThat) {
108
111
  messages.push(this.buildPleaseKeepInMindMessage(skill.pleaseKeepInMindThat));
109
112
  }
113
+ messages.push({ cache_marker: true });
114
+ if (skill.state) {
115
+ messages.push(this.buildStateMessage(skill.state));
116
+ }
110
117
  return messages;
111
118
  }
112
119
  buildCallbacksMessage(callbacks) {
@@ -1,8 +1,8 @@
1
1
  import { Log } from '@sprucelabs/spruce-skill-utils';
2
- import OpenAI from 'openai';
3
2
  import { RequestOptions } from 'openai/internal/request-options';
4
- import { ReasoningEffort, ChatCompletionCreateParamsNonStreaming } from 'openai/resources';
3
+ import { ReasoningEffort } from 'openai/resources';
5
4
  import { SprucebotLlmBot, SendMessageOptions } from '../../llm.types';
5
+ import { MessageBuilderMessage } from './MessageBuilder';
6
6
  export default class MessageSenderImpl implements MessageSender {
7
7
  static AbortController: {
8
8
  new (): AbortController;
@@ -18,12 +18,16 @@ export default class MessageSenderImpl implements MessageSender {
18
18
  private send;
19
19
  }
20
20
  export type MessageSenderSendOptions = SendMessageOptions & {
21
- messages: OpenAI.Chat.Completions.ChatCompletionMessageParam[];
21
+ messages: MessageBuilderMessage[];
22
22
  reasoningEffort?: ReasoningEffort;
23
23
  model: string;
24
24
  abortController: AbortController;
25
25
  };
26
- export type MessageSenderSendHandler = (params: ChatCompletionCreateParamsNonStreaming, options: RequestOptions) => Promise<string | undefined>;
26
+ export interface MessageHandlerSendHandlerParams {
27
+ model: string;
28
+ messages: MessageBuilderMessage[];
29
+ }
30
+ export type MessageSenderSendHandler = (params: MessageHandlerSendHandlerParams, options: RequestOptions) => Promise<string | undefined>;
27
31
  export interface MessageSender {
28
32
  sendMessage(bot: SprucebotLlmBot, options: MessageSenderSendMessageOptions): Promise<string>;
29
33
  }
@@ -33,6 +33,8 @@ class OpenAiAdapter {
33
33
  sendHandler(params, sendOptions) {
34
34
  return __awaiter(this, void 0, void 0, function* () {
35
35
  var _a, _b, _c, _d;
36
+ const { messages } = params;
37
+ params.messages = messages.filter((msg) => !msg.cache_marker);
36
38
  const response = yield this.api.chat.completions.create(params, sendOptions);
37
39
  const responseMessage = (_d = (_c = (_b = (_a = response.choices) === null || _a === void 0 ? void 0 : _a[0]) === null || _b === void 0 ? void 0 : _b.message) === null || _c === void 0 ? void 0 : _c.content) === null || _d === void 0 ? void 0 : _d.trim();
38
40
  return responseMessage;
package/package.json CHANGED
@@ -8,7 +8,7 @@
8
8
  "eta"
9
9
  ]
10
10
  },
11
- "version": "15.1.2",
11
+ "version": "15.1.4",
12
12
  "files": [
13
13
  "build"
14
14
  ],