@bedrockio/ai 0.2.1 → 0.3.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 +13 -0
- package/README.md +1 -0
- package/dist/cjs/BaseClient.js +84 -28
- package/dist/cjs/google.js +0 -6
- package/dist/cjs/index.js +65 -14
- package/dist/cjs/openai.js +5 -2
- package/dist/cjs/util.js +26 -11
- package/dist/cjs/xai.js +23 -0
- package/package.json +3 -3
- package/src/BaseClient.js +92 -35
- package/src/google.js +3 -6
- package/src/index.js +63 -11
- package/src/openai.js +4 -1
- package/src/util.js +28 -10
- package/src/xai.js +19 -0
- package/types/BaseClient.d.ts +1 -0
- package/types/BaseClient.d.ts.map +1 -1
- package/types/google.d.ts.map +1 -1
- package/types/index.d.ts +8 -0
- package/types/index.d.ts.map +1 -1
- package/types/openai.d.ts.map +1 -1
- package/types/util.d.ts +1 -1
- package/types/util.d.ts.map +1 -1
- package/types/xai.d.ts +4 -0
- package/types/xai.d.ts.map +1 -0
package/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,16 @@
|
|
|
1
|
+
## 0.3.0
|
|
2
|
+
|
|
3
|
+
- Added MultiClient.
|
|
4
|
+
- Allow partial template interpolation.
|
|
5
|
+
- Changed default openai model to `gpt-4o-mini`.
|
|
6
|
+
- Allow passing in specific `params` object.
|
|
7
|
+
- Allow passing in complex arrays.
|
|
8
|
+
|
|
9
|
+
## 0.2.1
|
|
10
|
+
|
|
11
|
+
- Better error handling for entry.
|
|
12
|
+
- Allow parsing of unknown code.
|
|
13
|
+
|
|
1
14
|
## 0.2.0
|
|
2
15
|
|
|
3
16
|
- Added Gemini
|
package/README.md
CHANGED
package/dist/cjs/BaseClient.js
CHANGED
|
@@ -58,23 +58,39 @@ class BaseClient {
|
|
|
58
58
|
}
|
|
59
59
|
}
|
|
60
60
|
async getMessages(options) {
|
|
61
|
+
const {
|
|
62
|
+
text
|
|
63
|
+
} = options;
|
|
61
64
|
const template = await this.resolveTemplate(options);
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
messages.
|
|
65
|
+
if (template) {
|
|
66
|
+
const raw = render(template, options);
|
|
67
|
+
const messages = [];
|
|
68
|
+
for (let match of raw.matchAll(MESSAGES_REG)) {
|
|
69
|
+
const [, role, content] = match;
|
|
70
|
+
messages.push({
|
|
71
|
+
role: role.toLowerCase(),
|
|
72
|
+
content: content.trim()
|
|
73
|
+
});
|
|
74
|
+
}
|
|
75
|
+
if (!messages.length) {
|
|
76
|
+
messages.push({
|
|
77
|
+
role: 'user',
|
|
78
|
+
content: raw.trim()
|
|
79
|
+
});
|
|
80
|
+
}
|
|
81
|
+
return messages;
|
|
82
|
+
} else if (text) {
|
|
83
|
+
return [{
|
|
73
84
|
role: 'user',
|
|
74
|
-
content:
|
|
75
|
-
}
|
|
85
|
+
content: text
|
|
86
|
+
}];
|
|
87
|
+
} else {
|
|
88
|
+
throw new Error('No input provided.');
|
|
76
89
|
}
|
|
77
|
-
|
|
90
|
+
}
|
|
91
|
+
async buildTemplate(options) {
|
|
92
|
+
const template = await this.resolveTemplate(options);
|
|
93
|
+
return render(template, options);
|
|
78
94
|
}
|
|
79
95
|
async loadTemplates() {
|
|
80
96
|
const {
|
|
@@ -83,18 +99,18 @@ class BaseClient {
|
|
|
83
99
|
this.templates ||= await (0, _util.loadTemplates)(templates);
|
|
84
100
|
}
|
|
85
101
|
async resolveTemplate(options) {
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
file
|
|
89
|
-
template
|
|
102
|
+
const {
|
|
103
|
+
template,
|
|
104
|
+
file
|
|
90
105
|
} = options;
|
|
91
|
-
if (
|
|
92
|
-
template
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
|
|
106
|
+
if (template) {
|
|
107
|
+
return template;
|
|
108
|
+
} else if (file?.endsWith('.md')) {
|
|
109
|
+
return await (0, _util.loadTemplate)(file);
|
|
110
|
+
} else if (file) {
|
|
111
|
+
await this.loadTemplates();
|
|
112
|
+
return this.templates[file];
|
|
96
113
|
}
|
|
97
|
-
return template;
|
|
98
114
|
}
|
|
99
115
|
async getStream(options) {
|
|
100
116
|
return await this.prompt({
|
|
@@ -114,17 +130,57 @@ class BaseClient {
|
|
|
114
130
|
}
|
|
115
131
|
}
|
|
116
132
|
exports.default = BaseClient;
|
|
117
|
-
function
|
|
133
|
+
function render(template, options) {
|
|
134
|
+
let params = {
|
|
135
|
+
...options,
|
|
136
|
+
...options.params
|
|
137
|
+
};
|
|
138
|
+
params = mapObjects(params);
|
|
139
|
+
params = wrapProxy(params);
|
|
140
|
+
return _mustache.default.render(template, params);
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
// Transform arrays and object to versions
|
|
144
|
+
// that are more understandable in the context
|
|
145
|
+
// of a template that may have meaningful whitespace.
|
|
146
|
+
function mapObjects(params) {
|
|
118
147
|
const result = {};
|
|
119
148
|
for (let [key, value] of Object.entries(params)) {
|
|
120
149
|
if (Array.isArray(value)) {
|
|
121
|
-
value = value
|
|
122
|
-
return `- ${el}`;
|
|
123
|
-
}).join('\n');
|
|
150
|
+
value = mapArray(value);
|
|
124
151
|
} else if (typeof value === 'object') {
|
|
125
152
|
value = JSON.stringify(value, null, 2);
|
|
126
153
|
}
|
|
127
154
|
result[key] = value;
|
|
128
155
|
}
|
|
129
156
|
return result;
|
|
157
|
+
}
|
|
158
|
+
function mapArray(arr) {
|
|
159
|
+
// Only map simple arrays of primitives.
|
|
160
|
+
if (typeof arr[0] === 'string') {
|
|
161
|
+
arr = arr.map(el => {
|
|
162
|
+
return `- ${el}`;
|
|
163
|
+
}).join('\n');
|
|
164
|
+
}
|
|
165
|
+
return arr;
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
// Wrap params with a proxy object that reports
|
|
169
|
+
// as having all properties. If one is accessed
|
|
170
|
+
// that does not exist then return the original
|
|
171
|
+
// token. This way templates can be partially
|
|
172
|
+
// interpolated and re-interpolated later.
|
|
173
|
+
function wrapProxy(params) {
|
|
174
|
+
return new Proxy(params, {
|
|
175
|
+
has() {
|
|
176
|
+
return true;
|
|
177
|
+
},
|
|
178
|
+
get(target, prop) {
|
|
179
|
+
if (prop in target) {
|
|
180
|
+
return target[prop];
|
|
181
|
+
} else {
|
|
182
|
+
return `{{{${prop.toString()}}}}`;
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
});
|
|
130
186
|
}
|
package/dist/cjs/google.js
CHANGED
|
@@ -47,12 +47,6 @@ class GoogleClient extends _BaseClient.default {
|
|
|
47
47
|
} else {
|
|
48
48
|
response = await generator.generateContent(prompts);
|
|
49
49
|
}
|
|
50
|
-
// const response = await client.chat.completions.create({
|
|
51
|
-
// model,
|
|
52
|
-
// messages,
|
|
53
|
-
// stream,
|
|
54
|
-
// });
|
|
55
|
-
|
|
56
50
|
if (output === 'raw') {
|
|
57
51
|
return response;
|
|
58
52
|
}
|
package/dist/cjs/index.js
CHANGED
|
@@ -3,27 +3,78 @@
|
|
|
3
3
|
Object.defineProperty(exports, "__esModule", {
|
|
4
4
|
value: true
|
|
5
5
|
});
|
|
6
|
-
exports.Client = void 0;
|
|
6
|
+
exports.MultiClient = exports.Client = void 0;
|
|
7
7
|
var _openai = require("./openai.js");
|
|
8
8
|
var _google = require("./google.js");
|
|
9
9
|
var _anthropic = require("./anthropic.js");
|
|
10
|
+
var _xai = require("./xai.js");
|
|
10
11
|
class Client {
|
|
12
|
+
constructor(options = {}) {
|
|
13
|
+
if (!options.platform) {
|
|
14
|
+
throw new Error('No platform specified.');
|
|
15
|
+
} else if (!options.templates) {
|
|
16
|
+
throw new Error('No templates directory specified.');
|
|
17
|
+
} else if (!options.apiKey) {
|
|
18
|
+
throw new Error('No API key specified.');
|
|
19
|
+
}
|
|
20
|
+
return getClientForPlatform(options);
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
exports.Client = Client;
|
|
24
|
+
class MultiClient {
|
|
11
25
|
constructor(options) {
|
|
12
26
|
const {
|
|
13
|
-
|
|
14
|
-
...rest
|
|
27
|
+
platforms
|
|
15
28
|
} = options;
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
29
|
+
this.clients = {};
|
|
30
|
+
for (let platform of platforms) {
|
|
31
|
+
const {
|
|
32
|
+
name,
|
|
33
|
+
apiKey
|
|
34
|
+
} = platform;
|
|
35
|
+
const client = getClientForPlatform({
|
|
36
|
+
...options,
|
|
37
|
+
platform: name,
|
|
38
|
+
apiKey
|
|
39
|
+
});
|
|
40
|
+
this.clients[name] = client;
|
|
41
|
+
this.clients[undefined] ||= client;
|
|
26
42
|
}
|
|
27
43
|
}
|
|
44
|
+
prompt(options) {
|
|
45
|
+
return this.getClient(options).prompt(options);
|
|
46
|
+
}
|
|
47
|
+
stream(options) {
|
|
48
|
+
return this.getClient(options).stream(options);
|
|
49
|
+
}
|
|
50
|
+
buildTemplate(options) {
|
|
51
|
+
return this.getClient(options).buildTemplate(options);
|
|
52
|
+
}
|
|
53
|
+
getClient(options) {
|
|
54
|
+
const {
|
|
55
|
+
platform
|
|
56
|
+
} = options;
|
|
57
|
+
const client = this.clients[platform];
|
|
58
|
+
if (!client) {
|
|
59
|
+
throw new Error(`Platform "${platform}" not found.`);
|
|
60
|
+
}
|
|
61
|
+
return client;
|
|
62
|
+
}
|
|
28
63
|
}
|
|
29
|
-
exports.
|
|
64
|
+
exports.MultiClient = MultiClient;
|
|
65
|
+
function getClientForPlatform(options) {
|
|
66
|
+
const {
|
|
67
|
+
platform
|
|
68
|
+
} = options;
|
|
69
|
+
if (platform === 'openai' || platform === 'gpt') {
|
|
70
|
+
return new _openai.OpenAiClient(options);
|
|
71
|
+
} else if (platform === 'google' || platform === 'gemini') {
|
|
72
|
+
return new _google.GoogleClient(options);
|
|
73
|
+
} else if (platform === 'anthropic' || platform === 'claude') {
|
|
74
|
+
return new _anthropic.AnthropicClient(options);
|
|
75
|
+
} else if (platform === 'xai' || platform === 'grok') {
|
|
76
|
+
return new _xai.XAiClient(options);
|
|
77
|
+
} else if (platform) {
|
|
78
|
+
throw new Error(`Unknown platform "${platform}".`);
|
|
79
|
+
}
|
|
80
|
+
}
|
package/dist/cjs/openai.js
CHANGED
|
@@ -8,7 +8,7 @@ var _openai = _interopRequireDefault(require("openai"));
|
|
|
8
8
|
var _BaseClient = _interopRequireDefault(require("./BaseClient.js"));
|
|
9
9
|
var _util = require("./util.js");
|
|
10
10
|
function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
|
|
11
|
-
const DEFAULT_MODEL = 'gpt-4o';
|
|
11
|
+
const DEFAULT_MODEL = 'gpt-4o-mini';
|
|
12
12
|
class OpenAiClient extends _BaseClient.default {
|
|
13
13
|
constructor(options) {
|
|
14
14
|
super(options);
|
|
@@ -40,7 +40,10 @@ class OpenAiClient extends _BaseClient.default {
|
|
|
40
40
|
const response = await client.chat.completions.create({
|
|
41
41
|
model,
|
|
42
42
|
messages,
|
|
43
|
-
stream
|
|
43
|
+
stream,
|
|
44
|
+
response_format: {
|
|
45
|
+
type: output === 'json' ? 'json_object' : 'text'
|
|
46
|
+
}
|
|
44
47
|
});
|
|
45
48
|
if (output === 'raw') {
|
|
46
49
|
return response;
|
package/dist/cjs/util.js
CHANGED
|
@@ -3,30 +3,29 @@
|
|
|
3
3
|
Object.defineProperty(exports, "__esModule", {
|
|
4
4
|
value: true
|
|
5
5
|
});
|
|
6
|
+
exports.loadTemplate = loadTemplate;
|
|
6
7
|
exports.loadTemplates = loadTemplates;
|
|
7
|
-
exports.parse = parse;
|
|
8
8
|
exports.transformResponse = transformResponse;
|
|
9
9
|
var _promises = _interopRequireDefault(require("fs/promises"));
|
|
10
10
|
var _path = _interopRequireDefault(require("path"));
|
|
11
11
|
var _glob = require("glob");
|
|
12
12
|
function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
|
|
13
|
+
const CODE_REG = /^```\w*$(.+)```/ms;
|
|
13
14
|
const JSON_REG = /([{[].+[}\]])/s;
|
|
14
15
|
async function loadTemplates(dir) {
|
|
15
16
|
const result = {};
|
|
16
17
|
const files = await (0, _glob.glob)(_path.default.join(dir, '*.md'));
|
|
18
|
+
if (!files.length) {
|
|
19
|
+
throw new Error(`No templates found in: ${dir}.`);
|
|
20
|
+
}
|
|
17
21
|
for (let file of files) {
|
|
18
22
|
const base = _path.default.basename(file, '.md');
|
|
19
|
-
result[base] = await
|
|
23
|
+
result[base] = await loadTemplate(file);
|
|
20
24
|
}
|
|
21
25
|
return result;
|
|
22
26
|
}
|
|
23
|
-
function
|
|
24
|
-
|
|
25
|
-
const match = content.match(JSON_REG);
|
|
26
|
-
return JSON.parse(match[1]);
|
|
27
|
-
} catch (error) {
|
|
28
|
-
throw new Error('Unable to derive JSON object in response.');
|
|
29
|
-
}
|
|
27
|
+
async function loadTemplate(file) {
|
|
28
|
+
return await _promises.default.readFile(file, 'utf-8');
|
|
30
29
|
}
|
|
31
30
|
function transformResponse(options) {
|
|
32
31
|
const {
|
|
@@ -40,8 +39,24 @@ function transformResponse(options) {
|
|
|
40
39
|
} else if (output === 'messages') {
|
|
41
40
|
return [...messages, message];
|
|
42
41
|
} else if (output === 'json') {
|
|
43
|
-
return
|
|
42
|
+
return parseJson(content);
|
|
43
|
+
} else if (output === 'code') {
|
|
44
|
+
return parseCode(content);
|
|
44
45
|
} else {
|
|
45
|
-
throw new Error(`
|
|
46
|
+
throw new Error(`No output type provided.`);
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
function parseJson(content) {
|
|
50
|
+
try {
|
|
51
|
+
return JSON.parse(content.match(JSON_REG)[0]);
|
|
52
|
+
} catch (error) {
|
|
53
|
+
throw new Error(`Unable to derive JSON from response:\n\n${content}`);
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
function parseCode(content) {
|
|
57
|
+
try {
|
|
58
|
+
return content.match(CODE_REG)[1].trim();
|
|
59
|
+
} catch (error) {
|
|
60
|
+
throw new Error(`Unable to derive code from response:\n\n${content}`);
|
|
46
61
|
}
|
|
47
62
|
}
|
package/dist/cjs/xai.js
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
exports.XAiClient = void 0;
|
|
7
|
+
var _openai = require("./openai.js");
|
|
8
|
+
const DEFAULT_MODEL = 'grok-2-1212';
|
|
9
|
+
class XAiClient extends _openai.OpenAiClient {
|
|
10
|
+
constructor(options) {
|
|
11
|
+
super({
|
|
12
|
+
...options,
|
|
13
|
+
baseURL: 'https://api.x.ai/v1'
|
|
14
|
+
});
|
|
15
|
+
}
|
|
16
|
+
async getCompletion(options) {
|
|
17
|
+
return super.getCompletion({
|
|
18
|
+
model: DEFAULT_MODEL,
|
|
19
|
+
...options
|
|
20
|
+
});
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
exports.XAiClient = XAiClient;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@bedrockio/ai",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.3.0",
|
|
4
4
|
"description": "Bedrock wrapper for common AI chatbots.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"scripts": {
|
|
@@ -31,11 +31,11 @@
|
|
|
31
31
|
"url": "https://github.com/bedrockio/router"
|
|
32
32
|
},
|
|
33
33
|
"dependencies": {
|
|
34
|
-
"@anthropic-ai/sdk": "^0.
|
|
34
|
+
"@anthropic-ai/sdk": "^0.36.3",
|
|
35
35
|
"@google/generative-ai": "^0.21.0",
|
|
36
36
|
"glob": "^11.0.1",
|
|
37
37
|
"mustache": "^4.2.0",
|
|
38
|
-
"openai": "^4.
|
|
38
|
+
"openai": "^4.83.0"
|
|
39
39
|
},
|
|
40
40
|
"devDependencies": {
|
|
41
41
|
"@babel/cli": "^7.26.4",
|
package/src/BaseClient.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import Mustache from 'mustache';
|
|
2
2
|
|
|
3
|
-
import { loadTemplates } from './util.js';
|
|
3
|
+
import { loadTemplates, loadTemplate } from './util.js';
|
|
4
4
|
|
|
5
5
|
const MESSAGES_REG = /(?:^|\n)-{3,}\s*(\w+)\s*-{3,}(.*?)(?=\n-{3,}|$)/gs;
|
|
6
6
|
|
|
@@ -57,26 +57,44 @@ export default class BaseClient {
|
|
|
57
57
|
}
|
|
58
58
|
|
|
59
59
|
async getMessages(options) {
|
|
60
|
+
const { text } = options;
|
|
60
61
|
const template = await this.resolveTemplate(options);
|
|
61
|
-
const raw = Mustache.render(template, transformParams(options));
|
|
62
|
-
|
|
63
|
-
const messages = [];
|
|
64
|
-
for (let match of raw.matchAll(MESSAGES_REG)) {
|
|
65
|
-
const [, role, content] = match;
|
|
66
|
-
messages.push({
|
|
67
|
-
role: role.toLowerCase(),
|
|
68
|
-
content: content.trim(),
|
|
69
|
-
});
|
|
70
|
-
}
|
|
71
62
|
|
|
72
|
-
if (
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
63
|
+
if (template) {
|
|
64
|
+
const raw = render(template, options);
|
|
65
|
+
|
|
66
|
+
const messages = [];
|
|
67
|
+
for (let match of raw.matchAll(MESSAGES_REG)) {
|
|
68
|
+
const [, role, content] = match;
|
|
69
|
+
messages.push({
|
|
70
|
+
role: role.toLowerCase(),
|
|
71
|
+
content: content.trim(),
|
|
72
|
+
});
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
if (!messages.length) {
|
|
76
|
+
messages.push({
|
|
77
|
+
role: 'user',
|
|
78
|
+
content: raw.trim(),
|
|
79
|
+
});
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
return messages;
|
|
83
|
+
} else if (text) {
|
|
84
|
+
return [
|
|
85
|
+
{
|
|
86
|
+
role: 'user',
|
|
87
|
+
content: text,
|
|
88
|
+
},
|
|
89
|
+
];
|
|
90
|
+
} else {
|
|
91
|
+
throw new Error('No input provided.');
|
|
77
92
|
}
|
|
93
|
+
}
|
|
78
94
|
|
|
79
|
-
|
|
95
|
+
async buildTemplate(options) {
|
|
96
|
+
const template = await this.resolveTemplate(options);
|
|
97
|
+
return render(template, options);
|
|
80
98
|
}
|
|
81
99
|
|
|
82
100
|
async loadTemplates() {
|
|
@@ -85,19 +103,15 @@ export default class BaseClient {
|
|
|
85
103
|
}
|
|
86
104
|
|
|
87
105
|
async resolveTemplate(options) {
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
if (!template) {
|
|
97
|
-
throw new Error('No template provided.');
|
|
106
|
+
const { template, file } = options;
|
|
107
|
+
if (template) {
|
|
108
|
+
return template;
|
|
109
|
+
} else if (file?.endsWith('.md')) {
|
|
110
|
+
return await loadTemplate(file);
|
|
111
|
+
} else if (file) {
|
|
112
|
+
await this.loadTemplates();
|
|
113
|
+
return this.templates[file];
|
|
98
114
|
}
|
|
99
|
-
|
|
100
|
-
return template;
|
|
101
115
|
}
|
|
102
116
|
|
|
103
117
|
async getStream(options) {
|
|
@@ -120,15 +134,25 @@ export default class BaseClient {
|
|
|
120
134
|
}
|
|
121
135
|
}
|
|
122
136
|
|
|
123
|
-
function
|
|
137
|
+
function render(template, options) {
|
|
138
|
+
let params = {
|
|
139
|
+
...options,
|
|
140
|
+
...options.params,
|
|
141
|
+
};
|
|
142
|
+
|
|
143
|
+
params = mapObjects(params);
|
|
144
|
+
params = wrapProxy(params);
|
|
145
|
+
return Mustache.render(template, params);
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
// Transform arrays and object to versions
|
|
149
|
+
// that are more understandable in the context
|
|
150
|
+
// of a template that may have meaningful whitespace.
|
|
151
|
+
function mapObjects(params) {
|
|
124
152
|
const result = {};
|
|
125
153
|
for (let [key, value] of Object.entries(params)) {
|
|
126
154
|
if (Array.isArray(value)) {
|
|
127
|
-
value = value
|
|
128
|
-
.map((el) => {
|
|
129
|
-
return `- ${el}`;
|
|
130
|
-
})
|
|
131
|
-
.join('\n');
|
|
155
|
+
value = mapArray(value);
|
|
132
156
|
} else if (typeof value === 'object') {
|
|
133
157
|
value = JSON.stringify(value, null, 2);
|
|
134
158
|
}
|
|
@@ -136,3 +160,36 @@ function transformParams(params) {
|
|
|
136
160
|
}
|
|
137
161
|
return result;
|
|
138
162
|
}
|
|
163
|
+
|
|
164
|
+
function mapArray(arr) {
|
|
165
|
+
// Only map simple arrays of primitives.
|
|
166
|
+
if (typeof arr[0] === 'string') {
|
|
167
|
+
arr = arr
|
|
168
|
+
.map((el) => {
|
|
169
|
+
return `- ${el}`;
|
|
170
|
+
})
|
|
171
|
+
.join('\n');
|
|
172
|
+
}
|
|
173
|
+
return arr;
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
// Wrap params with a proxy object that reports
|
|
177
|
+
// as having all properties. If one is accessed
|
|
178
|
+
// that does not exist then return the original
|
|
179
|
+
// token. This way templates can be partially
|
|
180
|
+
// interpolated and re-interpolated later.
|
|
181
|
+
function wrapProxy(params) {
|
|
182
|
+
return new Proxy(params, {
|
|
183
|
+
has() {
|
|
184
|
+
return true;
|
|
185
|
+
},
|
|
186
|
+
|
|
187
|
+
get(target, prop) {
|
|
188
|
+
if (prop in target) {
|
|
189
|
+
return target[prop];
|
|
190
|
+
} else {
|
|
191
|
+
return `{{{${prop.toString()}}}}`;
|
|
192
|
+
}
|
|
193
|
+
},
|
|
194
|
+
});
|
|
195
|
+
}
|
package/src/google.js
CHANGED
|
@@ -29,7 +29,9 @@ export class GoogleClient extends BaseClient {
|
|
|
29
29
|
const { model = DEFAULT_MODEL, output = 'text', stream = false } = options;
|
|
30
30
|
const { client } = this;
|
|
31
31
|
|
|
32
|
-
const generator = client.getGenerativeModel({
|
|
32
|
+
const generator = client.getGenerativeModel({
|
|
33
|
+
model,
|
|
34
|
+
});
|
|
33
35
|
|
|
34
36
|
const messages = await this.getMessages(options);
|
|
35
37
|
|
|
@@ -44,11 +46,6 @@ export class GoogleClient extends BaseClient {
|
|
|
44
46
|
} else {
|
|
45
47
|
response = await generator.generateContent(prompts);
|
|
46
48
|
}
|
|
47
|
-
// const response = await client.chat.completions.create({
|
|
48
|
-
// model,
|
|
49
|
-
// messages,
|
|
50
|
-
// stream,
|
|
51
|
-
// });
|
|
52
49
|
|
|
53
50
|
if (output === 'raw') {
|
|
54
51
|
return response;
|
package/src/index.js
CHANGED
|
@@ -1,20 +1,72 @@
|
|
|
1
1
|
import { OpenAiClient } from './openai.js';
|
|
2
2
|
import { GoogleClient } from './google.js';
|
|
3
3
|
import { AnthropicClient } from './anthropic.js';
|
|
4
|
+
import { XAiClient } from './xai.js';
|
|
4
5
|
|
|
5
6
|
export class Client {
|
|
7
|
+
constructor(options = {}) {
|
|
8
|
+
if (!options.platform) {
|
|
9
|
+
throw new Error('No platform specified.');
|
|
10
|
+
} else if (!options.templates) {
|
|
11
|
+
throw new Error('No templates directory specified.');
|
|
12
|
+
} else if (!options.apiKey) {
|
|
13
|
+
throw new Error('No API key specified.');
|
|
14
|
+
}
|
|
15
|
+
return getClientForPlatform(options);
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export class MultiClient {
|
|
6
20
|
constructor(options) {
|
|
7
|
-
const {
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
21
|
+
const { platforms } = options;
|
|
22
|
+
|
|
23
|
+
this.clients = {};
|
|
24
|
+
|
|
25
|
+
for (let platform of platforms) {
|
|
26
|
+
const { name, apiKey } = platform;
|
|
27
|
+
const client = getClientForPlatform({
|
|
28
|
+
...options,
|
|
29
|
+
platform: name,
|
|
30
|
+
apiKey,
|
|
31
|
+
});
|
|
32
|
+
this.clients[name] = client;
|
|
33
|
+
this.clients[undefined] ||= client;
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
prompt(options) {
|
|
38
|
+
return this.getClient(options).prompt(options);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
stream(options) {
|
|
42
|
+
return this.getClient(options).stream(options);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
buildTemplate(options) {
|
|
46
|
+
return this.getClient(options).buildTemplate(options);
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
getClient(options) {
|
|
50
|
+
const { platform } = options;
|
|
51
|
+
const client = this.clients[platform];
|
|
52
|
+
if (!client) {
|
|
53
|
+
throw new Error(`Platform "${platform}" not found.`);
|
|
18
54
|
}
|
|
55
|
+
return client;
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
function getClientForPlatform(options) {
|
|
60
|
+
const { platform } = options;
|
|
61
|
+
if (platform === 'openai' || platform === 'gpt') {
|
|
62
|
+
return new OpenAiClient(options);
|
|
63
|
+
} else if (platform === 'google' || platform === 'gemini') {
|
|
64
|
+
return new GoogleClient(options);
|
|
65
|
+
} else if (platform === 'anthropic' || platform === 'claude') {
|
|
66
|
+
return new AnthropicClient(options);
|
|
67
|
+
} else if (platform === 'xai' || platform === 'grok') {
|
|
68
|
+
return new XAiClient(options);
|
|
69
|
+
} else if (platform) {
|
|
70
|
+
throw new Error(`Unknown platform "${platform}".`);
|
|
19
71
|
}
|
|
20
72
|
}
|
package/src/openai.js
CHANGED
|
@@ -3,7 +3,7 @@ import OpenAI from 'openai';
|
|
|
3
3
|
import BaseClient from './BaseClient.js';
|
|
4
4
|
import { transformResponse } from './util.js';
|
|
5
5
|
|
|
6
|
-
const DEFAULT_MODEL = 'gpt-4o';
|
|
6
|
+
const DEFAULT_MODEL = 'gpt-4o-mini';
|
|
7
7
|
|
|
8
8
|
export class OpenAiClient extends BaseClient {
|
|
9
9
|
constructor(options) {
|
|
@@ -31,6 +31,9 @@ export class OpenAiClient extends BaseClient {
|
|
|
31
31
|
model,
|
|
32
32
|
messages,
|
|
33
33
|
stream,
|
|
34
|
+
response_format: {
|
|
35
|
+
type: output === 'json' ? 'json_object' : 'text',
|
|
36
|
+
},
|
|
34
37
|
});
|
|
35
38
|
|
|
36
39
|
if (output === 'raw') {
|
package/src/util.js
CHANGED
|
@@ -4,27 +4,27 @@ import path from 'path';
|
|
|
4
4
|
|
|
5
5
|
import { glob } from 'glob';
|
|
6
6
|
|
|
7
|
+
const CODE_REG = /^```\w*$(.+)```/ms;
|
|
7
8
|
const JSON_REG = /([{[].+[}\]])/s;
|
|
8
9
|
|
|
9
10
|
export async function loadTemplates(dir) {
|
|
10
11
|
const result = {};
|
|
11
12
|
const files = await glob(path.join(dir, '*.md'));
|
|
12
13
|
|
|
14
|
+
if (!files.length) {
|
|
15
|
+
throw new Error(`No templates found in: ${dir}.`);
|
|
16
|
+
}
|
|
17
|
+
|
|
13
18
|
for (let file of files) {
|
|
14
19
|
const base = path.basename(file, '.md');
|
|
15
|
-
result[base] = await
|
|
20
|
+
result[base] = await loadTemplate(file);
|
|
16
21
|
}
|
|
17
22
|
|
|
18
23
|
return result;
|
|
19
24
|
}
|
|
20
25
|
|
|
21
|
-
export function
|
|
22
|
-
|
|
23
|
-
const match = content.match(JSON_REG);
|
|
24
|
-
return JSON.parse(match[1]);
|
|
25
|
-
} catch (error) {
|
|
26
|
-
throw new Error('Unable to derive JSON object in response.');
|
|
27
|
-
}
|
|
26
|
+
export async function loadTemplate(file) {
|
|
27
|
+
return await fs.readFile(file, 'utf-8');
|
|
28
28
|
}
|
|
29
29
|
|
|
30
30
|
export function transformResponse(options) {
|
|
@@ -35,8 +35,26 @@ export function transformResponse(options) {
|
|
|
35
35
|
} else if (output === 'messages') {
|
|
36
36
|
return [...messages, message];
|
|
37
37
|
} else if (output === 'json') {
|
|
38
|
-
return
|
|
38
|
+
return parseJson(content);
|
|
39
|
+
} else if (output === 'code') {
|
|
40
|
+
return parseCode(content);
|
|
39
41
|
} else {
|
|
40
|
-
throw new Error(`
|
|
42
|
+
throw new Error(`No output type provided.`);
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
function parseJson(content) {
|
|
47
|
+
try {
|
|
48
|
+
return JSON.parse(content.match(JSON_REG)[0]);
|
|
49
|
+
} catch (error) {
|
|
50
|
+
throw new Error(`Unable to derive JSON from response:\n\n${content}`);
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
function parseCode(content) {
|
|
55
|
+
try {
|
|
56
|
+
return content.match(CODE_REG)[1].trim();
|
|
57
|
+
} catch (error) {
|
|
58
|
+
throw new Error(`Unable to derive code from response:\n\n${content}`);
|
|
41
59
|
}
|
|
42
60
|
}
|
package/src/xai.js
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { OpenAiClient } from './openai.js';
|
|
2
|
+
|
|
3
|
+
const DEFAULT_MODEL = 'grok-2-1212';
|
|
4
|
+
|
|
5
|
+
export class XAiClient extends OpenAiClient {
|
|
6
|
+
constructor(options) {
|
|
7
|
+
super({
|
|
8
|
+
...options,
|
|
9
|
+
baseURL: 'https://api.x.ai/v1',
|
|
10
|
+
});
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
async getCompletion(options) {
|
|
14
|
+
return super.getCompletion({
|
|
15
|
+
model: DEFAULT_MODEL,
|
|
16
|
+
...options,
|
|
17
|
+
});
|
|
18
|
+
}
|
|
19
|
+
}
|
package/types/BaseClient.d.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"BaseClient.d.ts","sourceRoot":"","sources":["../src/BaseClient.js"],"names":[],"mappings":"AAMA;IACE,0BAGC;IAFC,aAAsB;IACtB,eAAqB;IAGvB;;;;;;;;;;;OAWG;IACH,gBALG;QAAwB,KAAK,EAArB,MAAM;QACyC,MAAM,GAArD,KAAK,GAAG,MAAM,GAAG,MAAM,GAAG,UAAU;QACL,KAAK,GAApC;gBAAQ,MAAM,GAAE,GAAG;SAAC;KAE9B,iBAYA;IAED;;;OAGG;IACH,mDAeC;IAED;;;
|
|
1
|
+
{"version":3,"file":"BaseClient.d.ts","sourceRoot":"","sources":["../src/BaseClient.js"],"names":[],"mappings":"AAMA;IACE,0BAGC;IAFC,aAAsB;IACtB,eAAqB;IAGvB;;;;;;;;;;;OAWG;IACH,gBALG;QAAwB,KAAK,EAArB,MAAM;QACyC,MAAM,GAArD,KAAK,GAAG,MAAM,GAAG,MAAM,GAAG,UAAU;QACL,KAAK,GAApC;gBAAQ,MAAM,GAAE,GAAG;SAAC;KAE9B,iBAYA;IAED;;;OAGG;IACH,mDAeC;IAED;;;SAkCC;IAED,0CAGC;IAED,+BAGC;IAED,4CAUC;IAED,uCAMC;IAED,kCAGC;IAED,iDAIC;CACF"}
|
package/types/google.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"google.d.ts","sourceRoot":"","sources":["../src/google.js"],"names":[],"mappings":"AAOA;IAII,2BAA4C;IAG9C;;;OAGG;IACH,4BAOC;IAED,
|
|
1
|
+
{"version":3,"file":"google.d.ts","sourceRoot":"","sources":["../src/google.js"],"names":[],"mappings":"AAOA;IAII,2BAA4C;IAG9C;;;OAGG;IACH,4BAOC;IAED,0CAqCC;IACD,sCAIC;IAED;;;MAkBC;CACF;uBAxFsB,iBAAiB;mCAFL,uBAAuB"}
|
package/types/index.d.ts
CHANGED
|
@@ -1,4 +1,12 @@
|
|
|
1
1
|
export class Client {
|
|
2
|
+
constructor(options?: {});
|
|
3
|
+
}
|
|
4
|
+
export class MultiClient {
|
|
2
5
|
constructor(options: any);
|
|
6
|
+
clients: {};
|
|
7
|
+
prompt(options: any): any;
|
|
8
|
+
stream(options: any): any;
|
|
9
|
+
buildTemplate(options: any): any;
|
|
10
|
+
getClient(options: any): any;
|
|
3
11
|
}
|
|
4
12
|
//# sourceMappingURL=index.d.ts.map
|
package/types/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.js"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.js"],"names":[],"mappings":"AAKA;IACE,0BASC;CACF;AAED;IACE,0BAeC;IAZC,YAAiB;IAcnB,0BAEC;IAED,0BAEC;IAED,iCAEC;IAED,6BAOC;CACF"}
|
package/types/openai.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"openai.d.ts","sourceRoot":"","sources":["../src/openai.js"],"names":[],"mappings":"AAOA;IAGI,eAEE;IAGJ;;;OAGG;IACH,4BAGC;IAED,
|
|
1
|
+
{"version":3,"file":"openai.d.ts","sourceRoot":"","sources":["../src/openai.js"],"names":[],"mappings":"AAOA;IAGI,eAEE;IAGJ;;;OAGG;IACH,4BAGC;IAED,0CAyBC;IAED;;;MAkBC;CACF;uBApEsB,iBAAiB;mBAFrB,QAAQ"}
|
package/types/util.d.ts
CHANGED
package/types/util.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"util.d.ts","sourceRoot":"","sources":["../src/util.js"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"util.d.ts","sourceRoot":"","sources":["../src/util.js"],"names":[],"mappings":"AASA,qDAcC;AAED,yDAEC;AAED,qDAcC"}
|
package/types/xai.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"xai.d.ts","sourceRoot":"","sources":["../src/xai.js"],"names":[],"mappings":"AAIA;CAcC;6BAlB4B,aAAa"}
|