@bedrockio/ai 0.3.0 → 0.4.2

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.
Files changed (60) hide show
  1. package/.claude/settings.local.json +11 -0
  2. package/CHANGELOG.md +21 -0
  3. package/README.md +58 -17
  4. package/__mocks__/@anthropic-ai/sdk.js +16 -22
  5. package/__mocks__/@google/generative-ai.js +1 -1
  6. package/__mocks__/openai.js +33 -28
  7. package/dist/cjs/BaseClient.js +242 -182
  8. package/dist/cjs/anthropic.js +115 -93
  9. package/dist/cjs/google.js +74 -80
  10. package/dist/cjs/index.js +23 -75
  11. package/dist/cjs/openai.js +114 -72
  12. package/dist/cjs/package.json +1 -0
  13. package/dist/cjs/utils/code.js +11 -0
  14. package/dist/cjs/utils/json.js +53 -0
  15. package/dist/cjs/utils/templates.js +83 -0
  16. package/dist/cjs/xai.js +11 -20
  17. package/dist/esm/BaseClient.js +243 -0
  18. package/dist/esm/anthropic.js +116 -0
  19. package/dist/esm/google.js +75 -0
  20. package/dist/esm/index.js +25 -0
  21. package/dist/esm/openai.js +113 -0
  22. package/dist/esm/utils/code.js +8 -0
  23. package/dist/esm/utils/json.js +50 -0
  24. package/dist/esm/utils/templates.js +76 -0
  25. package/dist/esm/xai.js +10 -0
  26. package/eslint.config.js +2 -0
  27. package/package.json +18 -17
  28. package/src/BaseClient.js +233 -140
  29. package/src/anthropic.js +96 -56
  30. package/src/google.js +3 -6
  31. package/src/index.js +6 -54
  32. package/src/openai.js +96 -33
  33. package/src/utils/code.js +9 -0
  34. package/src/utils/json.js +58 -0
  35. package/src/utils/templates.js +87 -0
  36. package/src/xai.js +2 -9
  37. package/tsconfig.cjs.json +8 -0
  38. package/tsconfig.esm.json +8 -0
  39. package/tsconfig.types.json +9 -0
  40. package/types/BaseClient.d.ts +67 -26
  41. package/types/BaseClient.d.ts.map +1 -1
  42. package/types/anthropic.d.ts +26 -2
  43. package/types/anthropic.d.ts.map +1 -1
  44. package/types/google.d.ts.map +1 -1
  45. package/types/index.d.ts +4 -11
  46. package/types/index.d.ts.map +1 -1
  47. package/types/openai.d.ts +45 -2
  48. package/types/openai.d.ts.map +1 -1
  49. package/types/utils/code.d.ts +2 -0
  50. package/types/utils/code.d.ts.map +1 -0
  51. package/types/utils/json.d.ts +2 -0
  52. package/types/utils/json.d.ts.map +1 -0
  53. package/types/utils/templates.d.ts +3 -0
  54. package/types/utils/templates.d.ts.map +1 -0
  55. package/types/utils.d.ts +4 -0
  56. package/types/utils.d.ts.map +1 -0
  57. package/types/xai.d.ts.map +1 -1
  58. package/vitest.config.js +10 -0
  59. package/dist/cjs/util.js +0 -62
  60. package/src/util.js +0 -60
@@ -0,0 +1,11 @@
1
+ {
2
+ "permissions": {
3
+ "allow": [
4
+ "WebFetch(domain:ai-sdk.dev)",
5
+ "WebSearch",
6
+ "Bash(yarn build:*)"
7
+ ],
8
+ "deny": [],
9
+ "ask": []
10
+ }
11
+ }
package/CHANGELOG.md CHANGED
@@ -1,3 +1,24 @@
1
+ ## 0.4.2
2
+
3
+ - Exclude keys from tarball.
4
+
5
+ ## 0.4.1
6
+
7
+ - No error on missing API key.
8
+
9
+ ## 0.4.0
10
+
11
+ - Rewrote OpenAI to use new responses format.
12
+ - Allow structured responses.
13
+ - Allow yada or JSON schema.
14
+ - Allow extracting partial JSON when streaming.
15
+ - Using `template` option only now.
16
+ - Allow specifying model in client options.
17
+ - Store the previous response id for OpenAI.
18
+ - Removed MultiClient for now.
19
+ - Client -> createClient.
20
+ - Added debug feature.
21
+
1
22
  ## 0.3.0
