-
Notifications
You must be signed in to change notification settings - Fork 42
Add Groq provider support #577
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||
|---|---|---|---|---|---|---|
| @@ -0,0 +1,80 @@ | ||||||
| import { LlmTransactionMetadata, Transaction } from '../types'; | ||||||
| import { getCostPerToken } from '../services/AccountingService'; | ||||||
| import { BaseProvider } from './BaseProvider'; | ||||||
| import { ProviderType } from './ProviderType'; | ||||||
| import { CompletionStateBody, parseSSEGPTFormat } from './GPTProvider'; | ||||||
| import logger from '../logger'; | ||||||
|
|
||||||
| export class GroqProvider extends BaseProvider { | ||||||
| private readonly GROQ_BASE_URL = 'https://api.groq.com/openai/v1'; | ||||||
|
|
||||||
| getType(): ProviderType { | ||||||
| return ProviderType.GROQ; | ||||||
| } | ||||||
|
|
||||||
| getBaseUrl(): string { | ||||||
| return this.GROQ_BASE_URL; | ||||||
| } | ||||||
|
|
||||||
| getApiKey(): string | undefined { | ||||||
| return process.env.GROQ_API_KEY; | ||||||
| } | ||||||
|
|
||||||
| override supportsStream(): boolean { | ||||||
| return true; | ||||||
| } | ||||||
|
|
||||||
| async handleBody(data: string): Promise<Transaction> { | ||||||
| try { | ||||||
| let prompt_tokens = 0; | ||||||
| let completion_tokens = 0; | ||||||
| let total_tokens = 0; | ||||||
| let providerId = 'null'; | ||||||
|
|
||||||
| if (this.getIsStream()) { | ||||||
| const chunks = parseSSEGPTFormat(data); | ||||||
|
|
||||||
| for (const chunk of chunks) { | ||||||
| if (chunk.usage !== null) { | ||||||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
The code checks View DetailsAnalysisTypeError in GroqProvider.handleBody() when accessing undefined usage propertiesWhat fails: GroqProvider.handleBody() checks How to reproduce: // When streaming chunk has missing usage property (undefined)
const chunk = { id: "test", choices: [] }; // usage property omitted
if (chunk.usage !== null) { // undefined !== null is true
chunk.usage.prompt_tokens; // TypeError: Cannot read properties of undefined
}Result: Expected: Should safely skip undefined usage like OpenRouterProvider.ts does with |
||||||
| prompt_tokens += chunk.usage.prompt_tokens; | ||||||
| completion_tokens += chunk.usage.completion_tokens; | ||||||
| total_tokens += chunk.usage.total_tokens; | ||||||
| } | ||||||
| providerId = chunk.id || 'null'; | ||||||
| } | ||||||
| } else { | ||||||
| const parsed = JSON.parse(data) as CompletionStateBody; | ||||||
| prompt_tokens += parsed.usage.prompt_tokens; | ||||||
| completion_tokens += parsed.usage.completion_tokens; | ||||||
| total_tokens += parsed.usage.total_tokens; | ||||||
| providerId = parsed.id || 'null'; | ||||||
| } | ||||||
|
|
||||||
| const cost = getCostPerToken( | ||||||
| this.getModel(), | ||||||
| prompt_tokens, | ||||||
| completion_tokens | ||||||
| ); | ||||||
|
|
||||||
| const metadata: LlmTransactionMetadata = { | ||||||
| providerId: providerId, | ||||||
| provider: this.getType(), | ||||||
| model: this.getModel(), | ||||||
| inputTokens: prompt_tokens, | ||||||
| outputTokens: completion_tokens, | ||||||
| totalTokens: total_tokens, | ||||||
| }; | ||||||
|
|
||||||
| const transaction: Transaction = { | ||||||
| rawTransactionCost: cost, | ||||||
| metadata: metadata, | ||||||
| status: 'success', | ||||||
| }; | ||||||
|
|
||||||
| return transaction; | ||||||
| } catch (error) { | ||||||
| logger.error(`Error processing data: ${error}`); | ||||||
| throw error; | ||||||
| } | ||||||
| } | ||||||
| } | ||||||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,10 @@ | ||
| import { getEchoToken } from '../auth/token-manager'; | ||
| import { | ||
| createEchoGroq as createEchoGroqBase, | ||
| EchoConfig, | ||
| GroqProvider, | ||
| } from '@merit-systems/echo-typescript-sdk'; | ||
|
|
||
| export function createEchoGroq(config: EchoConfig): GroqProvider { | ||
| return createEchoGroqBase(config, async () => getEchoToken(config)); | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,3 +1,4 @@ | ||
| export * from './anthropic'; | ||
| export * from './google'; | ||
| export * from './groq'; | ||
| export * from './openai'; |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,23 @@ | ||
| import { createGroq as createGroqBase, GroqProvider } from '@ai-sdk/groq'; | ||
| import { ROUTER_BASE_URL } from 'config'; | ||
| import { EchoConfig } from '../types'; | ||
| import { validateAppId } from '../utils/validation'; | ||
| import { echoFetch } from './index'; | ||
|
|
||
| export function createEchoGroq( | ||
| { appId, baseRouterUrl = ROUTER_BASE_URL }: EchoConfig, | ||
| getTokenFn: (appId: string) => Promise<string | null>, | ||
| onInsufficientFunds?: () => void | ||
| ): GroqProvider { | ||
| validateAppId(appId, 'createEchoGroq'); | ||
|
|
||
| return createGroqBase({ | ||
| baseURL: baseRouterUrl, | ||
| apiKey: 'placeholder_replaced_by_echoFetch', | ||
| fetch: echoFetch( | ||
| fetch, | ||
| async () => await getTokenFn(appId), | ||
| onInsufficientFunds | ||
| ), | ||
| }); | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,98 @@ | ||
| import { SupportedModel } from '../types'; | ||
|
|
||
| // Groq model IDs | ||
| export type GroqModel = | ||
| | 'llama3-8b-8192' | ||
| | 'llama3-70b-8192' | ||
| | 'mixtral-8x7b-32768' | ||
| | 'gemma2-9b-it' | ||
| | 'llama-3.1-8b-instant' | ||
| | 'llama-3.3-70b-versatile' | ||
| | 'llama-4-scout' | ||
| | 'llama-4-maverick' | ||
| | 'llama-guard-4-12b' | ||
| | 'qwen3-32b' | ||
| | 'gpt-oss-20b' | ||
| | 'gpt-oss-120b' | ||
| | 'kimi-k2-0905-1t'; | ||
|
|
||
| export const GroqModels: SupportedModel[] = [ | ||
| { | ||
| model_id: 'llama3-8b-8192', | ||
| input_cost_per_token: 0.00000005, | ||
| output_cost_per_token: 0.00000008, | ||
| provider: 'Groq', | ||
| }, | ||
| { | ||
| model_id: 'llama3-70b-8192', | ||
| input_cost_per_token: 0.00000027, | ||
| output_cost_per_token: 0.00000027, | ||
| provider: 'Groq', | ||
| }, | ||
| { | ||
| model_id: 'mixtral-8x7b-32768', | ||
| input_cost_per_token: 0.00000027, | ||
| output_cost_per_token: 0.00000027, | ||
| provider: 'Groq', | ||
| }, | ||
| { | ||
| model_id: 'gemma2-9b-it', | ||
| input_cost_per_token: 0.00000007, | ||
| output_cost_per_token: 0.00000007, | ||
| provider: 'Groq', | ||
| }, | ||
| { | ||
| model_id: 'llama-3.1-8b-instant', | ||
| input_cost_per_token: 0.00000005, | ||
| output_cost_per_token: 0.00000008, | ||
| provider: 'Groq', | ||
| }, | ||
| { | ||
| model_id: 'llama-3.3-70b-versatile', | ||
| input_cost_per_token: 0.00000059, | ||
| output_cost_per_token: 0.00000079, | ||
| provider: 'Groq', | ||
| }, | ||
| { | ||
| model_id: 'llama-4-scout', | ||
| input_cost_per_token: 0.00000011, | ||
| output_cost_per_token: 0.00000034, | ||
| provider: 'Groq', | ||
| }, | ||
| { | ||
| model_id: 'llama-4-maverick', | ||
| input_cost_per_token: 0.0000002, | ||
| output_cost_per_token: 0.0000006, | ||
| provider: 'Groq', | ||
| }, | ||
| { | ||
| model_id: 'llama-guard-4-12b', | ||
| input_cost_per_token: 0.0000002, | ||
| output_cost_per_token: 0.0000002, | ||
| provider: 'Groq', | ||
| }, | ||
| { | ||
| model_id: 'qwen3-32b', | ||
| input_cost_per_token: 0.00000029, | ||
| output_cost_per_token: 0.00000059, | ||
| provider: 'Groq', | ||
| }, | ||
| { | ||
| model_id: 'gpt-oss-20b', | ||
| input_cost_per_token: 0.000000075, | ||
| output_cost_per_token: 0.0000003, | ||
| provider: 'Groq', | ||
| }, | ||
| { | ||
| model_id: 'gpt-oss-120b', | ||
| input_cost_per_token: 0.00000015, | ||
| output_cost_per_token: 0.0000006, | ||
| provider: 'Groq', | ||
| }, | ||
| { | ||
| model_id: 'kimi-k2-0905-1t', | ||
| input_cost_per_token: 0.000001, | ||
| output_cost_per_token: 0.000003, | ||
| provider: 'Groq', | ||
| }, | ||
| ]; |
Uh oh!
There was an error while loading. Please reload this page.