@bedrockio/ai 0.4.2 → 0.5.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,37 +0,0 @@
1
- let mock;
2
- let models;
3
-
4
- export default function MockAnthropicClient() {
5
- return {
6
- messages: {
7
- create(options) {
8
- if (options.stream) {
9
- return streamMock();
10
- } else {
11
- return mock;
12
- }
13
- },
14
- },
15
- models: {
16
- list() {
17
- return {
18
- data: models,
19
- };
20
- },
21
- },
22
- };
23
- }
24
-
25
- export function setResponse(data) {
26
- mock = data;
27
- }
28
-
29
- export function setModels(data) {
30
- models = data;
31
- }
32
-
33
- async function* streamMock() {
34
- for await (let event of mock) {
35
- yield event;
36
- }
37
- }
@@ -1,59 +0,0 @@
1
- let mock;
2
-
3
- class MockGoogleClient {
4
- constructor() {
5
- return {
6
- getGenerativeModel() {
7
- return {
8
- generateContent() {
9
- return mock;
10
- },
11
- generateContentStream() {
12
- return {
13
- stream: streamMock(),
14
- };
15
- },
16
- };
17
- },
18
- };
19
- }
20
- }
21
-
22
- function setResponse(data) {
23
- mock = data;
24
- }
25
-
26
- async function* streamMock() {
27
- const content = mock.response.candidates[0].content.parts[0].text;
28
- const size = Math.floor(content.length / 3);
29
- const one = content.slice(0, size);
30
- const two = content.slice(size, 2 * size);
31
- const three = content.slice(2 * size);
32
- yield wrapChunk(one);
33
- yield wrapChunk(two);
34
- yield wrapChunk(three, true);
35
- }
36
-
37
- function wrapChunk(str, finish) {
38
- return {
39
- candidates: [
40
- {
41
- ...(finish && {
42
- finishReason: 'STOP',
43
- }),
44
- content: {
45
- parts: [
46
- {
47
- text: str,
48
- },
49
- ],
50
- },
51
- },
52
- ],
53
- };
54
- }
55
-
56
- module.exports = {
57
- GoogleGenerativeAI: MockGoogleClient,
58
- setResponse,
59
- };
@@ -1,53 +0,0 @@
1
- let models;
2
- let responses = {};
3
-
4
- export default function MockOpenAiClient() {
5
- return {
6
- chat: {
7
- completions: {
8
- create(options) {
9
- if (options.stream) {
10
- return streamMock();
11
- } else {
12
- return responses['default'];
13
- }
14
- },
15
- },
16
- },
17
- responses: {
18
- create(options) {
19
- const { previous_response_id = 'default' } = options;
20
- if (!options.input) {
21
- throw new Error('Missing parameter "input".');
22
- }
23
- const response = responses[previous_response_id];
24
- if (options.stream) {
25
- return streamMock(response);
26
- } else {
27
- return response;
28
- }
29
- },
30
- },
31
- models: {
32
- list() {
33
- return {
34
- data: models,
35
- };
36
- },
37
- },
38
- };
39
- }
40
-
41
- export function setResponse(data, name = 'default') {
42
- responses[name] = data;
43
- }
44
-
45
- export function setModels(data) {
46
- models = data;
47
- }
48
-
49
- async function* streamMock(response) {
50
- for await (let event of response) {
51
- yield event;
52
- }
53
- }
package/eslint.config.js DELETED
@@ -1,2 +0,0 @@
1
- import { nodeImports, recommended } from '@bedrockio/eslint-plugin';
2
- export default [recommended, nodeImports];
package/src/BaseClient.js DELETED
@@ -1,288 +0,0 @@
1
- import { parseCode } from './utils/code.js';
2
- import { createMessageExtractor } from './utils/json.js';
3
- import { loadTemplates, renderTemplate } from './utils/templates.js';
4
-
5
- export default class BaseClient {
6
- constructor(options) {
7
- this.options = {
8
- // @ts-ignore
9
- model: this.constructor.DEFAULT_MODEL,
10
- ...options,
11
- };
12
- this.templates = null;
13
- }
14
-
15
- // Public
16
-
17
- /**
18
- * Interpolates vars into the provided template as instructions and runs the
19
- * prompt.
20
- *
21
- * @param {PromptOptions} options
22
- */
23
- async prompt(options) {
24
- options = await this.normalizeOptions(options);
25
-
26
- const { input, output, stream, schema } = options;
27
-
28
- const response = await this.runPrompt(options);
29
-
30
- if (!stream) {
31
- this.debug('Response:', response);
32
- }
33
-
34
- if (output === 'raw') {
35
- return response;
36
- }
37
-
38
- let result;
39
- if (schema) {
40
- result = this.getStructuredResponse(response);
41
-
42
- // @ts-ignore
43
- if (options.hasWrappedSchema) {
44
- result = result.items;
45
- }
46
- } else if (output === 'json') {
47
- result = JSON.parse(parseCode(this.getTextResponse(response)));
48
- } else {
49
- result = parseCode(this.getTextResponse(response));
50
- }
51
-
52
- if (output === 'messages') {
53
- return {
54
- result,
55
- ...this.getMessagesResponse(input, response),
56
- };
57
- } else {
58
- return result;
59
- }
60
- }
61
-
62
- /**
63
- * Streams the prompt response.
64
- *
65
- * @param {PromptOptions & StreamOptions} options
66
- * @returns {AsyncIterator}
67
- */
68
- async *stream(options) {
69
- options = await this.normalizeOptions(options);
70
-
71
- const extractor = this.getMessageExtractor(options);
72
-
73
- try {
74
- const stream = await this.runStream(options);
75
-
76
- // @ts-ignore
77
- for await (let event of stream) {
78
- this.debug('Event:', event);
79
-
80
- event = this.normalizeStreamEvent(event);
81
-
82
- if (event) {
83
- yield event;
84
- }
85
-
86
- const extractedMessages = extractor?.(event) || [];
87
-
88
- for (let message of extractedMessages) {
89
- const { key, delta, text, done } = message;
90
-
91
- let extractEvent;
92
- if (done) {
93
- extractEvent = {
94
- type: 'extract:done',
95
- text,
96
- key,
97
- };
98
- } else {
99
- extractEvent = {
100
- type: 'extract:delta',
101
- delta,
102
- key,
103
- };
104
- }
105
-
106
- this.debug('Extract:', extractEvent);
107
-
108
- yield extractEvent;
109
- }
110
- }
111
- } catch (error) {
112
- const { message, code } = error;
113
- yield {
114
- type: 'error',
115
- code,
116
- message,
117
- };
118
- }
119
- }
120
-
121
- async buildTemplate(options) {
122
- const template = await this.resolveTemplate(options);
123
- return renderTemplate(template, options);
124
- }
125
-
126
- // Protected
127
-
128
- runPrompt(options) {
129
- void options;
130
- throw new Error('Method not implemented.');
131
- }
132
-
133
- runStream(options) {
134
- void options;
135
- throw new Error('Method not implemented.');
136
- }
137
-
138
- getTextResponse(response) {
139
- void response;
140
- throw new Error('Method not implemented.');
141
- }
142
-
143
- /**
144
- * @returns {Object}
145
- */
146
- getStructuredResponse(response) {
147
- void response;
148
- throw new Error('Method not implemented.');
149
- }
150
-
151
- /**
152
- * @returns {Object}
153
- */
154
- getMessagesResponse(input, response) {
155
- void response;
156
- throw new Error('Method not implemented.');
157
- }
158
-
159
- /**
160
- * @returns {Object}
161
- */
162
- normalizeStreamEvent(event) {
163
- void event;
164
- throw new Error('Method not implemented.');
165
- }
166
-
167
- // Private
168
-
169
- async normalizeOptions(options) {
170
- options = {
171
- input: '',
172
- output: 'text',
173
- ...this.options,
174
- ...options,
175
- };
176
-
177
- options.input = this.normalizeInput(options);
178
- options.schema = this.normalizeSchema(options);
179
- options.instructions ||= await this.resolveInstructions(options);
180
-
181
- return options;
182
- }
183
-
184
- normalizeInput(options) {
185
- let { input = '', output } = options;
186
-
187
- if (typeof input === 'string') {
188
- if (output === 'json') {
189
- input += '\nOutput only valid JSON.';
190
- }
191
-
192
- input = [
193
- {
194
- role: 'user',
195
- content: input,
196
- },
197
- ];
198
- }
199
-
200
- return input;
201
- }
202
-
203
- normalizeSchema(options) {
204
- let { schema } = options;
205
-
206
- if (!schema) {
207
- return;
208
- }
209
-
210
- // Convert to JSON schema.
211
- schema = schema.toJSON?.() || schema;
212
-
213
- if (schema?.type === 'array') {
214
- schema = {
215
- type: 'object',
216
- properties: {
217
- items: schema,
218
- },
219
- required: ['items'],
220
- additionalProperties: false,
221
- };
222
- options.hasWrappedSchema = true;
223
- }
224
-
225
- return schema;
226
- }
227
-
228
- getMessageExtractor(options) {
229
- const { extractMessages } = options;
230
- if (!extractMessages) {
231
- return;
232
- }
233
- const messageExtractor = createMessageExtractor([extractMessages]);
234
- return (event) => {
235
- if (event?.type === 'delta') {
236
- return messageExtractor(event.delta);
237
- }
238
- };
239
- }
240
-
241
- debug(message, arg) {
242
- if (this.options.debug) {
243
- // TODO: replace with logger when opentelemetry is removed
244
- // eslint-disable-next-line
245
- console.debug(`${message}\n${JSON.stringify(arg, null, 2)}\n`);
246
- }
247
- }
248
-
249
- async resolveInstructions(options) {
250
- if (options.template) {
251
- const template = await this.resolveTemplate(options);
252
- return renderTemplate(template, options);
253
- }
254
- }
255
-
256
- async resolveTemplate(options) {
257
- const { template } = options;
258
- await this.loadTemplates();
259
- return this.templates[template] || template;
260
- }
261
-
262
- async loadTemplates() {
263
- const { templates } = this.options;
264
- this.templates ||= await loadTemplates(templates);
265
- }
266
- }
267
-
268
- /**
269
- * @typedef {Object} PromptOptions
270
- * @property {string|PromptMessage[]} input - Input to use.
271
- * @property {string} [model] - The model to use.
272
- * @property {boolean} stream - Stream response.
273
- * @property {Object} [schema] - A JSON schema compatible object that defines the output shape.
274
- * @property {"raw" | "text" | "json" | "messages"} [output] - The return value type.
275
- * @property {Object} [params] - Params to be interpolated into the template.
276
- * May also be passed as additional props to options.
277
- */
278
-
279
- /**
280
- * @typedef {Object} StreamOptions
281
- * @property {string} [extractMessages] - Key in JSON response to extract a message stream from.
282
- */
283
-
284
- /**
285
- * @typedef {Object} PromptMessage
286
- * @property {"system" | "user" | "assistant"} role
287
- * @property {string} content
288
- */
package/src/anthropic.js DELETED
@@ -1,137 +0,0 @@
1
- import Anthropic from '@anthropic-ai/sdk';
2
-
3
- import BaseClient from './BaseClient.js';
4
-
5
- const DEFAULT_TOKENS = 4096;
6
-
7
- export class AnthropicClient extends BaseClient {
8
- static DEFAULT_MODEL = 'claude-sonnet-4-5';
9
-
10
- constructor(options) {
11
- super(options);
12
- this.client = new Anthropic(options);
13
- }
14
-
15
- /**
16
- * Lists available models.
17
- * {@link https://docs.anthropic.com/en/docs/about-claude/models Documentation}
18
- */
19
- async models() {
20
- const { data } = await this.client.models.list();
21
- return data.map((o) => o.id);
22
- }
23
-
24
- async runPrompt(options) {
25
- const {
26
- input,
27
- model,
28
- temperature,
29
- instructions,
30
- stream = false,
31
- tokens = DEFAULT_TOKENS,
32
- } = options;
33
-
34
- // @ts-ignore
35
- return await this.client.messages.create({
36
- model,
37
- stream,
38
- temperature,
39
- max_tokens: tokens,
40
- system: instructions,
41
- ...this.getSchemaOptions(options),
42
- messages: input,
43
- });
44
- }
45
-
46
- async runStream(options) {
47
- return await this.runPrompt({
48
- ...options,
49
- output: 'raw',
50
- stream: true,
51
- });
52
- }
53
-
54
- getTextResponse(response) {
55
- const textBlock = response.content.find((block) => {
56
- return block.type === 'text';
57
- });
58
- return textBlock?.text || null;
59
- }
60
-
61
- getStructuredResponse(response) {
62
- const toolBlock = response.content.find((block) => {
63
- return block.type === 'tool_use';
64
- });
65
- return toolBlock?.input || null;
66
- }
67
-
68
- getMessagesResponse(input, response) {
69
- return {
70
- messages: [
71
- ...input,
72
- ...response.content
73
- .filter((item) => {
74
- return item.type === 'text';
75
- })
76
- .map((item) => {
77
- return {
78
- role: 'assistant',
79
- content: item.text,
80
- };
81
- }),
82
- ],
83
- };
84
- }
85
-
86
- normalizeStreamEvent(event) {
87
- let { type } = event;
88
- if (type === 'content_block_start') {
89
- return {
90
- type: 'start',
91
- };
92
- } else if (type === 'content_block_stop') {
93
- return {
94
- type: 'stop',
95
- };
96
- } else if (type === 'content_block_delta') {
97
- return {
98
- type: 'delta',
99
- text: event.delta.text,
100
- };
101
- }
102
- }
103
-
104
- // Private
105
-
106
- getSchemaOptions(options) {
107
- const { output } = options;
108
- if (output?.type) {
109
- let schema = output;
110
-
111
- if (schema.type === 'array') {
112
- schema = {
113
- type: 'object',
114
- properties: {
115
- items: schema,
116
- },
117
- required: ['items'],
118
- additionalProperties: false,
119
- };
120
- }
121
-
122
- return {
123
- tools: [
124
- {
125
- name: 'schema',
126
- description: 'Follow the schema for JSON output.',
127
- input_schema: schema,
128
- },
129
- ],
130
- tool_choice: {
131
- type: 'tool',
132
- name: 'schema',
133
- },
134
- };
135
- }
136
- }
137
- }
package/src/google.js DELETED
@@ -1,88 +0,0 @@
1
- import { GoogleGenerativeAI } from '@google/generative-ai';
2
-
3
- import BaseClient from './BaseClient.js';
4
-
5
- const DEFAULT_MODEL = 'models/gemini-2.0-flash-exp';
6
-
7
- export class GoogleClient extends BaseClient {
8
- constructor(options) {
9
- super(options);
10
- const { apiKey } = options;
11
- this.client = new GoogleGenerativeAI(apiKey);
12
- }
13
-
14
- /**
15
- * Lists available models.
16
- * {@link https://ai.google.dev/gemini-api/docs/models/gemini#gemini-2.0-flashl Documentation}
17
- */
18
- async models() {
19
- return [
20
- 'gemini-2.0-flash-exp',
21
- 'gemini-1.5-flash',
22
- 'gemini-1.5-flash-8b',
23
- 'gemini-1.5-pro',
24
- ];
25
- }
26
-
27
- async getCompletion(options) {
28
- const { model = DEFAULT_MODEL, output = 'text', stream = false } = options;
29
- const { client } = this;
30
-
31
- const generator = client.getGenerativeModel({
32
- model,
33
- });
34
-
35
- // @ts-ignore
36
- const messages = await this.getMessages(options);
37
-
38
- const prompts = messages.map((message) => {
39
- return message.content;
40
- });
41
-
42
- let response;
43
-
44
- if (stream) {
45
- response = await generator.generateContentStream(prompts);
46
- } else {
47
- response = await generator.generateContent(prompts);
48
- }
49
-
50
- if (output === 'raw') {
51
- return response;
52
- }
53
-
54
- // @ts-ignore
55
- const parts = response.response.candidates.flatMap((candidate) => {
56
- return candidate.content.parts;
57
- });
58
- const [message] = parts;
59
-
60
- return message;
61
- }
62
- async getStream(options) {
63
- // @ts-ignore
64
- const response = await super.getStream(options);
65
- // @ts-ignore
66
- return response.stream;
67
- }
68
-
69
- getStreamedChunk(chunk, started) {
70
- const [candidate] = chunk.candidates;
71
-
72
- let type;
73
- if (!started) {
74
- type = 'start';
75
- } else if (candidate.finishReason === 'STOP') {
76
- type = 'stop';
77
- } else {
78
- type = 'chunk';
79
- }
80
-
81
- if (type) {
82
- return {
83
- type,
84
- text: candidate.content.parts[0].text || '',
85
- };
86
- }
87
- }
88
- }
package/src/index.js DELETED
@@ -1,24 +0,0 @@
1
- import { AnthropicClient } from './anthropic.js';
2
- import { GoogleClient } from './google.js';
3
- import { OpenAiClient } from './openai.js';
4
- import { XAiClient } from './xai.js';
5
-
6
- export function createClient(options = {}) {
7
- const { platform } = options;
8
-
9
- if (!platform) {
10
- throw new Error('No platform specified.');
11
- }
12
-
13
- if (platform === 'openai' || platform === 'gpt') {
14
- return new OpenAiClient(options);
15
- } else if (platform === 'google' || platform === 'gemini') {
16
- return new GoogleClient(options);
17
- } else if (platform === 'anthropic' || platform === 'claude') {
18
- return new AnthropicClient(options);
19
- } else if (platform === 'xai' || platform === 'grok') {
20
- return new XAiClient(options);
21
- } else if (platform) {
22
- throw new Error(`Unknown platform "${platform}".`);
23
- }
24
- }