2
23
 
3
24
  - Added MultiClient.
package/README.md CHANGED
@@ -20,9 +20,10 @@ yarn install @bedrockio/ai
20
20
  ## Usage
21
21
 
22
22
  ```js
23
- import { Client } from '@bedrockio/ai';
23
+ import yd from '@bedrockio/yada';
24
+ import { createClient } from '@bedrockio/ai';
24
25
 
25
- const client = new Client({
26
+ const client = createClient({
26
27
  // Directory to templates
27
28
  templates: './test/templates',
28
29
  // Platform: openai|gpt|anthopic|claude
@@ -33,13 +34,18 @@ const client = new Client({
33
34
 
34
35
  // Get a one time response.
35
36
  const response = await client.prompt({
36
- // The template file to use.
37
- file: 'classify-fruits',
37
+ // The template to use. If no template is found will
38
+ // use this string as the template.
39
+ template: 'classify-fruits',
38
40
  // The form of output. May be raw|text|messages|json.
39
41
  // Default is "text".
40
42
  output: 'json',
41
- // A custom template may be passed if "file" is not.
42
- template: 'custom',
43
+
44
+ // Aa yada schema (or any JSON schema) may be passed
45
+ // here to define structured output.
46
+ schema: yd.object({
47
+ name: yd.string(),
48
+ })
43
49
 
44
50
  // All other variables will be
45
51
  // interpolated into the template.
@@ -55,27 +61,63 @@ Responses may be streamed:
55
61
  ```js
56
62
  // Stream the results
57
63
  const stream = await client.stream({
58
- file: 'classify-fruits',
64
+ template: 'classify-fruits',
65
+
66
+ // See below.
67
+ extractMessages: 'text',
59
68
  });
60
69
 
61
70
  // Will return an AsyncIterator
