feat: add xAI Grok adapters
This commit is contained in:
147
tests/grok.test.ts
Normal file
147
tests/grok.test.ts
Normal file
@@ -0,0 +1,147 @@
|
||||
import { describe, expect, it } from 'vitest';
|
||||
import {
|
||||
createGrokAdapters,
|
||||
createGrokImageModelAdapter,
|
||||
createGrokStructuredModelAdapter,
|
||||
createGrokTextModelAdapter,
|
||||
} from '../src';
|
||||
|
||||
describe('Grok adapters', () => {
|
||||
it('creates a text adapter that calls xAI chat completions', async () => {
|
||||
const calls: Array<{ url: string; init?: RequestInit }> = [];
|
||||
const adapter = createGrokTextModelAdapter({
|
||||
apiKey: 'test-key',
|
||||
model: 'grok-4.3-mini',
|
||||
fetch: async (url, init) => {
|
||||
calls.push({ url: String(url), ...(init ? { init } : {}) });
|
||||
return new Response(JSON.stringify({
|
||||
choices: [
|
||||
{
|
||||
message: {
|
||||
content: 'hello from grok',
|
||||
},
|
||||
},
|
||||
],
|
||||
}), {
|
||||
status: 200,
|
||||
headers: { 'content-type': 'application/json' },
|
||||
});
|
||||
},
|
||||
});
|
||||
|
||||
await expect(adapter.generateText({
|
||||
system: 'You are a helpful persona engine.',
|
||||
prompt: 'Say hi.',
|
||||
temperature: 0.3,
|
||||
})).resolves.toBe('hello from grok');
|
||||
|
||||
expect(calls).toHaveLength(1);
|
||||
expect(calls[0]?.url).toBe('https://api.x.ai/v1/chat/completions');
|
||||
expect(calls[0]?.init?.method).toBe('POST');
|
||||
expect(calls[0]?.init?.headers).toMatchObject({
|
||||
authorization: 'Bearer test-key',
|
||||
'content-type': 'application/json',
|
||||
});
|
||||
|
||||
const body = JSON.parse(String(calls[0]?.init?.body));
|
||||
expect(body.model).toBe('grok-4.3-mini');
|
||||
expect(body.temperature).toBe(0.3);
|
||||
expect(body.messages).toEqual([
|
||||
{ role: 'system', content: 'You are a helpful persona engine.' },
|
||||
{ role: 'user', content: 'Say hi.' },
|
||||
]);
|
||||
});
|
||||
|
||||
it('creates a structured adapter that sends json_schema response_format and parses JSON', async () => {
|
||||
const calls: Array<{ url: string; init?: RequestInit }> = [];
|
||||
const adapter = createGrokStructuredModelAdapter({
|
||||
apiKey: 'test-key',
|
||||
model: 'grok-4.3',
|
||||
fetch: async (url, init) => {
|
||||
calls.push({ url: String(url), ...(init ? { init } : {}) });
|
||||
return new Response(JSON.stringify({
|
||||
choices: [
|
||||
{
|
||||
message: {
|
||||
content: JSON.stringify({ biography: 'Born in Busan.' }),
|
||||
},
|
||||
},
|
||||
],
|
||||
}), {
|
||||
status: 200,
|
||||
headers: { 'content-type': 'application/json' },
|
||||
});
|
||||
},
|
||||
});
|
||||
|
||||
const schema = {
|
||||
type: 'object',
|
||||
required: ['biography'],
|
||||
properties: { biography: { type: 'string' } },
|
||||
};
|
||||
|
||||
await expect(adapter.generateObject<{ biography: string }>({
|
||||
prompt: 'Write a biography.',
|
||||
system: 'Return only JSON.',
|
||||
schema,
|
||||
})).resolves.toEqual({ biography: 'Born in Busan.' });
|
||||
|
||||
const body = JSON.parse(String(calls[0]?.init?.body));
|
||||
expect(body.response_format).toEqual({
|
||||
type: 'json_schema',
|
||||
json_schema: {
|
||||
name: 'boxbrain_structured_output',
|
||||
schema,
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
it('creates an image adapter that calls xAI image generation with mapped aspect ratios', async () => {
|
||||
const calls: Array<{ url: string; init?: RequestInit }> = [];
|
||||
const adapter = createGrokImageModelAdapter({
|
||||
apiKey: 'test-key',
|
||||
model: 'grok-imagine-image-quality',
|
||||
fetch: async (url, init) => {
|
||||
calls.push({ url: String(url), ...(init ? { init } : {}) });
|
||||
return new Response(JSON.stringify({
|
||||
data: [{ url: 'https://cdn.x.ai/generated.png', revised_prompt: 'revised prompt' }],
|
||||
}), {
|
||||
status: 200,
|
||||
headers: { 'content-type': 'application/json' },
|
||||
});
|
||||
},
|
||||
});
|
||||
|
||||
await expect(adapter.generateImage({
|
||||
prompt: 'A quiet portrait photo.',
|
||||
aspectRatio: 'portrait',
|
||||
})).resolves.toEqual({
|
||||
url: 'https://cdn.x.ai/generated.png',
|
||||
revisedPrompt: 'revised prompt',
|
||||
});
|
||||
|
||||
expect(calls[0]?.url).toBe('https://api.x.ai/v1/images/generations');
|
||||
const body = JSON.parse(String(calls[0]?.init?.body));
|
||||
expect(body.aspect_ratio).toBe('3:4');
|
||||
expect(body.model).toBe('grok-imagine-image-quality');
|
||||
});
|
||||
|
||||
it('can create a bundled Grok adapter set with shared defaults', async () => {
|
||||
const adapters = createGrokAdapters({
|
||||
apiKey: 'bundle-key',
|
||||
textModel: 'grok-4.3-mini',
|
||||
structuredModel: 'grok-4.3',
|
||||
imageModel: 'grok-imagine-image-quality',
|
||||
fetch: async (_url, _init) => new Response(JSON.stringify({
|
||||
choices: [{ message: { content: 'ok' } }],
|
||||
}), {
|
||||
status: 200,
|
||||
headers: { 'content-type': 'application/json' },
|
||||
}),
|
||||
});
|
||||
|
||||
expect(adapters.text.provider).toBe('xai-grok');
|
||||
expect(adapters.structured.model).toBe('grok-4.3');
|
||||
expect(adapters.image.model).toBe('grok-imagine-image-quality');
|
||||
});
|
||||
});
|
||||
@@ -1,5 +1,6 @@
|
||||
import { describe, expect, it } from 'vitest';
|
||||
import {
|
||||
createGrokAdapters,
|
||||
createReplyDelay,
|
||||
createTypingDelay,
|
||||
generateSchedule,
|
||||
@@ -38,7 +39,7 @@ describe('public API', () => {
|
||||
expect(fact.topics.map((topic) => topic.name)).toEqual(['Mina', 'quiet cafés']);
|
||||
});
|
||||
|
||||
it('exports schedule, conversation, and external special-date adapter contracts', () => {
|
||||
it('exports schedule, conversation, Grok, and external special-date adapter contracts', () => {
|
||||
const specialDateProvider: SpecialDateProvider = {
|
||||
async listSpecialDates() {
|
||||
return [{ date: '2026-05-08', title: 'Parents Day' }];
|
||||
@@ -47,6 +48,7 @@ describe('public API', () => {
|
||||
|
||||
expect(typeof generateSchedule).toBe('function');
|
||||
expect(typeof replyToConversation).toBe('function');
|
||||
expect(typeof createGrokAdapters).toBe('function');
|
||||
expect(specialDateProvider.listSpecialDates).toBeTypeOf('function');
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user