@intuned/browser-dev 0.1.14-dev.3 → 0.1.15-dev.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/dist/helpers/tests/testDownloadFile.spec.js +52 -0
- package/dist/helpers/types/Attachment.js +6 -0
- package/dist/helpers/types/__tests__/Attachment.test.js +8 -0
- package/dist/intunedServices/ApiGateway/providers/Anthropic.js +2 -1
- package/dist/intunedServices/ApiGateway/tests/testApiGateway.spec.js +55 -63
- package/package.json +2 -2
|
@@ -4,6 +4,7 @@ var _extendedTest = require("../../common/extendedTest");
|
|
|
4
4
|
var _playwright = require("playwright");
|
|
5
5
|
var _fs = _interopRequireDefault(require("fs"));
|
|
6
6
|
var _ = require("..");
|
|
7
|
+
var _Attachment = require("../types/Attachment");
|
|
7
8
|
var _promises = require("fs/promises");
|
|
8
9
|
var _os = require("os");
|
|
9
10
|
var _path = require("path");
|
|
@@ -278,4 +279,55 @@ _extendedTest.describe.skip("TestNotInGeneration", () => {
|
|
|
278
279
|
(0, _extendedTest.expect)(cancelled).toContain("canceled");
|
|
279
280
|
}
|
|
280
281
|
});
|
|
282
|
+
});
|
|
283
|
+
const SIGNED_URL = "https://bucket.r2.cloudflarestorage.com/img.jpg?X-Amz-Signature=abc";
|
|
284
|
+
(0, _extendedTest.describe)("SignedUrlAttachmentInTypedArray", () => {
|
|
285
|
+
(0, _extendedTest.test)("toDict() on SignedUrlAttachment stored in Attachment[] includes signedUrl", async () => {
|
|
286
|
+
const images = [];
|
|
287
|
+
images.push(new _Attachment.SignedUrlAttachment("img.jpg", SIGNED_URL, "img.jpg"));
|
|
288
|
+
const dict = images[0].toDict();
|
|
289
|
+
(0, _extendedTest.expect)(dict.signedUrl).toBe(SIGNED_URL);
|
|
290
|
+
});
|
|
291
|
+
(0, _extendedTest.test)("toDict() on SignedUrlAttachment stored in Attachment[] has no null/undefined fields", async () => {
|
|
292
|
+
const images = [];
|
|
293
|
+
images.push(new _Attachment.SignedUrlAttachment("img.jpg", SIGNED_URL, "img.jpg"));
|
|
294
|
+
const dict = images[0].toDict();
|
|
295
|
+
for (const [, value] of Object.entries(dict)) {
|
|
296
|
+
(0, _extendedTest.expect)(value).not.toBeNull();
|
|
297
|
+
(0, _extendedTest.expect)(value).not.toBeUndefined();
|
|
298
|
+
}
|
|
299
|
+
(0, _extendedTest.expect)(dict.bucket).toBeUndefined();
|
|
300
|
+
(0, _extendedTest.expect)(dict.region).toBeUndefined();
|
|
301
|
+
(0, _extendedTest.expect)(dict.endpoint).toBeUndefined();
|
|
302
|
+
});
|
|
303
|
+
(0, _extendedTest.test)("JSON.stringify on Attachment[] containing SignedUrlAttachment includes signedUrl", async () => {
|
|
304
|
+
const images = [new _Attachment.SignedUrlAttachment("img.jpg", SIGNED_URL, "img.jpg")];
|
|
305
|
+
const serialized = JSON.parse(JSON.stringify(images));
|
|
306
|
+
(0, _extendedTest.expect)(serialized[0].signedUrl).toBe(SIGNED_URL);
|
|
307
|
+
(0, _extendedTest.expect)(serialized[0].bucket).toBeUndefined();
|
|
308
|
+
(0, _extendedTest.expect)(serialized[0].region).toBeUndefined();
|
|
309
|
+
});
|
|
310
|
+
(0, _extendedTest.test)("JSON.stringify on plain object with imageAttachments: Attachment[] serializes signedUrl", async () => {
|
|
311
|
+
const product = {
|
|
312
|
+
name: "My Product",
|
|
313
|
+
imageAttachments: [new _Attachment.SignedUrlAttachment("img.jpg", SIGNED_URL, "img.jpg")]
|
|
314
|
+
};
|
|
315
|
+
const serialized = JSON.parse(JSON.stringify(product));
|
|
316
|
+
const item = serialized.imageAttachments[0];
|
|
317
|
+
(0, _extendedTest.expect)(item.signedUrl).toBe(SIGNED_URL);
|
|
318
|
+
(0, _extendedTest.expect)(item.bucket).toBeUndefined();
|
|
319
|
+
(0, _extendedTest.expect)(item.region).toBeUndefined();
|
|
320
|
+
(0, _extendedTest.expect)(item.endpoint).toBeUndefined();
|
|
321
|
+
});
|
|
322
|
+
(0, _extendedTest.test)("mixed Attachment and SignedUrlAttachment in Attachment[] both serialize correctly", async () => {
|
|
323
|
+
const images = [new _Attachment.Attachment("s3.jpg", "s3.jpg", "my-bucket", "us-east-1", "s3.jpg"), new _Attachment.SignedUrlAttachment("intuned.jpg", SIGNED_URL, "intuned.jpg")];
|
|
324
|
+
const s3Dict = images[0].toDict();
|
|
325
|
+
(0, _extendedTest.expect)(s3Dict.bucket).toBe("my-bucket");
|
|
326
|
+
(0, _extendedTest.expect)(s3Dict.region).toBe("us-east-1");
|
|
327
|
+
(0, _extendedTest.expect)(s3Dict.signedUrl).toBeUndefined();
|
|
328
|
+
const intunedDict = images[1].toDict();
|
|
329
|
+
(0, _extendedTest.expect)(intunedDict.signedUrl).toBe(SIGNED_URL);
|
|
330
|
+
(0, _extendedTest.expect)(intunedDict.bucket).toBeUndefined();
|
|
331
|
+
(0, _extendedTest.expect)(intunedDict.region).toBeUndefined();
|
|
332
|
+
});
|
|
281
333
|
});
|
|
@@ -89,6 +89,9 @@ class Attachment {
|
|
|
89
89
|
if (!this.bucket || !this.region) {
|
|
90
90
|
throw new Error("bucket and region are required to get the S3 key");
|
|
91
91
|
}
|
|
92
|
+
if (this.endpoint) {
|
|
93
|
+
throw new Error("getS3Key is not supported when using a custom endpoint, use getSignedUrl instead");
|
|
94
|
+
}
|
|
92
95
|
return `https://${this.bucket}.s3.${this.region}.amazonaws.com/${this.fileName}`;
|
|
93
96
|
}
|
|
94
97
|
getFileType() {
|
|
@@ -110,6 +113,9 @@ class SignedUrlAttachment extends Attachment {
|
|
|
110
113
|
signedUrl: this.signedUrl
|
|
111
114
|
};
|
|
112
115
|
}
|
|
116
|
+
getS3Key() {
|
|
117
|
+
throw new Error("SignedUrlAttachment does not support getS3Key, use getSignedUrl() instead");
|
|
118
|
+
}
|
|
113
119
|
async getSignedUrl() {
|
|
114
120
|
return this.signedUrl;
|
|
115
121
|
}
|
|
@@ -51,6 +51,10 @@ const SIGNED_URL = "https://bucket.r2.cloudflarestorage.com/file.pdf?X-Amz-Signa
|
|
|
51
51
|
const a = new _Attachment.Attachment("f", "f", undefined, undefined, "s");
|
|
52
52
|
(0, _vitest.expect)(() => a.getS3Key()).toThrow();
|
|
53
53
|
});
|
|
54
|
+
(0, _vitest.test)("getS3Key throws when custom endpoint is set", () => {
|
|
55
|
+
const a = new _Attachment.Attachment("f", "f", "b", "r", "s", "https://r2.example.com");
|
|
56
|
+
(0, _vitest.expect)(() => a.getS3Key()).toThrow(/custom endpoint/);
|
|
57
|
+
});
|
|
54
58
|
(0, _vitest.test)("does not have signedUrl field", () => {
|
|
55
59
|
const a = new _Attachment.Attachment("f", "f", "b", "r", "s");
|
|
56
60
|
(0, _vitest.expect)(a.signedUrl).toBeUndefined();
|
|
@@ -99,6 +103,10 @@ const SIGNED_URL = "https://bucket.r2.cloudflarestorage.com/file.pdf?X-Amz-Signa
|
|
|
99
103
|
(0, _vitest.test)("rejects invalid signed URL", () => {
|
|
100
104
|
(0, _vitest.expect)(() => new _Attachment.SignedUrlAttachment("f", "not-a-url")).toThrow();
|
|
101
105
|
});
|
|
106
|
+
(0, _vitest.test)("getS3Key throws with descriptive message pointing to getSignedUrl", () => {
|
|
107
|
+
const s = new _Attachment.SignedUrlAttachment("f", SIGNED_URL, "f");
|
|
108
|
+
(0, _vitest.expect)(() => s.getS3Key()).toThrow(/getSignedUrl/);
|
|
109
|
+
});
|
|
102
110
|
});
|
|
103
111
|
(0, _vitest.describe)("toDict null/undefined exclusion", () => {
|
|
104
112
|
(0, _vitest.test)("Attachment with S3 fields includes bucket and region", () => {
|
|
@@ -16,9 +16,10 @@ const createAnthropicInstance = input => {
|
|
|
16
16
|
apiKey
|
|
17
17
|
});
|
|
18
18
|
} else {
|
|
19
|
+
const baseURLWithV1 = baseUrl ? `${baseUrl}/v1` : undefined;
|
|
19
20
|
return (0, _anthropic.createAnthropic)({
|
|
20
21
|
apiKey,
|
|
21
|
-
baseURL:
|
|
22
|
+
baseURL: baseURLWithV1,
|
|
22
23
|
fetch: _jwtTokenManager.backendFunctionsTokenManager.fetchWithToken.bind(_jwtTokenManager.backendFunctionsTokenManager)
|
|
23
24
|
});
|
|
24
25
|
}
|
|
@@ -26,13 +26,15 @@ _vitest.vi.mock("../providers/Gemini", () => ({
|
|
|
26
26
|
createGoogleInstance: _vitest.vi.fn()
|
|
27
27
|
}));
|
|
28
28
|
_vitest.vi.mock("../../../common/loadRuntime", () => ({
|
|
29
|
-
loadRuntime: _vitest.vi.fn()
|
|
29
|
+
loadRuntime: _vitest.vi.fn(),
|
|
30
|
+
loadGetAiGatewayConfig: _vitest.vi.fn()
|
|
30
31
|
}));
|
|
31
32
|
const mockGetModelProvider = _vitest.vi.mocked(_getModelProvider.getModelProvider);
|
|
32
33
|
const mockCreateAnthropicInstance = _vitest.vi.mocked(_Anthropic.createAnthropicInstance);
|
|
33
34
|
const mockCreateOpenAIInstance = _vitest.vi.mocked(_OpenAI.createOpenAIInstance);
|
|
34
35
|
const mockCreateGoogleInstance = _vitest.vi.mocked(_Gemini.createGoogleInstance);
|
|
35
36
|
const mockLoadRuntime = _vitest.vi.mocked(_loadRuntime.loadRuntime);
|
|
37
|
+
const mockLoadGetAiGatewayConfig = _vitest.vi.mocked(_loadRuntime.loadGetAiGatewayConfig);
|
|
36
38
|
(0, _extendedTest.describe)("APIGateway", () => {
|
|
37
39
|
let originalEnv;
|
|
38
40
|
(0, _extendedTest.beforeEach)(() => {
|
|
@@ -108,57 +110,6 @@ const mockLoadRuntime = _vitest.vi.mocked(_loadRuntime.loadRuntime);
|
|
|
108
110
|
(0, _extendedTest.expect)(gateway["detectProvider"]("unknown-model")).toBe("unknown");
|
|
109
111
|
});
|
|
110
112
|
});
|
|
111
|
-
(0, _extendedTest.describe)("buildGatewayUrl", () => {
|
|
112
|
-
(0, _extendedTest.it)("should build correct URL for anthropic", async () => {
|
|
113
|
-
mockGetModelProvider.mockReturnValue("anthropic");
|
|
114
|
-
mockLoadRuntime.mockResolvedValue(() => ({
|
|
115
|
-
runId: "test"
|
|
116
|
-
}));
|
|
117
|
-
const gateway = new _aiApiGateway.APIGateway({
|
|
118
|
-
model: "claude-3-sonnet"
|
|
119
|
-
});
|
|
120
|
-
await gateway["ensureInitialized"]();
|
|
121
|
-
const result = gateway["buildGatewayUrl"]();
|
|
122
|
-
(0, _extendedTest.expect)(result).toBe("https://functions.example.com/api/workspace123/functions/integration456/intuned-ai-gateway");
|
|
123
|
-
});
|
|
124
|
-
(0, _extendedTest.it)("should build correct URL for google_vertexai", async () => {
|
|
125
|
-
mockGetModelProvider.mockReturnValue("google_vertexai");
|
|
126
|
-
mockLoadRuntime.mockResolvedValue(() => ({
|
|
127
|
-
runId: "test"
|
|
128
|
-
}));
|
|
129
|
-
const gateway = new _aiApiGateway.APIGateway({
|
|
130
|
-
model: "gemini-pro"
|
|
131
|
-
});
|
|
132
|
-
await gateway["ensureInitialized"]();
|
|
133
|
-
const result = gateway["buildGatewayUrl"]();
|
|
134
|
-
(0, _extendedTest.expect)(result).toBe("https://functions.example.com/api/workspace123/functions/integration456/intuned-ai-gateway");
|
|
135
|
-
});
|
|
136
|
-
(0, _extendedTest.it)("should build correct URL for other providers", async () => {
|
|
137
|
-
mockGetModelProvider.mockReturnValue("openai");
|
|
138
|
-
mockLoadRuntime.mockResolvedValue(() => ({
|
|
139
|
-
runId: "test"
|
|
140
|
-
}));
|
|
141
|
-
const gateway = new _aiApiGateway.APIGateway({
|
|
142
|
-
model: "gpt-4"
|
|
143
|
-
});
|
|
144
|
-
await gateway["ensureInitialized"]();
|
|
145
|
-
const result = gateway["buildGatewayUrl"]();
|
|
146
|
-
(0, _extendedTest.expect)(result).toBe("https://functions.example.com/api/workspace123/functions/integration456/intuned-ai-gateway");
|
|
147
|
-
});
|
|
148
|
-
(0, _extendedTest.it)("should strip trailing slash from domain", async () => {
|
|
149
|
-
process.env.FUNCTIONS_DOMAIN = "https://functions.example.com/";
|
|
150
|
-
mockGetModelProvider.mockReturnValue("openai");
|
|
151
|
-
mockLoadRuntime.mockResolvedValue(() => ({
|
|
152
|
-
runId: "test"
|
|
153
|
-
}));
|
|
154
|
-
const newGateway = new _aiApiGateway.APIGateway({
|
|
155
|
-
model: "gpt-4"
|
|
156
|
-
});
|
|
157
|
-
await newGateway["ensureInitialized"]();
|
|
158
|
-
const result = newGateway["buildGatewayUrl"]();
|
|
159
|
-
(0, _extendedTest.expect)(result).toBe("https://functions.example.com/api/workspace123/functions/integration456/intuned-ai-gateway");
|
|
160
|
-
});
|
|
161
|
-
});
|
|
162
113
|
(0, _extendedTest.describe)("ensureInitialized", () => {
|
|
163
114
|
(0, _extendedTest.it)("should use direct mode when API key provided", async () => {
|
|
164
115
|
const gateway = new _aiApiGateway.APIGateway({
|
|
@@ -192,11 +143,6 @@ const mockLoadRuntime = _vitest.vi.mocked(_loadRuntime.loadRuntime);
|
|
|
192
143
|
await gateway["ensureInitialized"]();
|
|
193
144
|
(0, _extendedTest.expect)(gateway["useGateway"]).toBe(true);
|
|
194
145
|
(0, _extendedTest.expect)(gateway["isInitialized"]).toBe(true);
|
|
195
|
-
(0, _extendedTest.expect)(gateway["config"]).toEqual({
|
|
196
|
-
functionsDomain: "https://functions.example.com",
|
|
197
|
-
workspaceId: "workspace123",
|
|
198
|
-
integrationId: "integration456"
|
|
199
|
-
});
|
|
200
146
|
});
|
|
201
147
|
(0, _extendedTest.it)("should throw error when no API key and not in intuned context", async () => {
|
|
202
148
|
mockGetModelProvider.mockReturnValue("openai");
|
|
@@ -206,16 +152,54 @@ const mockLoadRuntime = _vitest.vi.mocked(_loadRuntime.loadRuntime);
|
|
|
206
152
|
});
|
|
207
153
|
await (0, _extendedTest.expect)(gateway["ensureInitialized"]()).rejects.toThrow("No API key provided and not running in Intuned context");
|
|
208
154
|
});
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
155
|
+
});
|
|
156
|
+
(0, _extendedTest.describe)("getModelConfig", () => {
|
|
157
|
+
(0, _extendedTest.it)("should return direct config when apiKey is set", async () => {
|
|
158
|
+
const gateway = new _aiApiGateway.APIGateway({
|
|
159
|
+
model: "gpt-4",
|
|
160
|
+
apiKey: "sk-direct"
|
|
161
|
+
});
|
|
162
|
+
await gateway["ensureInitialized"]();
|
|
163
|
+
const result = await gateway["getModelConfig"]();
|
|
164
|
+
(0, _extendedTest.expect)(result).toEqual({
|
|
165
|
+
model: "gpt-4",
|
|
166
|
+
apiKey: "sk-direct",
|
|
167
|
+
extraHeaders: undefined,
|
|
168
|
+
baseUrl: undefined
|
|
169
|
+
});
|
|
170
|
+
});
|
|
171
|
+
(0, _extendedTest.it)("should resolve base URL and API key from runtime in gateway mode", async () => {
|
|
172
|
+
mockGetModelProvider.mockReturnValue("anthropic");
|
|
212
173
|
mockLoadRuntime.mockResolvedValue(() => ({
|
|
213
174
|
runId: "test"
|
|
214
175
|
}));
|
|
176
|
+
mockLoadGetAiGatewayConfig.mockResolvedValue(() => ({
|
|
177
|
+
baseUrl: "https://functions.example.com/api/workspace123/functions/integration456/intuned-ai-gateway",
|
|
178
|
+
apiKey: "resolved-token"
|
|
179
|
+
}));
|
|
215
180
|
const gateway = new _aiApiGateway.APIGateway({
|
|
216
|
-
model: "
|
|
181
|
+
model: "claude-3-sonnet"
|
|
182
|
+
});
|
|
183
|
+
await gateway["ensureInitialized"]();
|
|
184
|
+
const result = await gateway["getModelConfig"]();
|
|
185
|
+
(0, _extendedTest.expect)(result).toEqual({
|
|
186
|
+
model: "claude-3-sonnet",
|
|
187
|
+
apiKey: "resolved-token",
|
|
188
|
+
extraHeaders: undefined,
|
|
189
|
+
baseUrl: "https://functions.example.com/api/workspace123/functions/integration456/intuned-ai-gateway"
|
|
190
|
+
});
|
|
191
|
+
});
|
|
192
|
+
(0, _extendedTest.it)("should throw when runtime does not export getAiGatewayConfig", async () => {
|
|
193
|
+
mockGetModelProvider.mockReturnValue("anthropic");
|
|
194
|
+
mockLoadRuntime.mockResolvedValue(() => ({
|
|
195
|
+
runId: "test"
|
|
196
|
+
}));
|
|
197
|
+
mockLoadGetAiGatewayConfig.mockRejectedValue(new Error("@intuned/runtime does not export getAiGatewayConfig. Please upgrade to a newer version."));
|
|
198
|
+
const gateway = new _aiApiGateway.APIGateway({
|
|
199
|
+
model: "claude-3-sonnet"
|
|
217
200
|
});
|
|
218
|
-
await
|
|
201
|
+
await gateway["ensureInitialized"]();
|
|
202
|
+
await (0, _extendedTest.expect)(gateway["getModelConfig"]()).rejects.toThrow("Failed to load gateway configuration from @intuned/runtime");
|
|
219
203
|
});
|
|
220
204
|
});
|
|
221
205
|
(0, _extendedTest.describe)("createProviderInstance", () => {
|
|
@@ -224,6 +208,10 @@ const mockLoadRuntime = _vitest.vi.mocked(_loadRuntime.loadRuntime);
|
|
|
224
208
|
mockLoadRuntime.mockResolvedValue(() => ({
|
|
225
209
|
runId: "test"
|
|
226
210
|
}));
|
|
211
|
+
mockLoadGetAiGatewayConfig.mockResolvedValue(() => ({
|
|
212
|
+
baseUrl: "https://functions.example.com/api/workspace123/functions/integration456/intuned-ai-gateway",
|
|
213
|
+
apiKey: "resolved-token"
|
|
214
|
+
}));
|
|
227
215
|
const mockInstance = {
|
|
228
216
|
chat: _vitest.vi.fn()
|
|
229
217
|
};
|
|
@@ -233,7 +221,7 @@ const mockLoadRuntime = _vitest.vi.mocked(_loadRuntime.loadRuntime);
|
|
|
233
221
|
});
|
|
234
222
|
const result = await gateway.createProviderInstance();
|
|
235
223
|
(0, _extendedTest.expect)(mockCreateAnthropicInstance).toHaveBeenCalledWith({
|
|
236
|
-
apiKey: "
|
|
224
|
+
apiKey: "resolved-token",
|
|
237
225
|
headers: undefined,
|
|
238
226
|
model: "claude-3-sonnet",
|
|
239
227
|
baseUrl: "https://functions.example.com/api/workspace123/functions/integration456/intuned-ai-gateway"
|
|
@@ -264,6 +252,10 @@ const mockLoadRuntime = _vitest.vi.mocked(_loadRuntime.loadRuntime);
|
|
|
264
252
|
mockLoadRuntime.mockResolvedValue(() => ({
|
|
265
253
|
runId: "test"
|
|
266
254
|
}));
|
|
255
|
+
mockLoadGetAiGatewayConfig.mockResolvedValue(() => ({
|
|
256
|
+
baseUrl: "https://functions.example.com/api/workspace123/functions/integration456/intuned-ai-gateway",
|
|
257
|
+
apiKey: "resolved-token"
|
|
258
|
+
}));
|
|
267
259
|
const mockInstance = {
|
|
268
260
|
chat: _vitest.vi.fn()
|
|
269
261
|
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@intuned/browser-dev",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.15-dev.0",
|
|
4
4
|
"description": "runner package for intuned functions",
|
|
5
5
|
"types": "./dist/index.d.ts",
|
|
6
6
|
"typesVersions": {
|
|
@@ -52,7 +52,7 @@
|
|
|
52
52
|
},
|
|
53
53
|
"dependencies": {
|
|
54
54
|
"@ai-sdk/anthropic": "2.0.1",
|
|
55
|
-
"@ai-sdk/google": "
|
|
55
|
+
"@ai-sdk/google": "2.0.68",
|
|
56
56
|
"@ai-sdk/openai": "2.0.4",
|
|
57
57
|
"@ai-sdk/provider": "2.0.0",
|
|
58
58
|
"@anthropic-ai/sdk": "0.22.0",
|