62
- for await (const chunk of stream) {
63
- console.info(chunk.text);
71
+ for await (const event of stream) {
72
+ console.info(event.text);
64
73
  }
65
74
  ```
66
75
 
67
- ## Templates
76
+ Event types:
68
77
 
69
- Template files must be markdown (`.md`) and live in your templates directory.
70
- They may be a simple text prompt or delineated roles:
78
+ - `start` - Response has been initiated. This event also contains an `id` field.
79
+ that can be passsed back in as `prevResponseId` (OpenAI/Grok only).
80
+ - `stop` - Response has finished. Contains the `id` field and usage data.
81
+ - `delta`- Main text delta event when a new token is output.
82
+ - `done` - Text has stopped.
83
+ - `extract:delta` - Used with `extractMessages` (see below).
84
+ - `extract:done` - Used with `extractMessages` (see below).
71
85
 
72
- ````
73
- --- SYSTEM ---
86
+ ### Streaming Structured Data
87
+
88
+ Often you want prompt responses to be structured JSON, however you still want to
89
+ stream the user-facing message. In this case use the `extractMessages` option to
90
+ define the key of the structured output you want to stream. When this is defined
91
+ you receive additional `extract:delta` and `extract:done` events. These will
92
+ stream even as the partial JSON data comes in.
93
+
94
+ ### Streaming Notes
74
95
 
75
- This is a list of fruits: {{fruits}}
96
+ Note that in addition to streaming partial data above, there are 2 other valid
97
+ approaches:
76
98
 
77
- --- USER ---
99
+ 1. Send two prompts, one for the message and one for the extracted data. This
100
+ works, however there are edge cases when there needs to correlation between
101
+ the responses. For example when asking the user a "next question" in text but
102
+ extracting the type of question in data, the results may not match depending
103
+ on the LLM temperament. This also will increase token usage.
78
104
 
105
+ 2. Use function calls, ie "tools". This approach seems more appropriate as
106
+ function calls stream separately to text output and can easily be
107
+ multiplexed, however at the time of this writing there seem to me issues with
108
+ ensuring tht the LLM actually uses the correct tools and results have been
109
+ flaky. Depending on the approach this may also increase token usage.
110
+
111
+ For the reasons above currently the most reliable approach to streaming
112
+ structured data is using `extractMessage` to stream the partial JSON response.
113
+
114
+ ## Templates
115
+
116
+ Template files must be markdown (`.md`) and live in your templates directory.
117
+ These will be passed as `instructions`, or the equivalent to the `developer`
118
+ role.
119
+
120
+ ````
79
121
  Which fruit do you think the following input most closely resembles?
80
122
 
81
123
  Please provide your response as a JSON object containing:
@@ -95,7 +137,6 @@ Currently supported platforms:
95
137
 
96
138
  - OpenAI (ChatGPT)
97
139
  - Anthropic (Claude)
98
- - Google (Gemini).
99
140
  - xAi (Grok).
100
141
 
101
142
  ## Models
@@ -1,6 +1,7 @@
1
1
  let mock;
2
+ let models;
2
3
 
3
- function MockAnthropicClient() {
4
+ export default function MockAnthropicClient() {
4
5
  return {
5
6
  messages: {
6
7
  create(options) {
@@ -11,33 +12,26 @@ function MockAnthropicClient() {
11
12
  }
12
13
  },
13
14
  },
15
+ models: {
16
+ list() {
17
+ return {
18
+ data: models,
19
+ };
20
+ },
21
+ },
14
22
  };
15
23
  }
16
24
 
17
- function setResponse(data) {
25
+ export function setResponse(data) {
18
26
  mock = data;
19
27
  }
20
28
 
21
- async function* streamMock() {
22
- const content = mock.content[0].text;
23
- const size = Math.floor(content.length / 3);
24
- const one = content.slice(0, size);
25
- const two = content.slice(size, 2 * size);
26
- const three = content.slice(2 * size);
27
- yield wrapChunk(one, 'content_block_start');
28
- yield wrapChunk(two, 'content_block_delta');
29
- yield wrapChunk(three, 'message_stop');
29
+ export function setModels(data) {
30
+ models = data;
30
31
  }
31
32
 
32
- function wrapChunk(str, type) {
33
- return {
34
- type,
35
- delta: {
36
- text: str,
37
- },
38
- };
33
+ async function* streamMock() {
34
+ for await (let event of mock) {
35
+ yield event;
36
+ }
39
37
  }
40
-
41
- MockAnthropicClient.setResponse = setResponse;
42
-
43
- module.exports = MockAnthropicClient;
@@ -54,6 +54,6 @@ function wrapChunk(str, finish) {
54
54
  }
55
55
 
56
56
  module.exports = {
57
- setResponse,
58
57
  GoogleGenerativeAI: MockGoogleClient,
58
+ setResponse,
59
59
  };
@@ -1,6 +1,7 @@
1
- let mock;
1
+ let models;
2
+ let responses = {};
2
3
 
3
- function MockOpenAiClient() {
4
+ export default function MockOpenAiClient() {
4
5
  return {
5
6
  chat: {
6
7
  completions: {
@@ -8,41 +9,45 @@ function MockOpenAiClient() {
8
9
  if (options.stream) {
9
10
  return streamMock();
10
11
  } else {
11
- return mock;
12
+ return responses['default'];
12
13
  }
13
14
  },
14
15
  },
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
+ },
16
38
  };
17
39
  }
18
40
 
19
- async function* streamMock() {
20
- const content = mock.choices[0].message.content;
21
- const size = Math.floor(content.length / 3);
22
- const one = content.slice(0, size);
23
- const two = content.slice(size, 2 * size);
24
- const three = content.slice(2 * size);
25
- yield wrapChunk(one);
26
- yield wrapChunk(two);
27
- yield wrapChunk(three);
41
+ export function setResponse(data, name = 'default') {
42
+ responses[name] = data;
28
43
  }
29
44
 
30
- function wrapChunk(str) {
31
- return {
32
- choices: [
33
- {
34
- delta: {
35
- content: str,
36
- },
37
- },
38
- ],
39
- };
45
+ export function setModels(data) {
46
+ models = data;
40
47
  }
41
48
 
42
- function setResponse(data) {
43
- mock = data;
49
+ async function* streamMock(response) {
50
+ for await (let event of response) {
51
+ yield event;
52
+ }
44
53
  }
45
-
46
- MockOpenAiClient.setResponse = setResponse;
47
-
48
- module.exports = MockOpenAiClient;