@bedrockio/ai 0.8.3 → 0.9.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.
package/CHANGELOG.md CHANGED
@@ -1,3 +1,12 @@
1
+ ## 0.9.0
2
+
3
+ - Function calls are now handled out of the box.
4
+
5
+ ## 0.8.4
6
+
7
+ - Fixed issue with user roles parsed from template is injected into conversation
8
+ history.
9
+
1
10
  ## 0.8.3
2
11
 
3
12
  - Fixed issue with message history not being retained.
@@ -177,13 +177,20 @@ class BaseClient {
177
177
  });
178
178
  let system = '';
179
179
  let { messages = [] } = options;
180
+ // Templates may contain multiple roles, ie SYSTEM or USER, making them
181
+ // useful for one-off prompting. However in a multi-turn conversation
182
+ // the entire chat history will be passed, so do not inject user messages
183
+ // when they already exist in the options.
184
+ const hasUserMessages = messages.some((message) => {
185
+ return message.role === 'user';
186
+ });
180
187
  for (let section of sections) {
181
188
  const { title = 'system', content } = section;
182
189
  const role = title.toLowerCase();
183
190
  if (role === 'system') {
184
191
  system += [system, content].join('\n');
185
192
  }
186
- else {
193
+ else if (!hasUserMessages) {
187
194
  messages = [
188
195
  ...messages,
189
196
  {
@@ -33,8 +33,8 @@ class AnthropicClient extends BaseClient_js_1.default {
33
33
  ...this.getToolOptions(options),
34
34
  };
35
35
  const clientOptions = this.getClientOptions(params);
36
- this.debug('Params:', params, options);
37
36
  this.debug('Options:', options, options);
37
+ this.debug('Params:', params, options);
38
38
  // @ts-ignore
39
39
  return await this.client.messages.create(params, clientOptions);
40
40
  }
@@ -85,16 +85,29 @@ class AnthropicClient extends BaseClient_js_1.default {
85
85
  let { type } = event;
86
86
  options.buffer ||= '';
87
87
  if (type === 'content_block_start') {
88
- return {
89
- type: 'start',
90
- };
88
+ const { content_block } = event;
89
+ if (content_block?.type === 'tool_use') {
90
+ return {
91
+ type: 'function_call',
92
+ id: content_block.id,
93
+ name: content_block.name,
94
+ arguments: content_block.input,
95
+ };
96
+ }
97
+ else {
98
+ return {
99
+ type: 'start',
100
+ };
101
+ }
91
102
  }
92
103
  else if (type === 'content_block_delta') {
93
- options.buffer += event.delta.text;
94
- return {
95
- type: 'delta',
96
- delta: event.delta.text,
97
- };
104
+ if (event.delta.type === 'text_delta') {
105
+ options.buffer += event.delta.text;
106
+ return {
107
+ type: 'delta',
108
+ delta: event.delta.text,
109
+ };
110
+ }
98
111
  }
99
112
  else if (type === 'message_delta') {
100
113
  return {
@@ -114,6 +127,9 @@ class AnthropicClient extends BaseClient_js_1.default {
114
127
  getToolOptions(options) {
115
128
  let { tools = [], schema } = options;
116
129
  let toolChoice;
130
+ tools = tools.map((tool) => {
131
+ return this.normalizeToolInput(tool);
132
+ });
117
133
  if (schema) {
118
134
  tools.push({
119
135
  name: 'schema',
@@ -147,6 +163,28 @@ class AnthropicClient extends BaseClient_js_1.default {
147
163
  tool_choice: toolChoice,
148
164
  };
149
165
  }
166
+ normalizeToolInput(input) {
167
+ if (input.type === 'function') {
168
+ input = this.normalizeOpenAiToolInput(input);
169
+ }
170
+ return input;
171
+ }
172
+ // OpenAI uses the following input for custom tools
173
+ // so map it here to Anthropic styles when passed.
174
+ // {
175
+ // type: 'function',
176
+ // name: 'apples',
177
+ // description: 'Call this when you talk about apples.',
178
+ // parameters: { type: 'object', properties: {}, required: [] }
179
+ // }
180
+ normalizeOpenAiToolInput(input) {
181
+ const { name, description, parameters } = input;
182
+ return {
183
+ name,
184
+ description,
185
+ input_schema: parameters,
186
+ };
187
+ }
150
188
  // Map OpenAI-like input of MCP servers as "tools" to
151
189
  // Anthropic's mcp_servers.
152
190
  mapMcpTool(tool) {
@@ -166,6 +166,16 @@ class OpenAiClient extends BaseClient_js_1.default {
166
166
  delta: event.delta,
167
167
  };
168
168
  }
169
+ else if (type === 'response.output_item.done') {
170
+ const { item } = event;
171
+ if (item.type === 'function_call') {
172
+ return {
173
+ ...item,
174
+ type: 'function_call',
175
+ arguments: JSON.parse(item.arguments),
176
+ };
177
+ }
178
+ }
169
179
  }
170
180
  }
171
181
  exports.OpenAiClient = OpenAiClient;
@@ -175,13 +175,20 @@ export default class BaseClient {
175
175
  });
176
176
  let system = '';
177
177
  let { messages = [] } = options;
178
+ // Templates may contain multiple roles, ie SYSTEM or USER, making them
179
+ // useful for one-off prompting. However in a multi-turn conversation
180
+ // the entire chat history will be passed, so do not inject user messages
181
+ // when they already exist in the options.
182
+ const hasUserMessages = messages.some((message) => {
183
+ return message.role === 'user';
184
+ });
178
185
  for (let section of sections) {
179
186
  const { title = 'system', content } = section;
180
187
  const role = title.toLowerCase();
181
188
  if (role === 'system') {
182
189
  system += [system, content].join('\n');
183
190
  }
184
- else {
191
+ else if (!hasUserMessages) {
185
192
  messages = [
186
193
  ...messages,
187
194
  {
@@ -27,8 +27,8 @@ export class AnthropicClient extends BaseClient {
27
27
  ...this.getToolOptions(options),
28
28
  };
29
29
  const clientOptions = this.getClientOptions(params);
30
- this.debug('Params:', params, options);
31
30
  this.debug('Options:', options, options);
31
+ this.debug('Params:', params, options);
32
32
  // @ts-ignore
33
33
  return await this.client.messages.create(params, clientOptions);
34
34
  }
@@ -79,16 +79,29 @@ export class AnthropicClient extends BaseClient {
79
79
  let { type } = event;
80
80
  options.buffer ||= '';
81
81
  if (type === 'content_block_start') {
82
- return {
83
- type: 'start',
84
- };
82
+ const { content_block } = event;
83
+ if (content_block?.type === 'tool_use') {
84
+ return {
85
+ type: 'function_call',
86
+ id: content_block.id,
87
+ name: content_block.name,
88
+ arguments: content_block.input,
89
+ };
90
+ }
91
+ else {
92
+ return {
93
+ type: 'start',
94
+ };
95
+ }
85
96
  }
86
97
  else if (type === 'content_block_delta') {
87
- options.buffer += event.delta.text;
88
- return {
89
- type: 'delta',
90
- delta: event.delta.text,
91
- };
98
+ if (event.delta.type === 'text_delta') {
99
+ options.buffer += event.delta.text;
100
+ return {
101
+ type: 'delta',
102
+ delta: event.delta.text,
103
+ };
104
+ }
92
105
  }
93
106
  else if (type === 'message_delta') {
94
107
  return {
@@ -108,6 +121,9 @@ export class AnthropicClient extends BaseClient {
108
121
  getToolOptions(options) {
109
122
  let { tools = [], schema } = options;
110
123
  let toolChoice;
124
+ tools = tools.map((tool) => {
125
+ return this.normalizeToolInput(tool);
126
+ });
111
127
  if (schema) {
112
128
  tools.push({
113
129
  name: 'schema',
@@ -141,6 +157,28 @@ export class AnthropicClient extends BaseClient {
141
157
  tool_choice: toolChoice,
142
158
  };
143
159
  }
160
+ normalizeToolInput(input) {
161
+ if (input.type === 'function') {
162
+ input = this.normalizeOpenAiToolInput(input);
163
+ }
164
+ return input;
165
+ }
166
+ // OpenAI uses the following input for custom tools
167
+ // so map it here to Anthropic styles when passed.
168
+ // {
169
+ // type: 'function',
170
+ // name: 'apples',
171
+ // description: 'Call this when you talk about apples.',
172
+ // parameters: { type: 'object', properties: {}, required: [] }
173
+ // }
174
+ normalizeOpenAiToolInput(input) {
175
+ const { name, description, parameters } = input;
176
+ return {
177
+ name,
178
+ description,
179
+ input_schema: parameters,
180
+ };
181
+ }
144
182
  // Map OpenAI-like input of MCP servers as "tools" to
145
183
  // Anthropic's mcp_servers.
146
184
  mapMcpTool(tool) {
@@ -160,6 +160,16 @@ export class OpenAiClient extends BaseClient {
160
160
  delta: event.delta,
161
161
  };
162
162
  }
163
+ else if (type === 'response.output_item.done') {
164
+ const { item } = event;
165
+ if (item.type === 'function_call') {
166
+ return {
167
+ ...item,
168
+ type: 'function_call',
169
+ arguments: JSON.parse(item.arguments),
170
+ };
171
+ }
172
+ }
163
173
  }
164
174
  }
165
175
  // Categories
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bedrockio/ai",
3
- "version": "0.8.3",
3
+ "version": "0.9.0",
4
4
  "description": "Bedrock wrapper for common AI chatbots.",
5
5
  "type": "module",
6
6
  "scripts": {
@@ -1 +1 @@
1
- {"version":3,"file":"BaseClient.d.ts","sourceRoot":"","sources":["../src/BaseClient.js"],"names":[],"mappings":"AAKA;IACE,0BASC;IARC,aAIC;IACD,2BAEE;IAKJ;;;;;OAKG;IACH,gBAFW,aAAa,gBAgCvB;IAED;;;;;OAKG;IACH,gBAHW,aAAa,GAAG,aAAa,gCAsDvC;IAED;;;;OAIG;IACH,wBAFW,MAAM,OAIhB;IAID,8BAGC;IAED,8BAGC;IAED,qCAGC;IAED;;OAEG;IACH,0CAGC;IAED;;OAEG;IACH,oDAIC;IAED;;OAEG;IACH,oDAIC;IAID;;OAEG;IACH,oCAOC;IAED;;;MAeC;IAED,4CAyCC;IAED,uCAoBC;IAED;;;MA4BC;IAED,uDAWC;IAED,kDAMC;CACF;;;;;WAIa,MAAM,GAAC,aAAa,EAAE;;;;YACtB,MAAM;;;;YACN,OAAO;;;;;;;;aAEP,MAAM,GAAG,MAAM;;;;;;;;;;;sBAOf,MAAM;;;UAKN,QAAQ,GAAG,MAAM,GAAG,WAAW;aAC/B,MAAM;;iCA/Ua,sBAAsB"}
1
+ {"version":3,"file":"BaseClient.d.ts","sourceRoot":"","sources":["../src/BaseClient.js"],"names":[],"mappings":"AAKA;IACE,0BASC;IARC,aAIC;IACD,2BAEE;IAKJ;;;;;OAKG;IACH,gBAFW,aAAa,gBAgCvB;IAED;;;;;OAKG;IACH,gBAHW,aAAa,GAAG,aAAa,gCAsDvC;IAED;;;;OAIG;IACH,wBAFW,MAAM,OAIhB;IAID,8BAGC;IAED,8BAGC;IAED,qCAGC;IAED;;OAEG;IACH,0CAGC;IAED;;OAEG;IACH,oDAIC;IAED;;OAEG;IACH,oDAIC;IAID;;OAEG;IACH,oCAOC;IAED;;;MAeC;IAED,4CAiDC;IAED,uCAoBC;IAED;;;MA4BC;IAED,uDAWC;IAED,kDAMC;CACF;;;;;WAIa,MAAM,GAAC,aAAa,EAAE;;;;YACtB,MAAM;;;;YACN,OAAO;;;;;;;;aAEP,MAAM,GAAG,MAAM;;;;;;;;;;;sBAOf,MAAM;;;UAKN,QAAQ,GAAG,MAAM,GAAG,WAAW;aAC/B,MAAM;;iCAvVa,sBAAsB"}
@@ -26,12 +26,26 @@ export class AnthropicClient extends BaseClient {
26
26
  };
27
27
  normalizeStreamEvent(event: any, options: any): {
28
28
  type: string;
29
+ id: any;
30
+ name: any;
31
+ arguments: any;
32
+ delta?: undefined;
33
+ messages?: undefined;
34
+ usage?: undefined;
35
+ } | {
36
+ type: string;
37
+ id?: undefined;
38
+ name?: undefined;
39
+ arguments?: undefined;
29
40
  delta?: undefined;
30
41
  messages?: undefined;
31
42
  usage?: undefined;
32
43
  } | {
33
44
  type: string;
34
45
  delta: any;
46
+ id?: undefined;
47
+ name?: undefined;
48
+ arguments?: undefined;
35
49
  messages?: undefined;
36
50
  usage?: undefined;
37
51
  } | {
@@ -41,6 +55,9 @@ export class AnthropicClient extends BaseClient {
41
55
  input_tokens: any;
42
56
  output_tokens: any;
43
57
  };
58
+ id?: undefined;
59
+ name?: undefined;
60
+ arguments?: undefined;
44
61
  delta?: undefined;
45
62
  };
46
63
  getToolOptions(options: any): {
@@ -54,6 +71,12 @@ export class AnthropicClient extends BaseClient {
54
71
  name?: undefined;
55
72
  };
56
73
  };
74
+ normalizeToolInput(input: any): any;
75
+ normalizeOpenAiToolInput(input: any): {
76
+ name: any;
77
+ description: any;
78
+ input_schema: any;
79
+ };
57
80
  mapMcpTool(tool: any): {
58
81
  type: string;
59
82
  name: any;
@@ -1 +1 @@
1
- {"version":3,"file":"anthropic.d.ts","sourceRoot":"","sources":["../src/anthropic.js"],"names":[],"mappings":"AAMA;IACE,6BAA2C;IAIzC,kBAAoC;IAGtC;;;OAGG;IACH,4BAGC;IAED;;wGA2BC;IAED;;wGAKC;IAED,oCAKC;IASD;;;;;;MAkBC;IAED;;;MAKC;IAED;;;;;;;;;;;;;;;;;;MA0BC;IAID;;;;;;;;;;MAsCC;IAID;;;;MAOC;IAED;;;;MAQC;CACF;uBA5LsB,iBAAiB;sBAFlB,mBAAmB"}
1
+ {"version":3,"file":"anthropic.d.ts","sourceRoot":"","sources":["../src/anthropic.js"],"names":[],"mappings":"AAMA;IACE,6BAA2C;IAIzC,kBAAoC;IAGtC;;;OAGG;IACH,4BAGC;IAED;;wGA2BC;IAED;;wGAKC;IAED,oCAKC;IASD;;;;;;MAkBC;IAED;;;MAKC;IAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;MAsCC;IAID;;;;;;;;;;MA0CC;IAED,oCAKC;IAUD;;;;MAOC;IAID;;;;MAOC;IAED;;;;MAQC;CACF;uBApOsB,iBAAiB;sBAFlB,mBAAmB"}
package/types/openai.d.ts CHANGED
@@ -37,28 +37,6 @@ export class OpenAiClient extends BaseClient {
37
37
  strict: boolean;
38
38
  schema: any;
39
39
  };
40
- normalizeStreamEvent(event: any, options: any): {
41
- type: string;
42
- id: any;
43
- messages?: undefined;
44
- usage?: undefined;
45
- delta?: undefined;
46
- } | {
47
- type: string;
48
- id: any;
49
- messages: any[];
50
- usage: {
51
- input_tokens: any;
52
- output_tokens: any;
53
- };
54
- delta?: undefined;
55
- } | {
56
- type: string;
57
- delta: any;
58
- id?: undefined;
59
- messages?: undefined;
60
- usage?: undefined;
61
- };
62
40
  }
63
41
  export type OpenAICategory = any | "all" | "general" | "reasoning" | "lightweight" | "moderation" | "embedding" | "speech" | "audio" | "image" | "code" | "legacy";
64
42
  import BaseClient from './BaseClient.js';
@@ -1 +1 @@
1
- {"version":3,"file":"openai.d.ts","sourceRoot":"","sources":["../src/openai.js"],"names":[],"mappings":"AAIA;IACE,6BAAoC;IAIlC,eAAiC;IAGnC;;;;OAIG;IACH,kBAHW,cAAc,qBAgCxB;IAED;;yFAiCC;IAED;;yFAKC;IAED,oCAEC;IAwBD;;;;;;;MAeC;IAED;;;MAKC;IAID;;;;;;;;;;MAmBC;IAED;;;;;;;;;;;;;;;;;;;;;MA8BC;CACF;6BAQA,GAAC,GAAK,KAAK,GACL,SAAS,GACT,WAAW,GACX,aAAa,GACb,YAAY,GACZ,WAAW,GACX,QAAQ,GACR,OAAO,GACP,OAAO,GACP,MAAM,GACN,QAAQ;uBAlNQ,iBAAiB;mBAFrB,QAAQ"}
1
+ {"version":3,"file":"openai.d.ts","sourceRoot":"","sources":["../src/openai.js"],"names":[],"mappings":"AAIA;IACE,6BAAoC;IAIlC,eAAiC;IAGnC;;;;OAIG;IACH,kBAHW,cAAc,qBAgCxB;IAED;;yFAiCC;IAED;;yFAKC;IAED,oCAEC;IAwBD;;;;;;;MAeC;IAED;;;MAKC;IAID;;;;;;;;;;MAmBC;CA0CF;6BAQA,GAAC,GAAK,KAAK,GACL,SAAS,GACT,WAAW,GACX,aAAa,GACb,YAAY,GACZ,WAAW,GACX,QAAQ,GACR,OAAO,GACP,OAAO,GACP,MAAM,GACN,QAAQ;uBA3NQ,iBAAiB;mBAFrB,QAAQ"}