Skip to content

Commit 6ab5a05

Browse files
authored
Merge pull request #288 from shivasurya/shiva/grok-integ
feat: add Grok-4 AI model support with 2M context window and OpenAI-compatible API client
2 parents b12961f + d86c6e9 commit 6ab5a05

File tree

7 files changed

+151
-6
lines changed

7 files changed

+151
-6
lines changed

extension/secureflow/packages/secureflow-cli/config/model-context-limits.json

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -82,10 +82,17 @@
8282
"maxOutput": 8192,
8383
"description": "Claude 3.5 Haiku with 200K context window, 8K max output tokens"
8484
}
85+
},
86+
"xai": {
87+
"grok-4-fast-reasoning": {
88+
"contextWindow": 2000000,
89+
"maxOutput": 65536,
90+
"description": "Grok-4 fast reasoning model with 2M context window, 65K max output tokens"
91+
}
8592
}
8693
},
8794
"metadata": {
88-
"lastUpdated": "2025-09-07",
95+
"lastUpdated": "2025-09-28",
8996
"source": "Verified against official API documentation and provider websites",
9097
"notes": [
9198
"Context windows are measured in tokens",
@@ -98,10 +105,13 @@
98105
"All specifications verified as of September 2025",
99106
"Claude Opus models have 64K max output, not 32K as previously listed",
100107
"O3-mini has significantly higher limits than O1-mini",
101-
"Gemini models use 65,535 max output tokens, not 65,536"
108+
"Gemini models use 65,535 max output tokens, not 65,536",
109+
"Grok-4-fast-reasoning has 2M context window with 4M tokens/minute rate limit (verified from xAI docs)",
110+
"Grok max output tokens estimated at 65K (not specified in official docs)"
102111
],
103112
"recommendations": {
104113
"largeContext": [
114+
"grok-4-fast-reasoning",
105115
"gpt-4.1-2025-04-14",
106116
"gemini-2.5-pro",
107117
"gemini-2.5-flash"
@@ -119,7 +129,8 @@
119129
"reasoning": [
120130
"o1",
121131
"o1-mini",
122-
"o3-mini-2025-01-31"
132+
"o3-mini-2025-01-31",
133+
"grok-4-fast-reasoning"
123134
],
124135
"highOutput": [
125136
"o3-mini-2025-01-31",

extension/secureflow/packages/secureflow-cli/lib/ai-client-factory.js

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ const { ClaudeClient } = require('./claude-client');
33
const { GeminiClient } = require('./gemini-client');
44
const { OpenAIClient } = require('./openai-client');
55
const { OllamaClient } = require('./ollama-client');
6+
const { GrokClient } = require('./grok-client');
67

78
/**
89
* Factory class for creating AI clients
@@ -38,6 +39,10 @@ class AIClientFactory {
3839
case 'claude-3-5-haiku-20241022':
3940
return new ClaudeClient();
4041

42+
// Grok (xAI) models
43+
case 'grok-4-fast-reasoning':
44+
return new GrokClient();
45+
4146
// Ollama models
4247
case 'qwen3:4b':
4348
return new OllamaClient();
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
import { AIClient, AIClientOptions, AIResponse, AIResponseChunk } from './ai-client';
2+
import { HttpClient } from './http-client';
3+
4+
export declare class GrokClient extends HttpClient implements AIClient {
5+
constructor();
6+
sendRequest(prompt?: string, options?: AIClientOptions, messages?: any): Promise<AIResponse>;
7+
sendStreamingRequest(
8+
prompt?: string,
9+
callback: (chunk: AIResponseChunk) => void,
10+
options?: AIClientOptions,
11+
messages?: any
12+
): Promise<void>;
13+
}
Lines changed: 109 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,109 @@
1+
const { HttpClient } = require('./http-client');
2+
3+
class GrokClient extends HttpClient {
4+
constructor() {
5+
super();
6+
this.API_URL = 'https://api.x.ai/v1/chat/completions';
7+
this.defaultModel = 'grok-4-fast-reasoning';
8+
}
9+
10+
/**
11+
* Send a request to the xAI Grok API (OpenAI-compatible)
12+
* @param {string} prompt The prompt to send
13+
* @param {import('./ai-client').AIClientOptions} options Options
14+
* @param {import('./ai-client').AIMessage[]} [messages] Optional messages array
15+
* @returns {Promise<import('./ai-client').AIResponse>} The AI response
16+
*/
17+
async sendRequest(prompt, options, messages) {
18+
if (!options?.apiKey) {
19+
throw new Error('xAI Grok API key is required');
20+
}
21+
22+
const response = await this.post(
23+
this.API_URL,
24+
{
25+
model: options.model || this.defaultModel,
26+
messages: messages || [{ role: 'user', content: prompt }],
27+
temperature: options.temperature ?? 0,
28+
max_tokens: options.maxTokens ?? 2000,
29+
stream: false
30+
},
31+
{
32+
Authorization: `Bearer ${options.apiKey}`,
33+
'Content-Type': 'application/json'
34+
}
35+
);
36+
37+
return {
38+
content: response?.choices?.[0]?.message?.content ?? '',
39+
model: response.model,
40+
provider: 'grok',
41+
usage: response.usage
42+
};
43+
}
44+
45+
/**
46+
* Send a streaming request to the xAI Grok API (SSE, OpenAI-compatible)
47+
* @param {string} prompt The prompt to send
48+
* @param {function(import('./ai-client').AIResponseChunk): void} callback Callback for each chunk
49+
* @param {import('./ai-client').AIClientOptions} options Options
50+
* @param {import('./ai-client').AIMessage[]} [messages] Optional messages array
51+
* @returns {Promise<void>}
52+
*/
53+
async sendStreamingRequest(prompt, callback, options, messages) {
54+
if (!options?.apiKey) {
55+
throw new Error('xAI Grok API key is required');
56+
}
57+
58+
let contentSoFar = '';
59+
60+
await this.streamingPost(
61+
this.API_URL,
62+
{
63+
model: options.model || this.defaultModel,
64+
messages: messages || [{ role: 'user', content: prompt }],
65+
temperature: options.temperature ?? 0,
66+
max_tokens: options.maxTokens ?? 2000,
67+
stream: true
68+
},
69+
(chunk) => {
70+
try {
71+
const lines = chunk.split('\n').filter((line) => line.trim() !== '');
72+
for (const line of lines) {
73+
if (line.startsWith('data: ')) {
74+
const data = line.slice(6);
75+
if (data === '[DONE]') {
76+
callback({ content: contentSoFar, isComplete: true });
77+
return;
78+
}
79+
try {
80+
const parsed = JSON.parse(data);
81+
if (parsed.choices && parsed.choices[0]?.delta?.content) {
82+
const piece = parsed.choices[0].delta.content;
83+
contentSoFar += piece;
84+
callback({ content: contentSoFar, isComplete: false });
85+
}
86+
} catch (e) {
87+
console.error('Error parsing SSE data:', e);
88+
}
89+
}
90+
}
91+
} catch (error) {
92+
console.error('Error processing chunk:', error);
93+
}
94+
},
95+
() => {
96+
callback({ content: contentSoFar, isComplete: true });
97+
},
98+
{
99+
Authorization: `Bearer ${options.apiKey}`,
100+
'Content-Type': 'application/json',
101+
Accept: 'text/event-stream'
102+
}
103+
);
104+
}
105+
}
106+
107+
module.exports = {
108+
GrokClient
109+
};

extension/secureflow/packages/secureflow-cli/lib/token-tracker.js

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -137,10 +137,16 @@ class TokenTracker {
137137
inputTokens = usage.prompt_tokens || 0;
138138
outputTokens = usage.completion_tokens || 0;
139139
}
140+
// xAI Grok format (OpenAI-compatible but may have specific fields)
141+
else if (usage.prompt_tokens !== undefined && usage.completion_tokens !== undefined) {
142+
inputTokens = usage.prompt_tokens || 0;
143+
outputTokens = usage.completion_tokens || 0;
144+
reasoningTokens = usage.reasoning_tokens || 0; // Grok reasoning tokens
145+
}
140146
// Fallback - try both naming conventions
141147
else {
142-
inputTokens = usage.input_tokens || usage.promptTokenCount || 0;
143-
outputTokens = usage.output_tokens || usage.candidatesTokenCount || 0;
148+
inputTokens = usage.input_tokens || usage.promptTokenCount || usage.prompt_tokens || 0;
149+
outputTokens = usage.output_tokens || usage.candidatesTokenCount || usage.completion_tokens || 0;
144150
reasoningTokens = usage.reasoning_tokens || usage.thoughtsTokenCount || 0;
145151
}
146152

extension/secureflow/packages/secureflow-cli/lib/types.d.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,4 +17,5 @@ export type AIModel =
1717
| 'claude-3-7-sonnet-20250219'
1818
| 'claude-3-5-sonnet-20241022'
1919
| 'claude-3-5-haiku-20241022'
20+
| 'grok-4-fast-reasoning'
2021
| 'qwen3:4b';

extension/secureflow/packages/secureflow-cli/lib/types.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
*/
44

55
/**
6-
* @typedef {'gpt-4o' | 'gpt-4o-mini' | 'o1-mini' | 'o1' | 'gpt-4.1-2025-04-14' | 'o3-mini-2025-01-31' | 'gemini-2.5-pro' | 'gemini-2.5-flash' | 'claude-opus-4-1-20250805' | 'claude-opus-4-20250514' | 'claude-sonnet-4-20250514' | 'claude-3-7-sonnet-20250219' | 'claude-3-5-sonnet-20241022' | 'claude-3-5-haiku-20241022' | 'qwen3:4b'} AIModel
6+
* @typedef {'gpt-4o' | 'gpt-4o-mini' | 'o1-mini' | 'o1' | 'gpt-4.1-2025-04-14' | 'o3-mini-2025-01-31' | 'gemini-2.5-pro' | 'gemini-2.5-flash' | 'claude-opus-4-1-20250805' | 'claude-opus-4-20250514' | 'claude-sonnet-4-20250514' | 'claude-3-7-sonnet-20250219' | 'claude-3-5-sonnet-20241022' | 'claude-3-5-haiku-20241022' | 'grok-4-fast-reasoning' | 'qwen3:4b'} AIModel
77
*/
88

99
module.exports = {

0 commit comments

Comments
 (0)