@xenterprises/fastify-x-ai 1.1.0 → 1.1.1
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/package.json +1 -1
- package/test/xAI.test.js +0 -280
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@xenterprises/fastify-x-ai",
|
|
3
|
-
"version": "1.1.
|
|
3
|
+
"version": "1.1.1",
|
|
4
4
|
"description": "Fastify plugin for Vercel AI SDK - unified AI provider access with text generation, streaming, embeddings, and structured output",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "src/xAI.js",
|
package/test/xAI.test.js
DELETED
|
@@ -1,280 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* xAI Tests
|
|
3
|
-
*
|
|
4
|
-
* Tests for the xAI Fastify plugin (Vercel AI SDK integration)
|
|
5
|
-
* Note: These are unit tests that don't require actual API calls
|
|
6
|
-
*/
|
|
7
|
-
|
|
8
|
-
import { describe, test, beforeEach, afterEach } from "node:test";
|
|
9
|
-
import assert from "node:assert";
|
|
10
|
-
import Fastify from "fastify";
|
|
11
|
-
import xAI, { DEFAULT_MODELS } from "../src/xAI.js";
|
|
12
|
-
|
|
13
|
-
describe("xAI Plugin", () => {
|
|
14
|
-
let fastify;
|
|
15
|
-
|
|
16
|
-
beforeEach(() => {
|
|
17
|
-
fastify = Fastify({ logger: { level: "silent" } });
|
|
18
|
-
});
|
|
19
|
-
|
|
20
|
-
afterEach(async () => {
|
|
21
|
-
await fastify.close();
|
|
22
|
-
});
|
|
23
|
-
|
|
24
|
-
describe("Plugin Registration", () => {
|
|
25
|
-
test("should skip registration when active is false", async () => {
|
|
26
|
-
await fastify.register(xAI, { active: false });
|
|
27
|
-
await fastify.ready();
|
|
28
|
-
|
|
29
|
-
assert.ok(!fastify.xai, "xai should not exist");
|
|
30
|
-
});
|
|
31
|
-
|
|
32
|
-
test("should register with OpenAI provider from env", async () => {
|
|
33
|
-
// Set env var temporarily
|
|
34
|
-
const originalKey = process.env.OPENAI_API_KEY;
|
|
35
|
-
process.env.OPENAI_API_KEY = "test-key-12345";
|
|
36
|
-
|
|
37
|
-
try {
|
|
38
|
-
await fastify.register(xAI, {});
|
|
39
|
-
await fastify.ready();
|
|
40
|
-
|
|
41
|
-
assert.ok(fastify.xai, "xai should exist");
|
|
42
|
-
assert.ok(fastify.xai.config, "xai.config should exist");
|
|
43
|
-
assert.ok(fastify.xai.generate, "xai.generate should exist");
|
|
44
|
-
assert.ok(fastify.xai.stream, "xai.stream should exist");
|
|
45
|
-
assert.ok(fastify.xai.chat, "xai.chat should exist");
|
|
46
|
-
assert.ok(fastify.xai.complete, "xai.complete should exist");
|
|
47
|
-
assert.ok(fastify.xai.createEmbedding, "xai.createEmbedding should exist");
|
|
48
|
-
assert.ok(fastify.xai.similarity, "xai.similarity should exist");
|
|
49
|
-
assert.ok(fastify.xai.generateStructured, "xai.generateStructured should exist");
|
|
50
|
-
assert.ok(fastify.xai.getModel, "xai.getModel should exist");
|
|
51
|
-
assert.ok(fastify.xai.raw, "xai.raw should exist");
|
|
52
|
-
} finally {
|
|
53
|
-
if (originalKey) {
|
|
54
|
-
process.env.OPENAI_API_KEY = originalKey;
|
|
55
|
-
} else {
|
|
56
|
-
delete process.env.OPENAI_API_KEY;
|
|
57
|
-
}
|
|
58
|
-
}
|
|
59
|
-
});
|
|
60
|
-
|
|
61
|
-
test("should register with explicit provider config", async () => {
|
|
62
|
-
await fastify.register(xAI, {
|
|
63
|
-
providers: {
|
|
64
|
-
openai: { apiKey: "test-api-key" },
|
|
65
|
-
},
|
|
66
|
-
});
|
|
67
|
-
await fastify.ready();
|
|
68
|
-
|
|
69
|
-
assert.ok(fastify.xai, "xai should exist");
|
|
70
|
-
assert.ok(fastify.xai.config.providers.includes("openai"), "openai should be in providers");
|
|
71
|
-
});
|
|
72
|
-
|
|
73
|
-
test("should store default configuration", async () => {
|
|
74
|
-
await fastify.register(xAI, {
|
|
75
|
-
defaultProvider: "openai",
|
|
76
|
-
defaultMaxTokens: 2048,
|
|
77
|
-
defaultTemperature: 0.5,
|
|
78
|
-
providers: {
|
|
79
|
-
openai: { apiKey: "test-key" },
|
|
80
|
-
},
|
|
81
|
-
});
|
|
82
|
-
await fastify.ready();
|
|
83
|
-
|
|
84
|
-
assert.strictEqual(fastify.xai.config.defaultProvider, "openai");
|
|
85
|
-
assert.strictEqual(fastify.xai.config.defaultMaxTokens, 2048);
|
|
86
|
-
assert.strictEqual(fastify.xai.config.defaultTemperature, 0.5);
|
|
87
|
-
});
|
|
88
|
-
|
|
89
|
-
test("should use default values when not specified", async () => {
|
|
90
|
-
await fastify.register(xAI, {
|
|
91
|
-
providers: {
|
|
92
|
-
openai: { apiKey: "test-key" },
|
|
93
|
-
},
|
|
94
|
-
});
|
|
95
|
-
await fastify.ready();
|
|
96
|
-
|
|
97
|
-
assert.strictEqual(fastify.xai.config.defaultProvider, "openai");
|
|
98
|
-
assert.strictEqual(fastify.xai.config.defaultMaxTokens, 4096);
|
|
99
|
-
assert.strictEqual(fastify.xai.config.defaultTemperature, 0.7);
|
|
100
|
-
});
|
|
101
|
-
});
|
|
102
|
-
|
|
103
|
-
describe("Model Selection", () => {
|
|
104
|
-
test("should get model for configured provider", async () => {
|
|
105
|
-
await fastify.register(xAI, {
|
|
106
|
-
providers: {
|
|
107
|
-
openai: { apiKey: "test-key" },
|
|
108
|
-
},
|
|
109
|
-
});
|
|
110
|
-
await fastify.ready();
|
|
111
|
-
|
|
112
|
-
const model = fastify.xai.getModel("openai", "gpt-4o");
|
|
113
|
-
assert.ok(model, "model should exist");
|
|
114
|
-
});
|
|
115
|
-
|
|
116
|
-
test("should throw for unconfigured provider", async () => {
|
|
117
|
-
await fastify.register(xAI, {
|
|
118
|
-
providers: {
|
|
119
|
-
openai: { apiKey: "test-key" },
|
|
120
|
-
},
|
|
121
|
-
});
|
|
122
|
-
await fastify.ready();
|
|
123
|
-
|
|
124
|
-
assert.throws(() => {
|
|
125
|
-
fastify.xai.getModel("anthropic", "claude-3");
|
|
126
|
-
}, /Provider 'anthropic' not configured/);
|
|
127
|
-
});
|
|
128
|
-
|
|
129
|
-
test("should use default provider when not specified", async () => {
|
|
130
|
-
await fastify.register(xAI, {
|
|
131
|
-
defaultProvider: "openai",
|
|
132
|
-
providers: {
|
|
133
|
-
openai: { apiKey: "test-key" },
|
|
134
|
-
},
|
|
135
|
-
});
|
|
136
|
-
await fastify.ready();
|
|
137
|
-
|
|
138
|
-
// Should not throw when provider not specified
|
|
139
|
-
const model = fastify.xai.getModel();
|
|
140
|
-
assert.ok(model, "model should exist");
|
|
141
|
-
});
|
|
142
|
-
});
|
|
143
|
-
|
|
144
|
-
describe("Raw SDK Access", () => {
|
|
145
|
-
test("should expose raw AI SDK functions", async () => {
|
|
146
|
-
await fastify.register(xAI, {
|
|
147
|
-
providers: {
|
|
148
|
-
openai: { apiKey: "test-key" },
|
|
149
|
-
},
|
|
150
|
-
});
|
|
151
|
-
await fastify.ready();
|
|
152
|
-
|
|
153
|
-
assert.ok(fastify.xai.raw, "raw should exist");
|
|
154
|
-
assert.ok(fastify.xai.raw.generateText, "generateText should exist");
|
|
155
|
-
assert.ok(fastify.xai.raw.streamText, "streamText should exist");
|
|
156
|
-
assert.ok(fastify.xai.raw.embed, "embed should exist");
|
|
157
|
-
assert.ok(fastify.xai.raw.embedMany, "embedMany should exist");
|
|
158
|
-
assert.ok(fastify.xai.raw.cosineSimilarity, "cosineSimilarity should exist");
|
|
159
|
-
});
|
|
160
|
-
});
|
|
161
|
-
|
|
162
|
-
describe("Similarity Function", () => {
|
|
163
|
-
test("should calculate cosine similarity", async () => {
|
|
164
|
-
await fastify.register(xAI, {
|
|
165
|
-
providers: {
|
|
166
|
-
openai: { apiKey: "test-key" },
|
|
167
|
-
},
|
|
168
|
-
});
|
|
169
|
-
await fastify.ready();
|
|
170
|
-
|
|
171
|
-
// Test with identical vectors - should be 1
|
|
172
|
-
const sim1 = fastify.xai.similarity([1, 0, 0], [1, 0, 0]);
|
|
173
|
-
assert.ok(Math.abs(sim1 - 1) < 0.001, "identical vectors should have similarity ~1");
|
|
174
|
-
|
|
175
|
-
// Test with orthogonal vectors - should be 0
|
|
176
|
-
const sim2 = fastify.xai.similarity([1, 0], [0, 1]);
|
|
177
|
-
assert.ok(Math.abs(sim2) < 0.001, "orthogonal vectors should have similarity ~0");
|
|
178
|
-
|
|
179
|
-
// Test with opposite vectors - should be -1
|
|
180
|
-
const sim3 = fastify.xai.similarity([1, 0], [-1, 0]);
|
|
181
|
-
assert.ok(Math.abs(sim3 + 1) < 0.001, "opposite vectors should have similarity ~-1");
|
|
182
|
-
});
|
|
183
|
-
});
|
|
184
|
-
|
|
185
|
-
describe("Provider Configuration", () => {
|
|
186
|
-
test("should list enabled providers", async () => {
|
|
187
|
-
await fastify.register(xAI, {
|
|
188
|
-
providers: {
|
|
189
|
-
openai: { apiKey: "test-key" },
|
|
190
|
-
},
|
|
191
|
-
});
|
|
192
|
-
await fastify.ready();
|
|
193
|
-
|
|
194
|
-
assert.ok(Array.isArray(fastify.xai.config.providers));
|
|
195
|
-
assert.ok(fastify.xai.config.providers.includes("openai"));
|
|
196
|
-
});
|
|
197
|
-
|
|
198
|
-
test("should have empty providers when none configured", async () => {
|
|
199
|
-
// Clear any env vars
|
|
200
|
-
const originalOpenAI = process.env.OPENAI_API_KEY;
|
|
201
|
-
const originalAnthropic = process.env.ANTHROPIC_API_KEY;
|
|
202
|
-
const originalGoogle = process.env.GOOGLE_API_KEY;
|
|
203
|
-
|
|
204
|
-
delete process.env.OPENAI_API_KEY;
|
|
205
|
-
delete process.env.ANTHROPIC_API_KEY;
|
|
206
|
-
delete process.env.GOOGLE_API_KEY;
|
|
207
|
-
|
|
208
|
-
try {
|
|
209
|
-
await fastify.register(xAI, {});
|
|
210
|
-
await fastify.ready();
|
|
211
|
-
|
|
212
|
-
assert.ok(Array.isArray(fastify.xai.config.providers));
|
|
213
|
-
assert.strictEqual(fastify.xai.config.providers.length, 0);
|
|
214
|
-
} finally {
|
|
215
|
-
if (originalOpenAI) process.env.OPENAI_API_KEY = originalOpenAI;
|
|
216
|
-
if (originalAnthropic) process.env.ANTHROPIC_API_KEY = originalAnthropic;
|
|
217
|
-
if (originalGoogle) process.env.GOOGLE_API_KEY = originalGoogle;
|
|
218
|
-
}
|
|
219
|
-
});
|
|
220
|
-
});
|
|
221
|
-
});
|
|
222
|
-
|
|
223
|
-
describe("Exports", () => {
|
|
224
|
-
test("should export DEFAULT_MODELS", () => {
|
|
225
|
-
assert.ok(DEFAULT_MODELS, "DEFAULT_MODELS should exist");
|
|
226
|
-
assert.strictEqual(DEFAULT_MODELS.openai, "gpt-4o");
|
|
227
|
-
assert.strictEqual(DEFAULT_MODELS.anthropic, "claude-sonnet-4-20250514");
|
|
228
|
-
assert.strictEqual(DEFAULT_MODELS.google, "gemini-2.0-flash");
|
|
229
|
-
});
|
|
230
|
-
});
|
|
231
|
-
|
|
232
|
-
describe("Method Signatures", () => {
|
|
233
|
-
let fastify;
|
|
234
|
-
|
|
235
|
-
beforeEach(async () => {
|
|
236
|
-
fastify = Fastify({ logger: { level: "silent" } });
|
|
237
|
-
await fastify.register(xAI, {
|
|
238
|
-
providers: {
|
|
239
|
-
openai: { apiKey: "test-key" },
|
|
240
|
-
},
|
|
241
|
-
});
|
|
242
|
-
await fastify.ready();
|
|
243
|
-
});
|
|
244
|
-
|
|
245
|
-
afterEach(async () => {
|
|
246
|
-
await fastify.close();
|
|
247
|
-
});
|
|
248
|
-
|
|
249
|
-
test("generate should be a function", () => {
|
|
250
|
-
assert.strictEqual(typeof fastify.xai.generate, "function");
|
|
251
|
-
});
|
|
252
|
-
|
|
253
|
-
test("stream should be a function", () => {
|
|
254
|
-
assert.strictEqual(typeof fastify.xai.stream, "function");
|
|
255
|
-
});
|
|
256
|
-
|
|
257
|
-
test("chat should be a function", () => {
|
|
258
|
-
assert.strictEqual(typeof fastify.xai.chat, "function");
|
|
259
|
-
});
|
|
260
|
-
|
|
261
|
-
test("complete should be a function", () => {
|
|
262
|
-
assert.strictEqual(typeof fastify.xai.complete, "function");
|
|
263
|
-
});
|
|
264
|
-
|
|
265
|
-
test("createEmbedding should be a function", () => {
|
|
266
|
-
assert.strictEqual(typeof fastify.xai.createEmbedding, "function");
|
|
267
|
-
});
|
|
268
|
-
|
|
269
|
-
test("similarity should be a function", () => {
|
|
270
|
-
assert.strictEqual(typeof fastify.xai.similarity, "function");
|
|
271
|
-
});
|
|
272
|
-
|
|
273
|
-
test("generateStructured should be a function", () => {
|
|
274
|
-
assert.strictEqual(typeof fastify.xai.generateStructured, "function");
|
|
275
|
-
});
|
|
276
|
-
|
|
277
|
-
test("getModel should be a function", () => {
|
|
278
|
-
assert.strictEqual(typeof fastify.xai.getModel, "function");
|
|
279
|
-
});
|
|
280
|
-
});
|