From d7ce19e15aba7d346310874fd89e397f3c77e970 Mon Sep 17 00:00:00 2001 From: Danny Avila Date: Sun, 30 Nov 2025 17:10:26 -0500 Subject: [PATCH] =?UTF-8?q?=F0=9F=A4=96=20feat:=20Latest=20Grok=20Model=20?= =?UTF-8?q?Pricing=20&=20Context=20Rates=20(#10727)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * 🤖 feat: Latest Grok Model Pricing & Context Rates - Introduced 'grok-4-fast', 'grok-4-1-fast', and 'grok-code-fast' models with their respective prompt and completion rates. - Enhanced unit tests to validate prompt and completion rates for the new models, including variations with prefixes. - Updated token limits for the new models in the tokens utility, ensuring accurate handling in tests. * 🔧 refactor: Optimize JSON Export Logic in useExportConversation Hook Updated the export logic to create a Blob from the JSON string before downloading, improving compatibility and performance for file downloads. This change enhances the handling of deeply nested exports while maintaining the file size reduction achieved in previous updates. --- api/models/tx.js | 3 + api/models/tx.spec.js | 66 +++++++++++++++++++ api/utils/tokens.spec.js | 28 ++++++++ .../Conversations/useExportConversation.ts | 4 +- packages/api/src/utils/tokens.ts | 3 + 5 files changed, 103 insertions(+), 1 deletion(-) diff --git a/api/models/tx.js b/api/models/tx.js index 328f2c2d4d..4ea93e1a1e 100644 --- a/api/models/tx.js +++ b/api/models/tx.js @@ -173,6 +173,9 @@ const tokenValues = Object.assign( 'grok-3-mini': { prompt: 0.3, completion: 0.5 }, 'grok-3-mini-fast': { prompt: 0.6, completion: 4 }, 'grok-4': { prompt: 3.0, completion: 15.0 }, + 'grok-4-fast': { prompt: 0.2, completion: 0.5 }, + 'grok-4-1-fast': { prompt: 0.2, completion: 0.5 }, // covers reasoning & non-reasoning variants + 'grok-code-fast': { prompt: 0.2, completion: 1.5 }, codestral: { prompt: 0.3, completion: 0.9 }, 'ministral-3b': { prompt: 0.04, completion: 0.04 }, 'ministral-8b': { prompt: 0.1, completion: 0.1 }, diff --git a/api/models/tx.spec.js b/api/models/tx.spec.js index b70f9572d0..75a8fa1922 100644 --- a/api/models/tx.spec.js +++ b/api/models/tx.spec.js @@ -1205,6 +1205,39 @@ describe('Grok Model Tests - Pricing', () => { ); }); + test('should return correct prompt and completion rates for Grok 4 Fast model', () => { + expect(getMultiplier({ model: 'grok-4-fast', tokenType: 'prompt' })).toBe( + tokenValues['grok-4-fast'].prompt, + ); + expect(getMultiplier({ model: 'grok-4-fast', tokenType: 'completion' })).toBe( + tokenValues['grok-4-fast'].completion, + ); + }); + + test('should return correct prompt and completion rates for Grok 4.1 Fast models', () => { + expect(getMultiplier({ model: 'grok-4-1-fast-reasoning', tokenType: 'prompt' })).toBe( + tokenValues['grok-4-1-fast'].prompt, + ); + expect(getMultiplier({ model: 'grok-4-1-fast-reasoning', tokenType: 'completion' })).toBe( + tokenValues['grok-4-1-fast'].completion, + ); + expect(getMultiplier({ model: 'grok-4-1-fast-non-reasoning', tokenType: 'prompt' })).toBe( + tokenValues['grok-4-1-fast'].prompt, + ); + expect(getMultiplier({ model: 'grok-4-1-fast-non-reasoning', tokenType: 'completion' })).toBe( + tokenValues['grok-4-1-fast'].completion, + ); + }); + + test('should return correct prompt and completion rates for Grok Code Fast model', () => { + expect(getMultiplier({ model: 'grok-code-fast-1', tokenType: 'prompt' })).toBe( + tokenValues['grok-code-fast'].prompt, + ); + expect(getMultiplier({ model: 'grok-code-fast-1', tokenType: 'completion' })).toBe( + tokenValues['grok-code-fast'].completion, + ); + }); + test('should return correct prompt and completion rates for Grok 3 models with prefixes', () => { expect(getMultiplier({ model: 'xai/grok-3', tokenType: 'prompt' })).toBe( tokenValues['grok-3'].prompt, @@ -1240,6 +1273,39 @@ describe('Grok Model Tests - Pricing', () => { tokenValues['grok-4'].completion, ); }); + + test('should return correct prompt and completion rates for Grok 4 Fast model with prefixes', () => { + expect(getMultiplier({ model: 'xai/grok-4-fast', tokenType: 'prompt' })).toBe( + tokenValues['grok-4-fast'].prompt, + ); + expect(getMultiplier({ model: 'xai/grok-4-fast', tokenType: 'completion' })).toBe( + tokenValues['grok-4-fast'].completion, + ); + }); + + test('should return correct prompt and completion rates for Grok 4.1 Fast models with prefixes', () => { + expect(getMultiplier({ model: 'xai/grok-4-1-fast-reasoning', tokenType: 'prompt' })).toBe( + tokenValues['grok-4-1-fast'].prompt, + ); + expect(getMultiplier({ model: 'xai/grok-4-1-fast-reasoning', tokenType: 'completion' })).toBe( + tokenValues['grok-4-1-fast'].completion, + ); + expect(getMultiplier({ model: 'xai/grok-4-1-fast-non-reasoning', tokenType: 'prompt' })).toBe( + tokenValues['grok-4-1-fast'].prompt, + ); + expect( + getMultiplier({ model: 'xai/grok-4-1-fast-non-reasoning', tokenType: 'completion' }), + ).toBe(tokenValues['grok-4-1-fast'].completion); + }); + + test('should return correct prompt and completion rates for Grok Code Fast model with prefixes', () => { + expect(getMultiplier({ model: 'xai/grok-code-fast-1', tokenType: 'prompt' })).toBe( + tokenValues['grok-code-fast'].prompt, + ); + expect(getMultiplier({ model: 'xai/grok-code-fast-1', tokenType: 'completion' })).toBe( + tokenValues['grok-code-fast'].completion, + ); + }); }); }); diff --git a/api/utils/tokens.spec.js b/api/utils/tokens.spec.js index c4589c610e..a169c31622 100644 --- a/api/utils/tokens.spec.js +++ b/api/utils/tokens.spec.js @@ -778,6 +778,16 @@ describe('Grok Model Tests - Tokens', () => { expect(getModelMaxTokens('grok-4-0709')).toBe(256000); }); + test('should return correct tokens for Grok 4 Fast and Grok 4.1 Fast models', () => { + expect(getModelMaxTokens('grok-4-fast')).toBe(2000000); + expect(getModelMaxTokens('grok-4-1-fast-reasoning')).toBe(2000000); + expect(getModelMaxTokens('grok-4-1-fast-non-reasoning')).toBe(2000000); + }); + + test('should return correct tokens for Grok Code Fast model', () => { + expect(getModelMaxTokens('grok-code-fast-1')).toBe(256000); + }); + test('should handle partial matches for Grok models with prefixes', () => { // Vision models should match before general models expect(getModelMaxTokens('xai/grok-2-vision-1212')).toBe(32768); @@ -797,6 +807,12 @@ describe('Grok Model Tests - Tokens', () => { expect(getModelMaxTokens('xai/grok-3-mini-fast')).toBe(131072); // Grok 4 model expect(getModelMaxTokens('xai/grok-4-0709')).toBe(256000); + // Grok 4 Fast and 4.1 Fast models + expect(getModelMaxTokens('xai/grok-4-fast')).toBe(2000000); + expect(getModelMaxTokens('xai/grok-4-1-fast-reasoning')).toBe(2000000); + expect(getModelMaxTokens('xai/grok-4-1-fast-non-reasoning')).toBe(2000000); + // Grok Code Fast model + expect(getModelMaxTokens('xai/grok-code-fast-1')).toBe(256000); }); }); @@ -820,6 +836,12 @@ describe('Grok Model Tests - Tokens', () => { expect(matchModelName('grok-3-mini-fast')).toBe('grok-3-mini-fast'); // Grok 4 model expect(matchModelName('grok-4-0709')).toBe('grok-4'); + // Grok 4 Fast and 4.1 Fast models + expect(matchModelName('grok-4-fast')).toBe('grok-4-fast'); + expect(matchModelName('grok-4-1-fast-reasoning')).toBe('grok-4-1-fast'); + expect(matchModelName('grok-4-1-fast-non-reasoning')).toBe('grok-4-1-fast'); + // Grok Code Fast model + expect(matchModelName('grok-code-fast-1')).toBe('grok-code-fast'); }); test('should match Grok model variations with prefixes', () => { @@ -841,6 +863,12 @@ describe('Grok Model Tests - Tokens', () => { expect(matchModelName('xai/grok-3-mini-fast')).toBe('grok-3-mini-fast'); // Grok 4 model expect(matchModelName('xai/grok-4-0709')).toBe('grok-4'); + // Grok 4 Fast and 4.1 Fast models + expect(matchModelName('xai/grok-4-fast')).toBe('grok-4-fast'); + expect(matchModelName('xai/grok-4-1-fast-reasoning')).toBe('grok-4-1-fast'); + expect(matchModelName('xai/grok-4-1-fast-non-reasoning')).toBe('grok-4-1-fast'); + // Grok Code Fast model + expect(matchModelName('xai/grok-code-fast-1')).toBe('grok-code-fast'); }); }); }); diff --git a/client/src/hooks/Conversations/useExportConversation.ts b/client/src/hooks/Conversations/useExportConversation.ts index 25758f54b0..6b5d53e65e 100644 --- a/client/src/hooks/Conversations/useExportConversation.ts +++ b/client/src/hooks/Conversations/useExportConversation.ts @@ -366,7 +366,9 @@ export default function useExportConversation({ } /** Use JSON.stringify without indentation to minimize file size for deeply nested recursive exports */ - download(JSON.stringify(data), `${filename}.json`, 'application/json'); + const jsonString = JSON.stringify(data); + const blob = new Blob([jsonString], { type: 'application/json;charset=utf-8' }); + download(blob, `${filename}.json`, 'application/json'); }; const exportConversation = () => { diff --git a/packages/api/src/utils/tokens.ts b/packages/api/src/utils/tokens.ts index 5271d69f1d..7dda247cc1 100644 --- a/packages/api/src/utils/tokens.ts +++ b/packages/api/src/utils/tokens.ts @@ -280,6 +280,9 @@ const xAIModels = { 'grok-3-mini': 131072, 'grok-3-mini-fast': 131072, 'grok-4': 256000, // 256K context + 'grok-4-fast': 2000000, // 2M context + 'grok-4-1-fast': 2000000, // 2M context (covers reasoning & non-reasoning variants) + 'grok-code-fast': 256000, // 256K context }; const aggregateModels = {