Skip to content

Commit 48f913b

Browse files
danielsogldevversion
authored andcommitted
feat: add grok provider
1 parent 3d9c7b1 commit 48f913b

File tree

3 files changed

+116
-0
lines changed

3 files changed

+116
-0
lines changed

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ npm install -g web-codegen-scorer
3838
export GEMINI_API_KEY="YOUR_API_KEY_HERE" # If you're using Gemini models
3939
export OPENAI_API_KEY="YOUR_API_KEY_HERE" # If you're using OpenAI models
4040
export ANTHROPIC_API_KEY="YOUR_API_KEY_HERE" # If you're using Anthropic models
41+
export XAI_API_KEY="YOUR_API_KEY_HERE" # If you're using xAI Grok models
4142
```
4243

4344
3. **Run an eval:**

runner/codegen/genkit/models.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,11 @@
11
import { GeminiModelProvider } from './providers/gemini.js';
22
import { ClaudeModelProvider } from './providers/claude.js';
33
import { OpenAiModelProvider } from './providers/open-ai.js';
4+
import { GrokModelProvider } from './providers/grok.js';
45

56
export const MODEL_PROVIDERS = [
67
new GeminiModelProvider(),
78
new ClaudeModelProvider(),
89
new OpenAiModelProvider(),
10+
new GrokModelProvider(),
911
];
Lines changed: 113 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,113 @@
1+
import { xAI } from '@genkit-ai/compat-oai/xai';
2+
import { GenkitPlugin, GenkitPluginV2 } from 'genkit/plugin';
3+
import { RateLimiter } from 'limiter';
4+
import fetch from 'node-fetch';
5+
import {
6+
GenkitModelProvider,
7+
PromptDataForCounting,
8+
RateLimitConfig,
9+
} from '../model-provider.js';
10+
11+
export class GrokModelProvider extends GenkitModelProvider {
12+
readonly apiKeyVariableName = 'XAI_API_KEY';
13+
14+
protected readonly models = {
15+
'grok-4': () => xAI.model('grok-4'),
16+
'grok-code-fast-1': () => xAI.model('grok-code-fast-1'),
17+
};
18+
19+
private async countTokensWithXaiApi(
20+
prompt: PromptDataForCounting
21+
): Promise<number | null> {
22+
const apiKey = this.getApiKey();
23+
if (!apiKey) {
24+
return null;
25+
}
26+
27+
try {
28+
// Use xAI's tokenize API for accurate token counting
29+
const messages = this.genkitPromptToXaiFormat(prompt);
30+
const text = messages.map((m) => `${m.role}: ${m.content}`).join('\n');
31+
32+
const response = await fetch('https://api.x.ai/v1/tokenize', {
33+
method: 'POST',
34+
headers: {
35+
'Content-Type': 'application/json',
36+
Authorization: `Bearer ${apiKey}`,
37+
},
38+
body: JSON.stringify({ text }),
39+
});
40+
41+
if (response.ok) {
42+
const data = (await response.json()) as { tokens: unknown[] };
43+
return data.tokens?.length || 0;
44+
}
45+
return null;
46+
} catch (error) {
47+
console.warn('Failed to count tokens using xAI API', error);
48+
return null;
49+
}
50+
}
51+
52+
private async countTokensForModel(
53+
_modelName: string,
54+
prompt: PromptDataForCounting
55+
): Promise<number> {
56+
const xaiTokenCount = await this.countTokensWithXaiApi(prompt);
57+
if (xaiTokenCount !== null) {
58+
return xaiTokenCount;
59+
}
60+
return 0;
61+
}
62+
63+
protected rateLimitConfig: Record<string, RateLimitConfig> = {
64+
// XAI Grok rate limits https://docs.x.ai/docs/models
65+
'xai/grok-4': {
66+
requestPerMinute: new RateLimiter({
67+
tokensPerInterval: 480,
68+
interval: 1000 * 60 * 1.5, // Refresh tokens after 1.5 minutes to be on the safe side
69+
}),
70+
tokensPerMinute: new RateLimiter({
71+
tokensPerInterval: 2_000_000 * 0.75,
72+
interval: 1000 * 60 * 1.5, // Refresh tokens after 1.5 minutes to be on the safe side
73+
}),
74+
countTokens: (prompt) => this.countTokensForModel('grok-4', prompt),
75+
},
76+
'xai/grok-code-fast-1': {
77+
requestPerMinute: new RateLimiter({
78+
tokensPerInterval: 480,
79+
interval: 1000 * 60 * 1.5, // Refresh tokens after 1.5 minutes to be on the safe side
80+
}),
81+
tokensPerMinute: new RateLimiter({
82+
tokensPerInterval: 2_000_000 * 0.75,
83+
interval: 1000 * 60 * 1.5, // Refresh tokens after 1.5 minutes to be on the safe side
84+
}),
85+
countTokens: (prompt) =>
86+
this.countTokensForModel('grok-code-fast-1', prompt),
87+
},
88+
};
89+
90+
protected pluginFactory(apiKey: string): GenkitPlugin | GenkitPluginV2 {
91+
return xAI({ apiKey });
92+
}
93+
94+
getModelSpecificConfig(): object {
95+
// Grok doesn't require special configuration at this time
96+
return {};
97+
}
98+
99+
private genkitPromptToXaiFormat(
100+
prompt: PromptDataForCounting
101+
): Array<{ role: string; content: string }> {
102+
const xaiPrompt: Array<{ role: string; content: string }> = [];
103+
for (const part of prompt.messages) {
104+
for (const c of part.content) {
105+
xaiPrompt.push({
106+
role: part.role,
107+
content: 'media' in c ? c.media.url : c.text,
108+
});
109+
}
110+
}
111+
return [...xaiPrompt, { role: 'user', content: prompt.prompt }];
112+
}
113+
}

0 commit comments

Comments
 (0)