@intuned/browser-dev 0.1.14-dev.2 → 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/.claude/settings.local.json +8 -0
- package/dist/common/loadRuntime.js +11 -2
- 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/aiApiGateway.js +21 -45
- package/dist/intunedServices/ApiGateway/providers/Anthropic.js +3 -1
- package/dist/intunedServices/ApiGateway/providers/Gemini.js +1 -0
- package/dist/intunedServices/ApiGateway/providers/OpenAI.js +1 -0
- package/dist/intunedServices/ApiGateway/tests/testApiGateway.spec.js +55 -63
- package/package.json +2 -2
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
Object.defineProperty(exports, "__esModule", {
|
|
4
4
|
value: true
|
|
5
5
|
});
|
|
6
|
-
exports.loadRuntime = void 0;
|
|
6
|
+
exports.loadRuntime = exports.loadGetAiGatewayConfig = void 0;
|
|
7
7
|
function _interopRequireWildcard(e, t) { if ("function" == typeof WeakMap) var r = new WeakMap(), n = new WeakMap(); return (_interopRequireWildcard = function (e, t) { if (!t && e && e.__esModule) return e; var o, i, f = { __proto__: null, default: e }; if (null === e || "object" != typeof e && "function" != typeof e) return f; if (o = t ? n : r) { if (o.has(e)) return o.get(e); o.set(e, f); } for (const t in e) "default" !== t && {}.hasOwnProperty.call(e, t) && ((i = (o = Object.defineProperty) && Object.getOwnPropertyDescriptor(e, t)) && (i.get || i.set) ? o(f, t, i) : f[t] = e[t]); return f; })(e, t); }
|
|
8
8
|
const loadRuntime = async () => {
|
|
9
9
|
try {
|
|
@@ -13,4 +13,13 @@ const loadRuntime = async () => {
|
|
|
13
13
|
return () => null;
|
|
14
14
|
}
|
|
15
15
|
};
|
|
16
|
-
exports.loadRuntime = loadRuntime;
|
|
16
|
+
exports.loadRuntime = loadRuntime;
|
|
17
|
+
const loadGetAiGatewayConfig = async () => {
|
|
18
|
+
const runtime = await Promise.resolve().then(() => _interopRequireWildcard(require("@intuned/runtime")));
|
|
19
|
+
const fn = runtime.getAiGatewayConfig;
|
|
20
|
+
if (typeof fn !== "function") {
|
|
21
|
+
throw new Error("@intuned/runtime does not export getAiGatewayConfig. Please upgrade to a newer version.");
|
|
22
|
+
}
|
|
23
|
+
return fn;
|
|
24
|
+
};
|
|
25
|
+
exports.loadGetAiGatewayConfig = loadGetAiGatewayConfig;
|
|
@@ -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", () => {
|
|
@@ -36,8 +36,6 @@ class APIGateway {
|
|
|
36
36
|
return;
|
|
37
37
|
}
|
|
38
38
|
await this.validateIntunedContext();
|
|
39
|
-
this.config = this.getDefaultConfig();
|
|
40
|
-
this.validateGatewayConfig(this.config);
|
|
41
39
|
this.useGateway = true;
|
|
42
40
|
this.isInitialized = true;
|
|
43
41
|
}
|
|
@@ -62,41 +60,11 @@ class APIGateway {
|
|
|
62
60
|
throw new Error("No API key provided and not running in Intuned context. " + "Please provide an API key or ensure @intuned/runtime is installed and you're running within an Intuned workflow. " + `Error: ${error instanceof Error ? error.message : String(error)}`);
|
|
63
61
|
}
|
|
64
62
|
}
|
|
65
|
-
validateGatewayConfig(config) {
|
|
66
|
-
const missingVars = [];
|
|
67
|
-
if (!config.functionsDomain) {
|
|
68
|
-
missingVars.push("FUNCTIONS_DOMAIN");
|
|
69
|
-
}
|
|
70
|
-
if (!config.workspaceId) {
|
|
71
|
-
missingVars.push("INTUNED_WORKSPACE_ID");
|
|
72
|
-
}
|
|
73
|
-
if (!config.integrationId) {
|
|
74
|
-
missingVars.push("INTUNED_PROJECT_ID");
|
|
75
|
-
}
|
|
76
|
-
if (missingVars.length > 0) {
|
|
77
|
-
throw new Error(`Gateway configuration is incomplete. Missing environment variables: ${missingVars.join(", ")}. ` + "These are required when running in Intuned context without an API key.");
|
|
78
|
-
}
|
|
79
|
-
}
|
|
80
|
-
getDefaultConfig() {
|
|
81
|
-
return {
|
|
82
|
-
functionsDomain: process.env.FUNCTIONS_DOMAIN,
|
|
83
|
-
workspaceId: process.env.INTUNED_WORKSPACE_ID,
|
|
84
|
-
integrationId: process.env.INTUNED_PROJECT_ID || process.env.INTUNED_INTEGRATION_ID || undefined
|
|
85
|
-
};
|
|
86
|
-
}
|
|
87
63
|
detectProvider(model) {
|
|
88
64
|
const modelLower = model.toLowerCase();
|
|
89
65
|
return (0, _getModelProvider.getModelProvider)(modelLower);
|
|
90
66
|
}
|
|
91
|
-
|
|
92
|
-
var _this$config$function;
|
|
93
|
-
if (!this.config) {
|
|
94
|
-
throw new Error("Gateway configuration not initialized");
|
|
95
|
-
}
|
|
96
|
-
const baseDomain = (_this$config$function = this.config.functionsDomain) === null || _this$config$function === void 0 ? void 0 : _this$config$function.replace(/\/$/, "");
|
|
97
|
-
return `${baseDomain}/api/${this.config.workspaceId}/functions/${this.config.integrationId}/intuned-ai-gateway`;
|
|
98
|
-
}
|
|
99
|
-
getModelConfig(extraHeaders) {
|
|
67
|
+
async getModelConfig(extraHeaders) {
|
|
100
68
|
if (!this.useGateway && this.apiKey) {
|
|
101
69
|
return {
|
|
102
70
|
model: this.model,
|
|
@@ -105,23 +73,31 @@ class APIGateway {
|
|
|
105
73
|
baseUrl: undefined
|
|
106
74
|
};
|
|
107
75
|
}
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
76
|
+
try {
|
|
77
|
+
const getAiGatewayConfig = await (0, _loadRuntime.loadGetAiGatewayConfig)();
|
|
78
|
+
const {
|
|
79
|
+
baseUrl,
|
|
80
|
+
apiKey
|
|
81
|
+
} = getAiGatewayConfig();
|
|
82
|
+
return {
|
|
83
|
+
model: this.model,
|
|
84
|
+
apiKey,
|
|
85
|
+
extraHeaders,
|
|
86
|
+
baseUrl
|
|
87
|
+
};
|
|
88
|
+
} catch (error) {
|
|
89
|
+
throw new Error("Failed to load gateway configuration from @intuned/runtime. " + `Error: ${error instanceof Error ? error.message : String(error)}`);
|
|
90
|
+
}
|
|
115
91
|
}
|
|
116
92
|
async createProviderInstance(extraHeaders) {
|
|
117
93
|
await this.ensureInitialized();
|
|
118
|
-
const
|
|
94
|
+
const modelConfig = await this.getModelConfig(extraHeaders);
|
|
119
95
|
const provider = this.detectProvider(this.model);
|
|
120
96
|
const input = {
|
|
121
|
-
apiKey:
|
|
122
|
-
headers:
|
|
123
|
-
model:
|
|
124
|
-
baseUrl:
|
|
97
|
+
apiKey: modelConfig.apiKey,
|
|
98
|
+
headers: modelConfig.extraHeaders,
|
|
99
|
+
model: modelConfig.model,
|
|
100
|
+
baseUrl: modelConfig.baseUrl
|
|
125
101
|
};
|
|
126
102
|
const providerFactories = {
|
|
127
103
|
anthropic: () => (0, _Anthropic.createAnthropicInstance)(input),
|
|
@@ -16,8 +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,
|
|
22
|
+
baseURL: baseURLWithV1,
|
|
21
23
|
fetch: _jwtTokenManager.backendFunctionsTokenManager.fetchWithToken.bind(_jwtTokenManager.backendFunctionsTokenManager)
|
|
22
24
|
});
|
|
23
25
|
}
|
|
@@ -19,6 +19,7 @@ const createGoogleInstance = input => {
|
|
|
19
19
|
});
|
|
20
20
|
} else {
|
|
21
21
|
return (0, _google.createGoogleGenerativeAI)({
|
|
22
|
+
apiKey,
|
|
22
23
|
headers,
|
|
23
24
|
baseURL: baseUrl,
|
|
24
25
|
fetch: _jwtTokenManager.backendFunctionsTokenManager.fetchWithToken.bind(_jwtTokenManager.backendFunctionsTokenManager)
|
|
@@ -19,6 +19,7 @@ const createOpenAIInstance = input => {
|
|
|
19
19
|
});
|
|
20
20
|
} else {
|
|
21
21
|
return (0, _openai.createOpenAI)({
|
|
22
|
+
apiKey,
|
|
22
23
|
headers,
|
|
23
24
|
baseURL: baseUrl,
|
|
24
25
|
fetch: _jwtTokenManager.backendFunctionsTokenManager.fetchWithToken.bind(_jwtTokenManager.backendFunctionsTokenManager)
|
|
@@ -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",
|