- {status === 'loading' && (
- <>
-
-
Completing Authentication
-
Connecting to {provider}...
- >
- )}
-
- {status === 'success' && (
- <>
-
-
Authentication Successful!
-
Successfully connected to {provider}. You can now close this window.
-
- >
- )}
-
- {status === 'error' && (
- <>
-
-
Authentication Failed
-
{error || 'An error occurred during authentication'}
-
- >
- )}
+
+
+
Authenticating...
+
Please wait while we complete your authentication.
+
This window should close automatically.
+
+
)
}
-
-export default OAuthCallback
diff --git a/examples/chat-ui/src/components/PkceCallback.tsx b/examples/chat-ui/src/components/PkceCallback.tsx
new file mode 100644
index 0000000..0502ef3
--- /dev/null
+++ b/examples/chat-ui/src/components/PkceCallback.tsx
@@ -0,0 +1,153 @@
+import React, { useEffect, useState, useRef } from 'react'
+import { useSearchParams } from 'react-router-dom'
+import { completeOAuthFlow } from '../utils/auth'
+import { SupportedProvider } from '../types/models'
+
+interface OAuthCallbackProps {
+ provider: SupportedProvider
+}
+
+const PkceCallback: React.FC
= ({ provider }) => {
+ const [searchParams] = useSearchParams()
+ const [status, setStatus] = useState<'loading' | 'success' | 'error'>('loading')
+ const [error, setError] = useState(null)
+ const executedRef = useRef(false)
+
+ useEffect(() => {
+ const handleCallback = async () => {
+ if (executedRef.current) {
+ console.log('DEBUG: Skipping duplicate OAuth callback execution')
+ return
+ }
+ executedRef.current = true
+
+ try {
+ const code = searchParams.get('code')
+ const error = searchParams.get('error')
+
+ if (error) {
+ throw new Error(`OAuth error: ${error}`)
+ }
+
+ if (!code) {
+ throw new Error('Missing authorization code')
+ }
+
+ // TODO: Add state parameter handling back if needed later
+ // const stateToUse = state || 'no-state'
+ await completeOAuthFlow(provider, code)
+ setStatus('success')
+
+ console.log('DEBUG: OAuth flow completed successfully')
+ console.log('DEBUG: window.opener exists:', !!window.opener)
+ console.log('DEBUG: window.opener closed:', window.opener?.closed)
+ console.log('DEBUG: window.parent exists:', !!window.parent)
+ console.log('DEBUG: window.parent === window:', window.parent === window)
+
+ // Try to send success message to parent if possible
+ const sendSuccessMessage = () => {
+ const message = { type: 'oauth_success', provider }
+
+ // Try window.opener first
+ if (window.opener && !window.opener.closed) {
+ console.log('DEBUG: Sending message via window.opener')
+ window.opener.postMessage(message, '*')
+ return true
+ }
+
+ // Also try window.parent as fallback
+ if (window.parent && window.parent !== window) {
+ console.log('DEBUG: Sending message via window.parent')
+ window.parent.postMessage(message, '*')
+ return true
+ }
+
+ return false
+ }
+
+ // Try to send success message
+ const messageSent = sendSuccessMessage()
+ console.log({ messageSent })
+
+ console.log('DEBUG: Closing popup in 100ms')
+ setTimeout(() => {
+ console.log('DEBUG: Attempting to close popup')
+ window.close()
+ }, 100)
+ } catch (err) {
+ console.error('OAuth callback error:', err)
+ setError(err instanceof Error ? err.message : 'Unknown error')
+ setStatus('error')
+ }
+ }
+
+ handleCallback()
+ }, [searchParams, provider])
+
+ return (
+
+
+
+ {status === 'loading' && (
+ <>
+
+
Completing Authentication
+
Connecting to {provider}...
+ >
+ )}
+
+ {status === 'success' && (
+ <>
+
+
Authentication Successful!
+
Successfully connected to {provider}.
+
+
+
+ >
+ )}
+
+ {status === 'error' && (
+ <>
+
+
Authentication Failed
+
{error || 'An error occurred during authentication'}
+
+ >
+ )}
+
+
+
+ )
+}
+
+export default PkceCallback
diff --git a/examples/chat-ui/src/data/models.json b/examples/chat-ui/src/data/models.json
index d40234c..40397d9 100644
--- a/examples/chat-ui/src/data/models.json
+++ b/examples/chat-ui/src/data/models.json
@@ -1,86 +1,86 @@
{
"anthropic": {
- "claude-3-haiku-20240307": {
- "id": "claude-3-haiku-20240307",
- "name": "Claude Haiku 3",
+ "claude-3-5-haiku-20241022": {
+ "id": "claude-3-5-haiku-20241022",
+ "name": "Claude Haiku 3.5",
"attachment": true,
"reasoning": false,
"temperature": true,
"tool_call": true,
- "knowledge": "2023-08-31",
- "release_date": "2024-03-13",
- "last_updated": "2024-03-13",
+ "knowledge": "2024-07-31",
+ "release_date": "2024-10-22",
+ "last_updated": "2024-10-22",
"modalities": {
"input": ["text", "image"],
"output": ["text"]
},
"open_weights": false,
"cost": {
- "input": 0.25,
- "output": 1.25,
- "cache_read": 0.03,
- "cache_write": 0.3
+ "input": 0.8,
+ "output": 4,
+ "cache_read": 0.08,
+ "cache_write": 1
},
"limit": {
"context": 200000,
- "output": 4096
+ "output": 8192
}
},
- "claude-3-opus-20240229": {
- "id": "claude-3-opus-20240229",
- "name": "Claude Opus 3",
+ "claude-3-5-sonnet-20241022": {
+ "id": "claude-3-5-sonnet-20241022",
+ "name": "Claude Sonnet 3.5 v2",
"attachment": true,
"reasoning": false,
"temperature": true,
"tool_call": true,
- "knowledge": "2023-08-31",
- "release_date": "2024-02-29",
- "last_updated": "2024-02-29",
+ "knowledge": "2024-04-30",
+ "release_date": "2024-10-22",
+ "last_updated": "2024-10-22",
"modalities": {
"input": ["text", "image"],
"output": ["text"]
},
"open_weights": false,
"cost": {
- "input": 15,
- "output": 75,
- "cache_read": 1.5,
- "cache_write": 18.75
+ "input": 3,
+ "output": 15,
+ "cache_read": 0.3,
+ "cache_write": 3.75
},
"limit": {
"context": 200000,
- "output": 4096
+ "output": 8192
}
},
- "claude-3-5-haiku-20241022": {
- "id": "claude-3-5-haiku-20241022",
- "name": "Claude Haiku 3.5",
+ "claude-sonnet-4-20250514": {
+ "id": "claude-sonnet-4-20250514",
+ "name": "Claude Sonnet 4",
"attachment": true,
- "reasoning": false,
+ "reasoning": true,
"temperature": true,
"tool_call": true,
- "knowledge": "2024-07-31",
- "release_date": "2024-10-22",
- "last_updated": "2024-10-22",
+ "knowledge": "2025-03-31",
+ "release_date": "2025-05-22",
+ "last_updated": "2025-05-22",
"modalities": {
"input": ["text", "image"],
"output": ["text"]
},
"open_weights": false,
"cost": {
- "input": 0.8,
- "output": 4,
- "cache_read": 0.08,
- "cache_write": 1
+ "input": 3,
+ "output": 15,
+ "cache_read": 0.3,
+ "cache_write": 3.75
},
"limit": {
"context": 200000,
- "output": 8192
+ "output": 64000
}
},
- "claude-sonnet-4-20250514": {
- "id": "claude-sonnet-4-20250514",
- "name": "Claude Sonnet 4",
+ "claude-opus-4-20250514": {
+ "id": "claude-opus-4-20250514",
+ "name": "Claude Opus 4",
"attachment": true,
"reasoning": true,
"temperature": true,
@@ -94,36 +94,36 @@
},
"open_weights": false,
"cost": {
- "input": 3,
- "output": 15,
- "cache_read": 0.3,
- "cache_write": 3.75
+ "input": 15,
+ "output": 75,
+ "cache_read": 1.5,
+ "cache_write": 18.75
},
"limit": {
"context": 200000,
- "output": 64000
+ "output": 32000
}
},
- "claude-3-sonnet-20240229": {
- "id": "claude-3-sonnet-20240229",
- "name": "Claude Sonnet 3",
+ "claude-3-opus-20240229": {
+ "id": "claude-3-opus-20240229",
+ "name": "Claude Opus 3",
"attachment": true,
"reasoning": false,
"temperature": true,
"tool_call": true,
"knowledge": "2023-08-31",
- "release_date": "2024-03-04",
- "last_updated": "2024-03-04",
+ "release_date": "2024-02-29",
+ "last_updated": "2024-02-29",
"modalities": {
"input": ["text", "image"],
"output": ["text"]
},
"open_weights": false,
"cost": {
- "input": 3,
- "output": 15,
- "cache_read": 0.3,
- "cache_write": 0.3
+ "input": 15,
+ "output": 75,
+ "cache_read": 1.5,
+ "cache_write": 18.75
},
"limit": {
"context": 200000,
@@ -156,42 +156,42 @@
"output": 8192
}
},
- "claude-opus-4-20250514": {
- "id": "claude-opus-4-20250514",
- "name": "Claude Opus 4",
+ "claude-3-haiku-20240307": {
+ "id": "claude-3-haiku-20240307",
+ "name": "Claude Haiku 3",
"attachment": true,
- "reasoning": true,
+ "reasoning": false,
"temperature": true,
"tool_call": true,
- "knowledge": "2025-03-31",
- "release_date": "2025-05-22",
- "last_updated": "2025-05-22",
+ "knowledge": "2023-08-31",
+ "release_date": "2024-03-13",
+ "last_updated": "2024-03-13",
"modalities": {
"input": ["text", "image"],
"output": ["text"]
},
"open_weights": false,
"cost": {
- "input": 15,
- "output": 75,
- "cache_read": 1.5,
- "cache_write": 18.75
+ "input": 0.25,
+ "output": 1.25,
+ "cache_read": 0.03,
+ "cache_write": 0.3
},
"limit": {
"context": 200000,
- "output": 32000
+ "output": 4096
}
},
- "claude-3-5-sonnet-20241022": {
- "id": "claude-3-5-sonnet-20241022",
- "name": "Claude Sonnet 3.5 v2",
+ "claude-3-7-sonnet-20250219": {
+ "id": "claude-3-7-sonnet-20250219",
+ "name": "Claude Sonnet 3.7",
"attachment": true,
- "reasoning": false,
+ "reasoning": true,
"temperature": true,
"tool_call": true,
- "knowledge": "2024-04-30",
- "release_date": "2024-10-22",
- "last_updated": "2024-10-22",
+ "knowledge": "2024-10-31",
+ "release_date": "2025-02-19",
+ "last_updated": "2025-02-19",
"modalities": {
"input": ["text", "image"],
"output": ["text"]
@@ -205,19 +205,19 @@
},
"limit": {
"context": 200000,
- "output": 8192
+ "output": 64000
}
},
- "claude-3-7-sonnet-20250219": {
- "id": "claude-3-7-sonnet-20250219",
- "name": "Claude Sonnet 3.7",
+ "claude-3-sonnet-20240229": {
+ "id": "claude-3-sonnet-20240229",
+ "name": "Claude Sonnet 3",
"attachment": true,
- "reasoning": true,
+ "reasoning": false,
"temperature": true,
"tool_call": true,
- "knowledge": "2024-10-31",
- "release_date": "2025-02-19",
- "last_updated": "2025-02-19",
+ "knowledge": "2023-08-31",
+ "release_date": "2024-03-04",
+ "last_updated": "2024-03-04",
"modalities": {
"input": ["text", "image"],
"output": ["text"]
@@ -227,757 +227,1795 @@
"input": 3,
"output": 15,
"cache_read": 0.3,
- "cache_write": 3.75
+ "cache_write": 0.3
},
"limit": {
"context": 200000,
- "output": 64000
+ "output": 4096
}
}
},
"groq": {
- "llama-3.3-70b-versatile": {
- "id": "llama-3.3-70b-versatile",
- "name": "Llama 3.3 70B Versatile",
+ "mistral-saba-24b": {
+ "id": "mistral-saba-24b",
+ "name": "Mistral Saba 24B",
"attachment": false,
"reasoning": false,
"temperature": true,
"tool_call": true,
- "knowledge": "2023-12",
- "release_date": "2024-12-06",
- "last_updated": "2024-12-06",
+ "knowledge": "2024-08",
+ "release_date": "2025-02-06",
+ "last_updated": "2025-02-06",
"modalities": {
"input": ["text"],
"output": ["text"]
},
- "open_weights": true,
+ "open_weights": false,
"cost": {
- "input": 0.59,
+ "input": 0.79,
"output": 0.79
},
"limit": {
- "context": 131072,
+ "context": 32768,
"output": 32768
}
},
- "llama-3.1-8b-instant": {
- "id": "llama-3.1-8b-instant",
- "name": "Llama 3.1 8B Instant",
+ "deepseek-r1-distill-llama-70b": {
+ "id": "deepseek-r1-distill-llama-70b",
+ "name": "DeepSeek R1 Distill Llama 70B",
"attachment": false,
- "reasoning": false,
+ "reasoning": true,
"temperature": true,
"tool_call": true,
- "knowledge": "2023-12",
- "release_date": "2024-07-23",
- "last_updated": "2024-07-23",
+ "knowledge": "2024-07",
+ "release_date": "2025-01-20",
+ "last_updated": "2025-01-20",
"modalities": {
"input": ["text"],
"output": ["text"]
},
"open_weights": true,
"cost": {
- "input": 0.05,
- "output": 0.08
+ "input": 0.75,
+ "output": 0.99
},
"limit": {
"context": 131072,
"output": 8192
}
},
- "llama-guard-3-8b": {
- "id": "llama-guard-3-8b",
- "name": "Llama Guard 3 8B",
+ "llama-3.3-70b-versatile": {
+ "id": "llama-3.3-70b-versatile",
+ "name": "Llama 3.3 70B Versatile",
"attachment": false,
"reasoning": false,
"temperature": true,
- "tool_call": false,
- "release_date": "2024-07-23",
- "last_updated": "2024-07-23",
+ "tool_call": true,
+ "knowledge": "2023-12",
+ "release_date": "2024-12-06",
+ "last_updated": "2024-12-06",
"modalities": {
"input": ["text"],
"output": ["text"]
},
"open_weights": true,
"cost": {
- "input": 0.2,
- "output": 0.2
+ "input": 0.59,
+ "output": 0.79
},
"limit": {
- "context": 8192,
- "output": 8192
+ "context": 131072,
+ "output": 32768
}
},
- "gemma2-9b-it": {
- "id": "gemma2-9b-it",
- "name": "Gemma 2 9B",
+ "moonshotai/kimi-k2-instruct": {
+ "id": "moonshotai/kimi-k2-instruct",
+ "name": "Kimi K2 Instruct",
"attachment": false,
"reasoning": false,
"temperature": true,
"tool_call": true,
- "knowledge": "2024-06",
- "release_date": "2024-06-27",
- "last_updated": "2024-06-27",
+ "knowledge": "2024-10",
+ "release_date": "2025-07-14",
+ "last_updated": "2025-07-14",
"modalities": {
"input": ["text"],
"output": ["text"]
},
"open_weights": true,
"cost": {
- "input": 0.2,
- "output": 0.2
+ "input": 1,
+ "output": 3
},
"limit": {
- "context": 8192,
- "output": 8192
+ "context": 131072,
+ "output": 16384
}
},
- "llama3-8b-8192": {
- "id": "llama3-8b-8192",
- "name": "Llama 3 8B",
+ "meta-llama/llama-4-maverick-17b-128e-instruct": {
+ "id": "meta-llama/llama-4-maverick-17b-128e-instruct",
+ "name": "Llama 4 Maverick 17B",
"attachment": false,
"reasoning": false,
"temperature": true,
"tool_call": true,
- "knowledge": "2023-03",
- "release_date": "2024-04-18",
- "last_updated": "2024-04-18",
+ "knowledge": "2024-08",
+ "release_date": "2025-04-05",
+ "last_updated": "2025-04-05",
"modalities": {
- "input": ["text"],
+ "input": ["text", "image"],
"output": ["text"]
},
"open_weights": true,
"cost": {
- "input": 0.05,
- "output": 0.08
+ "input": 0.2,
+ "output": 0.6
},
"limit": {
- "context": 8192,
+ "context": 131072,
"output": 8192
}
},
- "llama3-70b-8192": {
- "id": "llama3-70b-8192",
- "name": "Llama 3 70B",
+ "meta-llama/llama-4-scout-17b-16e-instruct": {
+ "id": "meta-llama/llama-4-scout-17b-16e-instruct",
+ "name": "Llama 4 Scout 17B",
"attachment": false,
"reasoning": false,
"temperature": true,
"tool_call": true,
- "knowledge": "2023-03",
- "release_date": "2024-04-18",
- "last_updated": "2024-04-18",
+ "knowledge": "2024-08",
+ "release_date": "2025-04-05",
+ "last_updated": "2025-04-05",
"modalities": {
- "input": ["text"],
+ "input": ["text", "image"],
"output": ["text"]
},
"open_weights": true,
"cost": {
- "input": 0.59,
- "output": 0.79
+ "input": 0.11,
+ "output": 0.34
},
"limit": {
- "context": 8192,
+ "context": 131072,
"output": 8192
}
},
- "deepseek-r1-distill-llama-70b": {
- "id": "deepseek-r1-distill-llama-70b",
- "name": "DeepSeek R1 Distill Llama 70B",
+ "qwen/qwen3-32b": {
+ "id": "qwen/qwen3-32b",
+ "name": "Qwen3 32B",
"attachment": false,
"reasoning": true,
"temperature": true,
"tool_call": true,
- "knowledge": "2024-07",
- "release_date": "2025-01-20",
- "last_updated": "2025-01-20",
+ "knowledge": "2024-11-08",
+ "release_date": "2024-12-23",
+ "last_updated": "2024-12-23",
"modalities": {
"input": ["text"],
"output": ["text"]
},
"open_weights": true,
"cost": {
- "input": 0.75,
- "output": 0.99
+ "input": 0.29,
+ "output": 0.59
},
"limit": {
"context": 131072,
- "output": 8192
+ "output": 16384
}
- },
- "qwen-qwq-32b": {
- "id": "qwen-qwq-32b",
- "name": "Qwen QwQ 32B",
+ }
+ },
+ "openrouter": {
+ "openai/gpt-4.1-mini": {
+ "id": "openai/gpt-4.1-mini",
+ "name": "GPT-4.1 Mini",
+ "attachment": true,
+ "reasoning": false,
+ "temperature": true,
+ "tool_call": true,
+ "knowledge": "2024-04",
+ "release_date": "2025-04-14",
+ "last_updated": "2025-04-14",
+ "modalities": {
+ "input": ["text", "image"],
+ "output": ["text"]
+ },
+ "open_weights": false,
+ "cost": {
+ "input": 0.4,
+ "output": 1.6,
+ "cache_read": 0.1
+ },
+ "limit": {
+ "context": 1047576,
+ "output": 32768
+ }
+ },
+ "openai/o4-mini": {
+ "id": "openai/o4-mini",
+ "name": "o4 Mini",
+ "attachment": true,
+ "reasoning": true,
+ "temperature": true,
+ "tool_call": true,
+ "knowledge": "2024-06",
+ "release_date": "2025-04-16",
+ "last_updated": "2025-04-16",
+ "modalities": {
+ "input": ["text", "image"],
+ "output": ["text"]
+ },
+ "open_weights": false,
+ "cost": {
+ "input": 1.1,
+ "output": 4.4,
+ "cache_read": 0.28
+ },
+ "limit": {
+ "context": 200000,
+ "output": 100000
+ }
+ },
+ "openai/gpt-4o-mini": {
+ "id": "openai/gpt-4o-mini",
+ "name": "GPT-4o-mini",
+ "attachment": true,
+ "reasoning": false,
+ "temperature": true,
+ "tool_call": true,
+ "knowledge": "2024-10",
+ "release_date": "2024-07-18",
+ "last_updated": "2024-07-18",
+ "modalities": {
+ "input": ["text", "image"],
+ "output": ["text"]
+ },
+ "open_weights": false,
+ "cost": {
+ "input": 0.15,
+ "output": 0.6,
+ "cache_read": 0.08
+ },
+ "limit": {
+ "context": 128000,
+ "output": 16384
+ }
+ },
+ "openai/gpt-4.1": {
+ "id": "openai/gpt-4.1",
+ "name": "GPT-4.1",
+ "attachment": true,
+ "reasoning": false,
+ "temperature": true,
+ "tool_call": true,
+ "knowledge": "2024-04",
+ "release_date": "2025-04-14",
+ "last_updated": "2025-04-14",
+ "modalities": {
+ "input": ["text", "image"],
+ "output": ["text"]
+ },
+ "open_weights": false,
+ "cost": {
+ "input": 2,
+ "output": 8,
+ "cache_read": 0.5
+ },
+ "limit": {
+ "context": 1047576,
+ "output": 32768
+ }
+ },
+ "sarvamai/sarvam-m:free": {
+ "id": "sarvamai/sarvam-m:free",
+ "name": "Sarvam-M (free)",
+ "attachment": false,
+ "reasoning": true,
+ "temperature": true,
+ "tool_call": true,
+ "knowledge": "2025-05",
+ "release_date": "2025-05-25",
+ "last_updated": "2025-05-25",
+ "modalities": {
+ "input": ["text"],
+ "output": ["text"]
+ },
+ "open_weights": true,
+ "cost": {
+ "input": 0,
+ "output": 0
+ },
+ "limit": {
+ "context": 32768,
+ "output": 32768
+ }
+ },
+ "deepseek/deepseek-r1-distill-qwen-14b": {
+ "id": "deepseek/deepseek-r1-distill-qwen-14b",
+ "name": "DeepSeek R1 Distill Qwen 14B",
+ "attachment": false,
+ "reasoning": true,
+ "temperature": true,
+ "tool_call": false,
+ "knowledge": "2024-10",
+ "release_date": "2025-01-29",
+ "last_updated": "2025-01-29",
+ "modalities": {
+ "input": ["text"],
+ "output": ["text"]
+ },
+ "open_weights": true,
+ "cost": {
+ "input": 0,
+ "output": 0
+ },
+ "limit": {
+ "context": 64000,
+ "output": 8192
+ }
+ },
+ "deepseek/deepseek-r1:free": {
+ "id": "deepseek/deepseek-r1:free",
+ "name": "R1 (free)",
+ "attachment": false,
+ "reasoning": true,
+ "temperature": true,
+ "tool_call": true,
+ "knowledge": "2025-01",
+ "release_date": "2025-01-20",
+ "last_updated": "2025-01-20",
+ "modalities": {
+ "input": ["text"],
+ "output": ["text"]
+ },
+ "open_weights": true,
+ "cost": {
+ "input": 0,
+ "output": 0
+ },
+ "limit": {
+ "context": 163840,
+ "output": 163840
+ }
+ },
+ "deepseek/deepseek-r1-distill-llama-70b": {
+ "id": "deepseek/deepseek-r1-distill-llama-70b",
+ "name": "DeepSeek R1 Distill Llama 70B",
+ "attachment": false,
+ "reasoning": true,
+ "temperature": true,
+ "tool_call": false,
+ "knowledge": "2024-10",
+ "release_date": "2025-01-23",
+ "last_updated": "2025-01-23",
+ "modalities": {
+ "input": ["text"],
+ "output": ["text"]
+ },
+ "open_weights": true,
+ "cost": {
+ "input": 0,
+ "output": 0
+ },
+ "limit": {
+ "context": 8192,
+ "output": 8192
+ }
+ },
+ "deepseek/deepseek-r1-0528:free": {
+ "id": "deepseek/deepseek-r1-0528:free",
+ "name": "R1 0528 (free)",
+ "attachment": false,
+ "reasoning": true,
+ "temperature": true,
+ "tool_call": true,
+ "knowledge": "2025-05",
+ "release_date": "2025-05-28",
+ "last_updated": "2025-05-28",
+ "modalities": {
+ "input": ["text"],
+ "output": ["text"]
+ },
+ "open_weights": true,
+ "cost": {
+ "input": 0,
+ "output": 0
+ },
+ "limit": {
+ "context": 163840,
+ "output": 163840
+ }
+ },
+ "deepseek/deepseek-v3-base:free": {
+ "id": "deepseek/deepseek-v3-base:free",
+ "name": "DeepSeek V3 Base (free)",
+ "attachment": false,
+ "reasoning": false,
+ "temperature": true,
+ "tool_call": false,
+ "knowledge": "2025-03",
+ "release_date": "2025-03-29",
+ "last_updated": "2025-03-29",
+ "modalities": {
+ "input": ["text"],
+ "output": ["text"]
+ },
+ "open_weights": true,
+ "cost": {
+ "input": 0,
+ "output": 0
+ },
+ "limit": {
+ "context": 163840,
+ "output": 163840
+ }
+ },
+ "deepseek/deepseek-chat-v3-0324": {
+ "id": "deepseek/deepseek-chat-v3-0324",
+ "name": "DeepSeek V3 0324",
+ "attachment": false,
+ "reasoning": false,
+ "temperature": true,
+ "tool_call": false,
+ "knowledge": "2024-10",
+ "release_date": "2025-03-24",
+ "last_updated": "2025-03-24",
+ "modalities": {
+ "input": ["text"],
+ "output": ["text"]
+ },
+ "open_weights": true,
+ "cost": {
+ "input": 0,
+ "output": 0
+ },
+ "limit": {
+ "context": 16384,
+ "output": 8192
+ }
+ },
+ "deepseek/deepseek-r1-0528-qwen3-8b:free": {
+ "id": "deepseek/deepseek-r1-0528-qwen3-8b:free",
+ "name": "Deepseek R1 0528 Qwen3 8B (free)",
+ "attachment": false,
+ "reasoning": true,
+ "temperature": true,
+ "tool_call": true,
+ "knowledge": "2025-05",
+ "release_date": "2025-05-29",
+ "last_updated": "2025-05-29",
+ "modalities": {
+ "input": ["text"],
+ "output": ["text"]
+ },
+ "open_weights": true,
+ "cost": {
+ "input": 0,
+ "output": 0
+ },
+ "limit": {
+ "context": 131072,
+ "output": 131072
+ }
+ },
+ "cognitivecomputations/dolphin3.0-mistral-24b": {
+ "id": "cognitivecomputations/dolphin3.0-mistral-24b",
+ "name": "Dolphin3.0 Mistral 24B",
+ "attachment": false,
+ "reasoning": false,
+ "temperature": true,
+ "tool_call": true,
+ "knowledge": "2024-10",
+ "release_date": "2025-02-13",
+ "last_updated": "2025-02-13",
+ "modalities": {
+ "input": ["text"],
+ "output": ["text"]
+ },
+ "open_weights": true,
+ "cost": {
+ "input": 0,
+ "output": 0
+ },
+ "limit": {
+ "context": 32768,
+ "output": 8192
+ }
+ },
+ "cognitivecomputations/dolphin3.0-r1-mistral-24b": {
+ "id": "cognitivecomputations/dolphin3.0-r1-mistral-24b",
+ "name": "Dolphin3.0 R1 Mistral 24B",
+ "attachment": false,
+ "reasoning": true,
+ "temperature": true,
+ "tool_call": true,
+ "knowledge": "2024-10",
+ "release_date": "2025-02-13",
+ "last_updated": "2025-02-13",
+ "modalities": {
+ "input": ["text"],
+ "output": ["text"]
+ },
+ "open_weights": true,
+ "cost": {
+ "input": 0,
+ "output": 0
+ },
+ "limit": {
+ "context": 32768,
+ "output": 8192
+ }
+ },
+ "anthropic/claude-4-sonnet-20250522": {
+ "id": "anthropic/claude-4-sonnet-20250522",
+ "name": "Claude Sonnet 4",
+ "attachment": true,
+ "reasoning": true,
+ "temperature": true,
+ "tool_call": true,
+ "knowledge": "2025-03-31",
+ "release_date": "2025-05-22",
+ "last_updated": "2025-05-22",
+ "modalities": {
+ "input": ["text", "image"],
+ "output": ["text"]
+ },
+ "open_weights": false,
+ "cost": {
+ "input": 3,
+ "output": 15,
+ "cache_read": 0.3,
+ "cache_write": 3.75
+ },
+ "limit": {
+ "context": 200000,
+ "output": 64000
+ }
+ },
+ "anthropic/claude-3.5-haiku": {
+ "id": "anthropic/claude-3.5-haiku",
+ "name": "Claude Haiku 3.5",
+ "attachment": true,
+ "reasoning": false,
+ "temperature": true,
+ "tool_call": true,
+ "knowledge": "2024-07-31",
+ "release_date": "2024-10-22",
+ "last_updated": "2024-10-22",
+ "modalities": {
+ "input": ["text", "image"],
+ "output": ["text"]
+ },
+ "open_weights": false,
+ "cost": {
+ "input": 0.8,
+ "output": 4,
+ "cache_read": 0.08,
+ "cache_write": 1
+ },
+ "limit": {
+ "context": 200000,
+ "output": 8192
+ }
+ },
+ "anthropic/claude-3.7-sonnet": {
+ "id": "anthropic/claude-3.7-sonnet",
+ "name": "Claude Sonnet 3.7",
+ "attachment": true,
+ "reasoning": true,
+ "temperature": true,
+ "tool_call": true,
+ "knowledge": "2024-01",
+ "release_date": "2025-02-19",
+ "last_updated": "2025-02-19",
+ "modalities": {
+ "input": ["text", "image"],
+ "output": ["text"]
+ },
+ "open_weights": false,
+ "cost": {
+ "input": 15,
+ "output": 75,
+ "cache_read": 1.5,
+ "cache_write": 18.75
+ },
+ "limit": {
+ "context": 200000,
+ "output": 128000
+ }
+ },
+ "anthropic/claude-opus-4": {
+ "id": "anthropic/claude-opus-4",
+ "name": "Claude Opus 4",
+ "attachment": true,
+ "reasoning": true,
+ "temperature": true,
+ "tool_call": true,
+ "knowledge": "2025-03-31",
+ "release_date": "2025-05-22",
+ "last_updated": "2025-05-22",
+ "modalities": {
+ "input": ["text", "image"],
+ "output": ["text"]
+ },
+ "open_weights": false,
+ "cost": {
+ "input": 15,
+ "output": 75,
+ "cache_read": 1.5,
+ "cache_write": 18.75
+ },
+ "limit": {
+ "context": 200000,
+ "output": 32000
+ }
+ },
+ "featherless/qwerky-72b": {
+ "id": "featherless/qwerky-72b",
+ "name": "Qwerky 72B",
+ "attachment": false,
+ "reasoning": false,
+ "temperature": true,
+ "tool_call": false,
+ "knowledge": "2024-10",
+ "release_date": "2025-03-20",
+ "last_updated": "2025-03-20",
+ "modalities": {
+ "input": ["text"],
+ "output": ["text"]
+ },
+ "open_weights": true,
+ "cost": {
+ "input": 0,
+ "output": 0
+ },
+ "limit": {
+ "context": 32768,
+ "output": 8192
+ }
+ },
+ "rekaai/reka-flash-3": {
+ "id": "rekaai/reka-flash-3",
+ "name": "Reka Flash 3",
+ "attachment": false,
+ "reasoning": true,
+ "temperature": true,
+ "tool_call": true,
+ "knowledge": "2024-10",
+ "release_date": "2025-03-12",
+ "last_updated": "2025-03-12",
+ "modalities": {
+ "input": ["text"],
+ "output": ["text"]
+ },
+ "open_weights": true,
+ "cost": {
+ "input": 0,
+ "output": 0
+ },
+ "limit": {
+ "context": 32768,
+ "output": 8192
+ }
+ },
+ "moonshotai/kimi-dev-72b:free": {
+ "id": "moonshotai/kimi-dev-72b:free",
+ "name": "Kimi Dev 72b (free)",
+ "attachment": false,
+ "reasoning": false,
+ "temperature": true,
+ "tool_call": true,
+ "knowledge": "2025-06",
+ "release_date": "2025-06-16",
+ "last_updated": "2025-06-16",
+ "modalities": {
+ "input": ["text"],
+ "output": ["text"]
+ },
+ "open_weights": true,
+ "cost": {
+ "input": 0,
+ "output": 0
+ },
+ "limit": {
+ "context": 131072,
+ "output": 131072
+ }
+ },
+ "moonshotai/kimi-k2": {
+ "id": "moonshotai/kimi-k2",
+ "name": "Kimi K2",
+ "attachment": false,
+ "reasoning": false,
+ "temperature": true,
+ "tool_call": true,
+ "knowledge": "2024-10",
+ "release_date": "2025-07-11",
+ "last_updated": "2025-07-11",
+ "modalities": {
+ "input": ["text"],
+ "output": ["text"]
+ },
+ "open_weights": false,
+ "cost": {
+ "input": 0.57,
+ "output": 2.3
+ },
+ "limit": {
+ "context": 131072,
+ "output": 32768
+ }
+ },
+ "x-ai/grok-3-mini": {
+ "id": "x-ai/grok-3-mini",
+ "name": "Grok 3 Mini",
+ "attachment": false,
+ "reasoning": true,
+ "temperature": true,
+ "tool_call": true,
+ "knowledge": "2024-11",
+ "release_date": "2025-02-17",
+ "last_updated": "2025-02-17",
+ "modalities": {
+ "input": ["text"],
+ "output": ["text"]
+ },
+ "open_weights": false,
+ "cost": {
+ "input": 0.3,
+ "output": 0.5,
+ "cache_read": 0.075,
+ "cache_write": 0.5
+ },
+ "limit": {
+ "context": 131072,
+ "output": 8192
+ }
+ },
+ "x-ai/grok-3": {
+ "id": "x-ai/grok-3",
+ "name": "Grok 3",
+ "attachment": false,
+ "reasoning": false,
+ "temperature": true,
+ "tool_call": true,
+ "knowledge": "2024-11",
+ "release_date": "2025-02-17",
+ "last_updated": "2025-02-17",
+ "modalities": {
+ "input": ["text"],
+ "output": ["text"]
+ },
+ "open_weights": false,
+ "cost": {
+ "input": 3,
+ "output": 15,
+ "cache_read": 0.75,
+ "cache_write": 15
+ },
+ "limit": {
+ "context": 131072,
+ "output": 8192
+ }
+ },
+ "x-ai/grok-3-beta": {
+ "id": "x-ai/grok-3-beta",
+ "name": "Grok 3 Beta",
+ "attachment": false,
+ "reasoning": false,
+ "temperature": true,
+ "tool_call": true,
+ "knowledge": "2024-11",
+ "release_date": "2025-02-17",
+ "last_updated": "2025-02-17",
+ "modalities": {
+ "input": ["text"],
+ "output": ["text"]
+ },
+ "open_weights": false,
+ "cost": {
+ "input": 3,
+ "output": 15,
+ "cache_read": 0.75,
+ "cache_write": 15
+ },
+ "limit": {
+ "context": 131072,
+ "output": 8192
+ }
+ },
+ "x-ai/grok-4": {
+ "id": "x-ai/grok-4",
+ "name": "Grok 4",
+ "attachment": false,
+ "reasoning": true,
+ "temperature": true,
+ "tool_call": true,
+ "knowledge": "2025-07",
+ "release_date": "2025-07-09",
+ "last_updated": "2025-07-09",
+ "modalities": {
+ "input": ["text"],
+ "output": ["text"]
+ },
+ "open_weights": false,
+ "cost": {
+ "input": 3,
+ "output": 15,
+ "cache_read": 0.75,
+ "cache_write": 15
+ },
+ "limit": {
+ "context": 256000,
+ "output": 64000
+ }
+ },
+ "x-ai/grok-3-mini-beta": {
+ "id": "x-ai/grok-3-mini-beta",
+ "name": "Grok 3 Mini Beta",
+ "attachment": false,
+ "reasoning": true,
+ "temperature": true,
+ "tool_call": true,
+ "knowledge": "2024-11",
+ "release_date": "2025-02-17",
+ "last_updated": "2025-02-17",
+ "modalities": {
+ "input": ["text"],
+ "output": ["text"]
+ },
+ "open_weights": false,
+ "cost": {
+ "input": 0.3,
+ "output": 0.5,
+ "cache_read": 0.075,
+ "cache_write": 0.5
+ },
+ "limit": {
+ "context": 131072,
+ "output": 8192
+ }
+ },
+ "microsoft/mai-ds-r1:free": {
+ "id": "microsoft/mai-ds-r1:free",
+ "name": "MAI DS R1 (free)",
+ "attachment": false,
+ "reasoning": true,
+ "temperature": true,
+ "tool_call": true,
+ "knowledge": "2025-04",
+ "release_date": "2025-04-21",
+ "last_updated": "2025-04-21",
+ "modalities": {
+ "input": ["text"],
+ "output": ["text"]
+ },
+ "open_weights": true,
+ "cost": {
+ "input": 0,
+ "output": 0
+ },
+ "limit": {
+ "context": 163840,
+ "output": 163840
+ }
+ },
+ "openrouter/cypher-alpha:free": {
+ "id": "openrouter/cypher-alpha:free",
+ "name": "Cypher Alpha (free)",
+ "attachment": false,
+ "reasoning": false,
+ "temperature": true,
+ "tool_call": true,
+ "knowledge": "2025-07",
+ "release_date": "2025-07-01",
+ "last_updated": "2025-07-01",
+ "modalities": {
+ "input": ["text"],
+ "output": ["text"]
+ },
+ "open_weights": false,
+ "cost": {
+ "input": 0,
+ "output": 0
+ },
+ "limit": {
+ "context": 1000000,
+ "output": 1000000
+ }
+ },
+ "meta-llama/llama-3.3-70b-instruct:free": {
+ "id": "meta-llama/llama-3.3-70b-instruct:free",
+ "name": "Llama 3.3 70B Instruct (free)",
+ "attachment": false,
+ "reasoning": false,
+ "temperature": true,
+ "tool_call": true,
+ "knowledge": "2024-12",
+ "release_date": "2024-12-06",
+ "last_updated": "2024-12-06",
+ "modalities": {
+ "input": ["text"],
+ "output": ["text"]
+ },
+ "open_weights": true,
+ "cost": {
+ "input": 0,
+ "output": 0
+ },
+ "limit": {
+ "context": 65536,
+ "output": 65536
+ }
+ },
+ "meta-llama/llama-3.2-11b-vision-instruct": {
+ "id": "meta-llama/llama-3.2-11b-vision-instruct",
+ "name": "Llama 3.2 11B Vision Instruct",
+ "attachment": true,
+ "reasoning": false,
+ "temperature": true,
+ "tool_call": false,
+ "knowledge": "2023-12",
+ "release_date": "2024-09-25",
+ "last_updated": "2024-09-25",
+ "modalities": {
+ "input": ["text", "image"],
+ "output": ["text"]
+ },
+ "open_weights": true,
+ "cost": {
+ "input": 0,
+ "output": 0
+ },
+ "limit": {
+ "context": 131072,
+ "output": 8192
+ }
+ },
+ "meta-llama/llama-4-scout:free": {
+ "id": "meta-llama/llama-4-scout:free",
+ "name": "Llama 4 Scout (free)",
+ "attachment": true,
+ "reasoning": false,
+ "temperature": true,
+ "tool_call": true,
+ "knowledge": "2024-08",
+ "release_date": "2025-04-05",
+ "last_updated": "2025-04-05",
+ "modalities": {
+ "input": ["text", "image"],
+ "output": ["text"]
+ },
+ "open_weights": true,
+ "cost": {
+ "input": 0,
+ "output": 0
+ },
+ "limit": {
+ "context": 64000,
+ "output": 64000
+ }
+ },
+ "google/gemma-3n-e4b-it:free": {
+ "id": "google/gemma-3n-e4b-it:free",
+ "name": "Gemma 3n 4B (free)",
+ "attachment": true,
+ "reasoning": false,
+ "temperature": true,
+ "tool_call": true,
+ "knowledge": "2025-05",
+ "release_date": "2025-05-20",
+ "last_updated": "2025-05-20",
+ "modalities": {
+ "input": ["text", "image", "audio"],
+ "output": ["text"]
+ },
+ "open_weights": true,
+ "cost": {
+ "input": 0,
+ "output": 0
+ },
+ "limit": {
+ "context": 8192,
+ "output": 8192
+ }
+ },
+ "google/gemini-2.5-flash-preview-05-20": {
+ "id": "google/gemini-2.5-flash-preview-05-20",
+ "name": "Gemini 2.5 Flash Preview 05-20",
+ "attachment": true,
+ "reasoning": true,
+ "temperature": true,
+ "tool_call": true,
+ "knowledge": "2025-01",
+ "release_date": "2025-05-20",
+ "last_updated": "2025-05-20",
+ "modalities": {
+ "input": ["text", "image", "audio", "video", "pdf"],
+ "output": ["text"]
+ },
+ "open_weights": false,
+ "cost": {
+ "input": 0.15,
+ "output": 0.6,
+ "cache_read": 0.0375
+ },
+ "limit": {
+ "context": 1048576,
+ "output": 65536
+ }
+ },
+ "google/gemma-3-27b-it": {
+ "id": "google/gemma-3-27b-it",
+ "name": "Gemma 3 27B IT",
+ "attachment": true,
+ "reasoning": false,
+ "temperature": true,
+ "tool_call": true,
+ "knowledge": "2024-10",
+ "release_date": "2025-03-12",
+ "last_updated": "2025-03-12",
+ "modalities": {
+ "input": ["text", "image"],
+ "output": ["text"]
+ },
+ "open_weights": true,
+ "cost": {
+ "input": 0,
+ "output": 0
+ },
+ "limit": {
+ "context": 96000,
+ "output": 8192
+ }
+ },
+ "google/gemini-2.5-pro-preview-06-05": {
+ "id": "google/gemini-2.5-pro-preview-06-05",
+ "name": "Gemini 2.5 Pro Preview 06-05",
+ "attachment": true,
+ "reasoning": true,
+ "temperature": true,
+ "tool_call": true,
+ "knowledge": "2025-01",
+ "release_date": "2025-06-05",
+ "last_updated": "2025-06-05",
+ "modalities": {
+ "input": ["text", "image", "audio", "video", "pdf"],
+ "output": ["text"]
+ },
+ "open_weights": false,
+ "cost": {
+ "input": 1.25,
+ "output": 10,
+ "cache_read": 0.31
+ },
+ "limit": {
+ "context": 1048576,
+ "output": 65536
+ }
+ },
+ "google/gemini-2.5-flash-preview-04-17": {
+ "id": "google/gemini-2.5-flash-preview-04-17",
+ "name": "Gemini 2.5 Flash Preview 04-17",
+ "attachment": true,
+ "reasoning": true,
+ "temperature": true,
+ "tool_call": true,
+ "knowledge": "2025-01",
+ "release_date": "2025-04-17",
+ "last_updated": "2025-04-17",
+ "modalities": {
+ "input": ["text", "image", "audio", "video", "pdf"],
+ "output": ["text"]
+ },
+ "open_weights": false,
+ "cost": {
+ "input": 0.15,
+ "output": 0.6,
+ "cache_read": 0.0375
+ },
+ "limit": {
+ "context": 1048576,
+ "output": 65536
+ }
+ },
+ "google/gemini-2.0-flash-exp:free": {
+ "id": "google/gemini-2.0-flash-exp:free",
+ "name": "Gemini 2.0 Flash Experimental (free)",
+ "attachment": true,
+ "reasoning": false,
+ "temperature": true,
+ "tool_call": true,
+ "knowledge": "2024-12",
+ "release_date": "2024-12-11",
+ "last_updated": "2024-12-11",
+ "modalities": {
+ "input": ["text", "image"],
+ "output": ["text"]
+ },
+ "open_weights": false,
+ "cost": {
+ "input": 0,
+ "output": 0
+ },
+ "limit": {
+ "context": 1048576,
+ "output": 1048576
+ }
+ },
+ "google/gemma-2-9b-it:free": {
+ "id": "google/gemma-2-9b-it:free",
+ "name": "Gemma 2 9B (free)",
"attachment": false,
- "reasoning": true,
+ "reasoning": false,
"temperature": true,
"tool_call": true,
- "knowledge": "2024-09",
- "release_date": "2024-11-27",
- "last_updated": "2024-11-27",
+ "knowledge": "2024-06",
+ "release_date": "2024-06-28",
+ "last_updated": "2024-06-28",
"modalities": {
"input": ["text"],
"output": ["text"]
},
"open_weights": true,
"cost": {
- "input": 0.29,
- "output": 0.39
+ "input": 0,
+ "output": 0
},
"limit": {
- "context": 131072,
- "output": 16384
+ "context": 8192,
+ "output": 8192
}
},
- "mistral-saba-24b": {
- "id": "mistral-saba-24b",
- "name": "Mistral Saba 24B",
- "attachment": false,
+ "google/gemma-3-12b-it": {
+ "id": "google/gemma-3-12b-it",
+ "name": "Gemma 3 12B IT",
+ "attachment": true,
"reasoning": false,
"temperature": true,
"tool_call": true,
- "knowledge": "2024-08",
- "release_date": "2025-02-06",
- "last_updated": "2025-02-06",
+ "knowledge": "2024-10",
+ "release_date": "2025-03-13",
+ "last_updated": "2025-03-13",
"modalities": {
- "input": ["text"],
+ "input": ["text", "image"],
+ "output": ["text"]
+ },
+ "open_weights": true,
+ "cost": {
+ "input": 0,
+ "output": 0
+ },
+ "limit": {
+ "context": 96000,
+ "output": 8192
+ }
+ },
+ "google/gemini-2.5-pro-preview-05-06": {
+ "id": "google/gemini-2.5-pro-preview-05-06",
+ "name": "Gemini 2.5 Pro Preview 05-06",
+ "attachment": true,
+ "reasoning": true,
+ "temperature": true,
+ "tool_call": true,
+ "knowledge": "2025-01",
+ "release_date": "2025-05-06",
+ "last_updated": "2025-05-06",
+ "modalities": {
+ "input": ["text", "image", "audio", "video", "pdf"],
"output": ["text"]
},
"open_weights": false,
"cost": {
- "input": 0.79,
- "output": 0.79
+ "input": 1.25,
+ "output": 10,
+ "cache_read": 0.31
},
"limit": {
- "context": 32768,
- "output": 32768
+ "context": 1048576,
+ "output": 65536
}
},
- "qwen/qwen3-32b": {
- "id": "qwen/qwen3-32b",
- "name": "Qwen3 32B",
+ "google/gemini-2.0-flash-001": {
+ "id": "google/gemini-2.0-flash-001",
+ "name": "Gemini 2.0 Flash",
+ "attachment": true,
+ "reasoning": false,
+ "temperature": true,
+ "tool_call": true,
+ "knowledge": "2024-06",
+ "release_date": "2024-12-11",
+ "last_updated": "2024-12-11",
+ "modalities": {
+ "input": ["text", "image", "audio", "video", "pdf"],
+ "output": ["text"]
+ },
+ "open_weights": false,
+ "cost": {
+ "input": 0.1,
+ "output": 0.4,
+ "cache_read": 0.025
+ },
+ "limit": {
+ "context": 1048576,
+ "output": 8192
+ }
+ },
+ "google/gemma-3n-e4b-it": {
+ "id": "google/gemma-3n-e4b-it",
+ "name": "Gemma 3n E4B IT",
+ "attachment": true,
+ "reasoning": false,
+ "temperature": true,
+ "tool_call": false,
+ "knowledge": "2024-10",
+ "release_date": "2025-05-20",
+ "last_updated": "2025-05-20",
+ "modalities": {
+ "input": ["text", "image", "audio"],
+ "output": ["text"]
+ },
+ "open_weights": true,
+ "cost": {
+ "input": 0,
+ "output": 0
+ },
+ "limit": {
+ "context": 8192,
+ "output": 8192
+ }
+ },
+ "google/gemini-2.5-pro": {
+ "id": "google/gemini-2.5-pro",
+ "name": "Gemini 2.5 Pro",
+ "attachment": true,
+ "reasoning": true,
+ "temperature": true,
+ "tool_call": true,
+ "knowledge": "2025-01",
+ "release_date": "2025-03-20",
+ "last_updated": "2025-06-05",
+ "modalities": {
+ "input": ["text", "image", "audio", "video", "pdf"],
+ "output": ["text"]
+ },
+ "open_weights": false,
+ "cost": {
+ "input": 1.25,
+ "output": 10,
+ "cache_read": 0.31
+ },
+ "limit": {
+ "context": 1048576,
+ "output": 65536
+ }
+ },
+ "thudm/glm-z1-32b:free": {
+ "id": "thudm/glm-z1-32b:free",
+ "name": "GLM Z1 32B (free)",
"attachment": false,
"reasoning": true,
"temperature": true,
"tool_call": true,
- "knowledge": "2024-11-08",
- "release_date": "2024-12-23",
- "last_updated": "2024-12-23",
+ "knowledge": "2025-04",
+ "release_date": "2025-04-17",
+ "last_updated": "2025-04-17",
"modalities": {
"input": ["text"],
"output": ["text"]
},
"open_weights": true,
"cost": {
- "input": 0.29,
- "output": 0.59
+ "input": 0,
+ "output": 0
},
"limit": {
- "context": 131072,
- "output": 16384
+ "context": 32768,
+ "output": 32768
}
},
- "meta-llama/llama-4-maverick-17b-128e-instruct": {
- "id": "meta-llama/llama-4-maverick-17b-128e-instruct",
- "name": "Llama 4 Maverick 17B",
- "attachment": false,
+ "mistralai/mistral-small-3.1-24b-instruct": {
+ "id": "mistralai/mistral-small-3.1-24b-instruct",
+ "name": "Mistral Small 3.1 24B Instruct",
+ "attachment": true,
"reasoning": false,
"temperature": true,
"tool_call": true,
- "knowledge": "2024-08",
- "release_date": "2025-04-05",
- "last_updated": "2025-04-05",
+ "knowledge": "2024-10",
+ "release_date": "2025-03-17",
+ "last_updated": "2025-03-17",
"modalities": {
"input": ["text", "image"],
"output": ["text"]
},
"open_weights": true,
"cost": {
- "input": 0.2,
- "output": 0.6
+ "input": 0,
+ "output": 0
},
"limit": {
- "context": 131072,
+ "context": 128000,
"output": 8192
}
},
- "meta-llama/llama-4-scout-17b-16e-instruct": {
- "id": "meta-llama/llama-4-scout-17b-16e-instruct",
- "name": "Llama 4 Scout 17B",
+ "mistralai/devstral-small-2507": {
+ "id": "mistralai/devstral-small-2507",
+ "name": "Devstral Small 1.1",
"attachment": false,
"reasoning": false,
"temperature": true,
"tool_call": true,
- "knowledge": "2024-08",
- "release_date": "2025-04-05",
- "last_updated": "2025-04-05",
+ "knowledge": "2025-05",
+ "release_date": "2025-07-10",
+ "last_updated": "2025-07-10",
"modalities": {
- "input": ["text", "image"],
+ "input": ["text"],
"output": ["text"]
},
"open_weights": true,
"cost": {
- "input": 0.11,
- "output": 0.34
+ "input": 0.1,
+ "output": 0.3
},
"limit": {
"context": 131072,
+ "output": 131072
+ }
+ },
+ "mistralai/mistral-small-3.2-24b-instruct": {
+ "id": "mistralai/mistral-small-3.2-24b-instruct",
+ "name": "Mistral Small 3.2 24B Instruct",
+ "attachment": true,
+ "reasoning": false,
+ "temperature": true,
+ "tool_call": true,
+ "knowledge": "2024-10",
+ "release_date": "2025-06-20",
+ "last_updated": "2025-06-20",
+ "modalities": {
+ "input": ["text", "image"],
+ "output": ["text"]
+ },
+ "open_weights": true,
+ "cost": {
+ "input": 0,
+ "output": 0
+ },
+ "limit": {
+ "context": 96000,
"output": 8192
}
},
- "meta-llama/llama-guard-4-12b": {
- "id": "meta-llama/llama-guard-4-12b",
- "name": "Llama Guard 4 12B",
+ "mistralai/mistral-7b-instruct:free": {
+ "id": "mistralai/mistral-7b-instruct:free",
+ "name": "Mistral 7B Instruct (free)",
"attachment": false,
"reasoning": false,
"temperature": true,
- "tool_call": false,
- "release_date": "2025-04-05",
- "last_updated": "2025-04-05",
+ "tool_call": true,
+ "knowledge": "2024-05",
+ "release_date": "2024-05-27",
+ "last_updated": "2024-05-27",
"modalities": {
- "input": ["text", "image"],
+ "input": ["text"],
"output": ["text"]
},
"open_weights": true,
"cost": {
- "input": 0.2,
- "output": 0.2
+ "input": 0,
+ "output": 0
},
"limit": {
- "context": 131072,
- "output": 128
+ "context": 32768,
+ "output": 32768
}
- }
- },
- "openrouter": {
- "openai/gpt-4.1-mini": {
- "id": "openai/gpt-4.1-mini",
- "name": "GPT-4.1 Mini",
- "attachment": true,
+ },
+ "mistralai/devstral-medium-2507": {
+ "id": "mistralai/devstral-medium-2507",
+ "name": "Devstral Medium",
+ "attachment": false,
"reasoning": false,
"temperature": true,
"tool_call": true,
- "knowledge": "2024-04",
- "release_date": "2025-04-14",
- "last_updated": "2025-04-14",
+ "knowledge": "2025-05",
+ "release_date": "2025-07-10",
+ "last_updated": "2025-07-10",
"modalities": {
- "input": ["text", "image"],
+ "input": ["text"],
"output": ["text"]
},
- "open_weights": false,
+ "open_weights": true,
"cost": {
"input": 0.4,
- "output": 1.6,
- "cache_read": 0.1
+ "output": 2
},
"limit": {
- "context": 1047576,
- "output": 32768
+ "context": 131072,
+ "output": 131072
}
},
- "openai/gpt-4.1": {
- "id": "openai/gpt-4.1",
- "name": "GPT-4.1",
+ "mistralai/mistral-small-3.2-24b-instruct:free": {
+ "id": "mistralai/mistral-small-3.2-24b-instruct:free",
+ "name": "Mistral Small 3.2 24B (free)",
"attachment": true,
"reasoning": false,
"temperature": true,
"tool_call": true,
- "knowledge": "2024-04",
- "release_date": "2025-04-14",
- "last_updated": "2025-04-14",
+ "knowledge": "2025-06",
+ "release_date": "2025-06-20",
+ "last_updated": "2025-06-20",
"modalities": {
"input": ["text", "image"],
"output": ["text"]
},
- "open_weights": false,
+ "open_weights": true,
"cost": {
- "input": 2,
- "output": 8,
- "cache_read": 0.5
+ "input": 0,
+ "output": 0
},
"limit": {
- "context": 1047576,
- "output": 32768
+ "context": 96000,
+ "output": 96000
}
},
- "openai/gpt-4o-mini": {
- "id": "openai/gpt-4o-mini",
- "name": "GPT-4o-mini",
- "attachment": true,
+ "mistralai/devstral-small-2505:free": {
+ "id": "mistralai/devstral-small-2505:free",
+ "name": "Devstral Small 2505 (free)",
+ "attachment": false,
"reasoning": false,
"temperature": true,
"tool_call": true,
- "knowledge": "2024-10",
- "release_date": "2024-07-18",
- "last_updated": "2024-07-18",
+ "knowledge": "2025-05",
+ "release_date": "2025-05-21",
+ "last_updated": "2025-05-21",
"modalities": {
- "input": ["text", "image"],
+ "input": ["text"],
"output": ["text"]
},
- "open_weights": false,
+ "open_weights": true,
"cost": {
- "input": 0.15,
- "output": 0.6,
- "cache_read": 0.08
+ "input": 0,
+ "output": 0
},
"limit": {
- "context": 128000,
- "output": 16384
+ "context": 32768,
+ "output": 32768
}
},
- "openai/o4-mini": {
- "id": "openai/o4-mini",
- "name": "o4 Mini",
- "attachment": true,
- "reasoning": true,
+ "mistralai/mistral-nemo:free": {
+ "id": "mistralai/mistral-nemo:free",
+ "name": "Mistral Nemo (free)",
+ "attachment": false,
+ "reasoning": false,
"temperature": true,
"tool_call": true,
- "knowledge": "2024-06",
- "release_date": "2025-04-16",
- "last_updated": "2025-04-16",
+ "knowledge": "2024-07",
+ "release_date": "2024-07-19",
+ "last_updated": "2024-07-19",
"modalities": {
- "input": ["text", "image"],
+ "input": ["text"],
"output": ["text"]
},
- "open_weights": false,
+ "open_weights": true,
"cost": {
- "input": 1.1,
- "output": 4.4,
- "cache_read": 0.28
+ "input": 0,
+ "output": 0
},
"limit": {
- "context": 200000,
- "output": 100000
+ "context": 131072,
+ "output": 131072
}
},
- "anthropic/claude-4-sonnet-20250522": {
- "id": "anthropic/claude-4-sonnet-20250522",
- "name": "Claude Sonnet 4",
- "attachment": true,
- "reasoning": true,
+ "mistralai/devstral-small-2505": {
+ "id": "mistralai/devstral-small-2505",
+ "name": "Devstral Small",
+ "attachment": false,
+ "reasoning": false,
"temperature": true,
"tool_call": true,
- "knowledge": "2025-03-31",
- "release_date": "2025-05-22",
- "last_updated": "2025-05-22",
+ "knowledge": "2025-05",
+ "release_date": "2025-05-07",
+ "last_updated": "2025-05-07",
"modalities": {
- "input": ["text", "image"],
+ "input": ["text"],
"output": ["text"]
},
- "open_weights": false,
+ "open_weights": true,
"cost": {
- "input": 3,
- "output": 15,
- "cache_read": 0.3,
- "cache_write": 3.75
+ "input": 0.06,
+ "output": 0.12
},
"limit": {
- "context": 200000,
- "output": 64000
+ "context": 128000,
+ "output": 128000
}
},
- "anthropic/claude-3.7-sonnet": {
- "id": "anthropic/claude-3.7-sonnet",
- "name": "Claude Sonnet 3.7",
- "attachment": true,
+ "nousresearch/deephermes-3-llama-3-8b-preview": {
+ "id": "nousresearch/deephermes-3-llama-3-8b-preview",
+ "name": "DeepHermes 3 Llama 3 8B Preview",
+ "attachment": false,
"reasoning": true,
"temperature": true,
"tool_call": true,
- "knowledge": "2024-01",
- "release_date": "2025-02-19",
- "last_updated": "2025-02-19",
+ "knowledge": "2024-04",
+ "release_date": "2025-02-28",
+ "last_updated": "2025-02-28",
"modalities": {
- "input": ["text", "image"],
+ "input": ["text"],
"output": ["text"]
},
- "open_weights": false,
+ "open_weights": true,
"cost": {
- "input": 15,
- "output": 75,
- "cache_read": 1.5,
- "cache_write": 18.75
+ "input": 0,
+ "output": 0
},
"limit": {
- "context": 200000,
- "output": 128000
+ "context": 131072,
+ "output": 8192
}
},
- "anthropic/claude-opus-4": {
- "id": "anthropic/claude-opus-4",
- "name": "Claude Opus 4",
- "attachment": true,
+ "tngtech/deepseek-r1t2-chimera:free": {
+ "id": "tngtech/deepseek-r1t2-chimera:free",
+ "name": "DeepSeek R1T2 Chimera (free)",
+ "attachment": false,
"reasoning": true,
"temperature": true,
"tool_call": true,
- "knowledge": "2025-03-31",
- "release_date": "2025-05-22",
- "last_updated": "2025-05-22",
+ "knowledge": "2025-07",
+ "release_date": "2025-07-08",
+ "last_updated": "2025-07-08",
"modalities": {
- "input": ["text", "image"],
+ "input": ["text"],
"output": ["text"]
},
- "open_weights": false,
+ "open_weights": true,
"cost": {
- "input": 15,
- "output": 75,
- "cache_read": 1.5,
- "cache_write": 18.75
+ "input": 0,
+ "output": 0
},
"limit": {
- "context": 200000,
- "output": 32000
+ "context": 163840,
+ "output": 163840
}
},
- "x-ai/grok-3-mini": {
- "id": "x-ai/grok-3-mini",
- "name": "Grok 3 Mini",
+ "qwen/qwen3-30b-a3b:free": {
+ "id": "qwen/qwen3-30b-a3b:free",
+ "name": "Qwen3 30B A3B (free)",
"attachment": false,
"reasoning": true,
"temperature": true,
"tool_call": true,
- "knowledge": "2024-11",
- "release_date": "2025-02-17",
- "last_updated": "2025-02-17",
+ "knowledge": "2025-04",
+ "release_date": "2025-04-28",
+ "last_updated": "2025-04-28",
"modalities": {
"input": ["text"],
"output": ["text"]
},
- "open_weights": false,
+ "open_weights": true,
"cost": {
- "input": 0.3,
- "output": 0.5,
- "cache_read": 0.075,
- "cache_write": 0.5
+ "input": 0,
+ "output": 0
},
"limit": {
- "context": 131072,
- "output": 8192
+ "context": 40960,
+ "output": 40960
}
},
- "x-ai/grok-3-mini-beta": {
- "id": "x-ai/grok-3-mini-beta",
- "name": "Grok 3 Mini Beta",
+ "qwen/qwen3-235b-a22b:free": {
+ "id": "qwen/qwen3-235b-a22b:free",
+ "name": "Qwen3 235B A22B (free)",
"attachment": false,
"reasoning": true,
"temperature": true,
"tool_call": true,
- "knowledge": "2024-11",
- "release_date": "2025-02-17",
- "last_updated": "2025-02-17",
+ "knowledge": "2025-04",
+ "release_date": "2025-04-28",
+ "last_updated": "2025-04-28",
"modalities": {
"input": ["text"],
"output": ["text"]
},
- "open_weights": false,
+ "open_weights": true,
"cost": {
- "input": 0.3,
- "output": 0.5,
- "cache_read": 0.075,
- "cache_write": 0.5
+ "input": 0,
+ "output": 0
},
"limit": {
"context": 131072,
- "output": 8192
+ "output": 131072
}
},
- "x-ai/grok-3": {
- "id": "x-ai/grok-3",
- "name": "Grok 3",
+ "qwen/qwq-32b:free": {
+ "id": "qwen/qwq-32b:free",
+ "name": "QwQ 32B (free)",
"attachment": false,
- "reasoning": false,
+ "reasoning": true,
"temperature": true,
"tool_call": true,
- "knowledge": "2024-11",
- "release_date": "2025-02-17",
- "last_updated": "2025-02-17",
+ "knowledge": "2025-03",
+ "release_date": "2025-03-05",
+ "last_updated": "2025-03-05",
"modalities": {
"input": ["text"],
"output": ["text"]
},
- "open_weights": false,
+ "open_weights": true,
"cost": {
- "input": 3,
- "output": 15,
- "cache_read": 0.75,
- "cache_write": 15
+ "input": 0,
+ "output": 0
},
"limit": {
- "context": 131072,
- "output": 8192
+ "context": 32768,
+ "output": 32768
}
},
- "x-ai/grok-3-beta": {
- "id": "x-ai/grok-3-beta",
- "name": "Grok 3 Beta",
+ "qwen/qwen-2.5-coder-32b-instruct": {
+ "id": "qwen/qwen-2.5-coder-32b-instruct",
+ "name": "Qwen2.5 Coder 32B Instruct",
"attachment": false,
"reasoning": false,
"temperature": true,
- "tool_call": true,
- "knowledge": "2024-11",
- "release_date": "2025-02-17",
- "last_updated": "2025-02-17",
+ "tool_call": false,
+ "knowledge": "2024-10",
+ "release_date": "2024-11-11",
+ "last_updated": "2024-11-11",
"modalities": {
"input": ["text"],
"output": ["text"]
},
- "open_weights": false,
+ "open_weights": true,
"cost": {
- "input": 3,
- "output": 15,
- "cache_read": 0.75,
- "cache_write": 15
+ "input": 0,
+ "output": 0
},
"limit": {
- "context": 131072,
+ "context": 32768,
"output": 8192
}
},
- "google/gemini-2.5-pro-preview-05-06": {
- "id": "google/gemini-2.5-pro-preview-05-06",
- "name": "Gemini 2.5 Pro Preview 05-06",
- "attachment": true,
+ "qwen/qwen3-8b:free": {
+ "id": "qwen/qwen3-8b:free",
+ "name": "Qwen3 8B (free)",
+ "attachment": false,
"reasoning": true,
"temperature": true,
"tool_call": true,
- "knowledge": "2025-01",
- "release_date": "2025-05-06",
- "last_updated": "2025-05-06",
+ "knowledge": "2025-04",
+ "release_date": "2025-04-28",
+ "last_updated": "2025-04-28",
"modalities": {
- "input": ["text", "image", "audio", "video", "pdf"],
+ "input": ["text"],
"output": ["text"]
},
- "open_weights": false,
+ "open_weights": true,
"cost": {
- "input": 1.25,
- "output": 10,
- "cache_read": 0.31
+ "input": 0,
+ "output": 0
},
"limit": {
- "context": 1048576,
- "output": 65536
+ "context": 40960,
+ "output": 40960
}
},
- "google/gemini-2.5-pro-preview-06-05": {
- "id": "google/gemini-2.5-pro-preview-06-05",
- "name": "Gemini 2.5 Pro Preview 06-05",
- "attachment": true,
+ "qwen/qwen3-14b:free": {
+ "id": "qwen/qwen3-14b:free",
+ "name": "Qwen3 14B (free)",
+ "attachment": false,
"reasoning": true,
"temperature": true,
"tool_call": true,
- "knowledge": "2025-01",
- "release_date": "2025-06-05",
- "last_updated": "2025-06-05",
+ "knowledge": "2025-04",
+ "release_date": "2025-04-28",
+ "last_updated": "2025-04-28",
"modalities": {
- "input": ["text", "image", "audio", "video", "pdf"],
+ "input": ["text"],
"output": ["text"]
},
- "open_weights": false,
+ "open_weights": true,
"cost": {
- "input": 1.25,
- "output": 10,
- "cache_read": 0.31
+ "input": 0,
+ "output": 0
},
"limit": {
- "context": 1048576,
- "output": 65536
+ "context": 40960,
+ "output": 40960
}
},
- "google/gemini-2.5-pro": {
- "id": "google/gemini-2.5-pro",
- "name": "Gemini 2.5 Pro",
+ "qwen/qwen2.5-vl-72b-instruct": {
+ "id": "qwen/qwen2.5-vl-72b-instruct",
+ "name": "Qwen2.5 VL 72B Instruct",
"attachment": true,
- "reasoning": true,
+ "reasoning": false,
"temperature": true,
- "tool_call": true,
- "knowledge": "2025-01",
- "release_date": "2025-03-20",
- "last_updated": "2025-06-05",
+ "tool_call": false,
+ "knowledge": "2024-10",
+ "release_date": "2025-02-01",
+ "last_updated": "2025-02-01",
"modalities": {
- "input": ["text", "image", "audio", "video", "pdf"],
+ "input": ["text", "image"],
"output": ["text"]
},
- "open_weights": false,
+ "open_weights": true,
"cost": {
- "input": 1.25,
- "output": 10,
- "cache_read": 0.31
+ "input": 0,
+ "output": 0
},
"limit": {
- "context": 1048576,
- "output": 65536
+ "context": 32768,
+ "output": 8192
}
},
- "google/gemini-2.5-flash-preview-04-17": {
- "id": "google/gemini-2.5-flash-preview-04-17",
- "name": "Gemini 2.5 Flash Preview 04-17",
+ "qwen/qwen2.5-vl-72b-instruct:free": {
+ "id": "qwen/qwen2.5-vl-72b-instruct:free",
+ "name": "Qwen2.5 VL 72B Instruct (free)",
"attachment": true,
- "reasoning": true,
+ "reasoning": false,
"temperature": true,
"tool_call": true,
- "knowledge": "2025-01",
- "release_date": "2025-04-17",
- "last_updated": "2025-04-17",
+ "knowledge": "2025-02",
+ "release_date": "2025-02-01",
+ "last_updated": "2025-02-01",
"modalities": {
- "input": ["text", "image", "audio", "video", "pdf"],
+ "input": ["text", "image"],
"output": ["text"]
},
- "open_weights": false,
+ "open_weights": true,
"cost": {
- "input": 0.15,
- "output": 0.6,
- "cache_read": 0.0375
+ "input": 0,
+ "output": 0
},
"limit": {
- "context": 1048576,
- "output": 65536
+ "context": 32768,
+ "output": 32768
}
},
- "google/gemini-2.5-flash-preview-05-20": {
- "id": "google/gemini-2.5-flash-preview-05-20",
- "name": "Gemini 2.5 Flash Preview 05-20",
+ "qwen/qwen2.5-vl-32b-instruct:free": {
+ "id": "qwen/qwen2.5-vl-32b-instruct:free",
+ "name": "Qwen2.5 VL 32B Instruct (free)",
"attachment": true,
- "reasoning": true,
+ "reasoning": false,
"temperature": true,
"tool_call": true,
- "knowledge": "2025-01",
- "release_date": "2025-05-20",
- "last_updated": "2025-05-20",
+ "knowledge": "2025-03",
+ "release_date": "2025-03-24",
+ "last_updated": "2025-03-24",
"modalities": {
- "input": ["text", "image", "audio", "video", "pdf"],
+ "input": ["text", "image", "video"],
"output": ["text"]
},
- "open_weights": false,
+ "open_weights": true,
"cost": {
- "input": 0.15,
- "output": 0.6,
- "cache_read": 0.0375
+ "input": 0,
+ "output": 0
},
"limit": {
- "context": 1048576,
- "output": 65536
+ "context": 8192,
+ "output": 8192
}
},
- "google/gemini-2.0-flash-001": {
- "id": "google/gemini-2.0-flash-001",
- "name": "Gemini 2.0 Flash",
- "attachment": true,
- "reasoning": false,
+ "qwen/qwen3-32b:free": {
+ "id": "qwen/qwen3-32b:free",
+ "name": "Qwen3 32B (free)",
+ "attachment": false,
+ "reasoning": true,
"temperature": true,
"tool_call": true,
- "knowledge": "2024-06",
- "release_date": "2024-12-11",
- "last_updated": "2024-12-11",
+ "knowledge": "2025-04",
+ "release_date": "2025-04-28",
+ "last_updated": "2025-04-28",
"modalities": {
- "input": ["text", "image", "audio", "video", "pdf"],
+ "input": ["text"],
"output": ["text"]
},
- "open_weights": false,
+ "open_weights": true,
"cost": {
- "input": 0.1,
- "output": 0.4,
- "cache_read": 0.025
+ "input": 0,
+ "output": 0
},
"limit": {
- "context": 1048576,
- "output": 8192
+ "context": 40960,
+ "output": 40960
}
}
}
diff --git a/examples/chat-ui/src/hooks/useStreamResponse.ts b/examples/chat-ui/src/hooks/useStreamResponse.ts
index dbfdec0..f8e9b6b 100644
--- a/examples/chat-ui/src/hooks/useStreamResponse.ts
+++ b/examples/chat-ui/src/hooks/useStreamResponse.ts
@@ -90,7 +90,7 @@ export const useStreamResponse = ({
switch (model.provider.id) {
case 'groq': {
const apiKey = authHeaders.Authorization?.replace('Bearer ', '')
- const groqProvider = createGroq({ apiKey })
+ const groqProvider = createGroq({ apiKey, baseURL: model.provider.baseUrl })
baseModel = groqProvider(model.modelId)
break
}
diff --git a/examples/chat-ui/src/types/models.ts b/examples/chat-ui/src/types/models.ts
index 73c4d16..2e373a6 100644
--- a/examples/chat-ui/src/types/models.ts
+++ b/examples/chat-ui/src/types/models.ts
@@ -67,13 +67,22 @@ export const providers: Record = {
baseUrl: 'https://api.groq.com/openai/v1',
logo: '🚀',
documentationUrl: 'https://console.groq.com/docs',
- authType: 'apiKey',
apiKeyHeader: 'Authorization',
- // oauth: {
- // authorizeUrl: 'http://localhost:3000/keys/request',
- // tokenUrl: 'https://openrouter.ai/api/v1/auth/keys'
- // },
+ authType: 'apiKey',
},
+ // groq: {
+ // id: 'groq',
+ // name: 'Groq',
+ // baseUrl: 'http://localhost:8000/api/openai/v1',
+ // logo: '🚀',
+ // documentationUrl: 'https://console.groq.com/docs',
+ // apiKeyHeader: 'Authorization',
+ // authType: 'oauth',
+ // oauth: {
+ // authorizeUrl: 'http://localhost:3000/keys/request',
+ // tokenUrl: 'http://localhost:3000/keys/request/exchange',
+ // },
+ // },
anthropic: {
id: 'anthropic',
name: 'Anthropic',
diff --git a/examples/chat-ui/src/utils/auth.ts b/examples/chat-ui/src/utils/auth.ts
index d476cfe..b1f46ff 100644
--- a/examples/chat-ui/src/utils/auth.ts
+++ b/examples/chat-ui/src/utils/auth.ts
@@ -11,7 +11,8 @@ export interface OAuthToken {
// Types for PKCE flow
interface PKCEState {
code_verifier: string
- state: string
+ // TODO: Add state support back if needed later
+ // state: string
}
// Generate a random code verifier for PKCE
@@ -35,15 +36,15 @@ async function generateCodeChallenge(verifier: string): Promise {
.replace(/=/g, '')
}
-// Generate random state parameter
-function generateState(): string {
- const array = new Uint8Array(16)
- crypto.getRandomValues(array)
- return btoa(String.fromCharCode(...array))
- .replace(/\+/g, '-')
- .replace(/\//g, '_')
- .replace(/=/g, '')
-}
+// TODO: Add state generation back if needed later
+// function generateState(): string {
+// const array = new Uint8Array(16)
+// crypto.getRandomValues(array)
+// return btoa(String.fromCharCode(...array))
+// .replace(/\+/g, '-')
+// .replace(/\//g, '_')
+// .replace(/=/g, '')
+// }
// API Key functions (existing functionality)
export function hasApiKey(providerId: SupportedProvider): boolean {
@@ -111,11 +112,17 @@ export async function beginOAuthFlow(providerId: SupportedProvider): Promise {
- console.log('DEBUG: Starting OAuth completion for', providerId, 'with code:', code?.substring(0, 10) + '...', 'state:', state)
+export async function completeOAuthFlow(providerId: SupportedProvider, code: string): Promise {
+ console.log('DEBUG: Starting OAuth completion for', providerId, 'with code:', code?.substring(0, 10) + '...')
+ console.log('DEBUG: Full code for debugging:', code)
+ console.log('DEBUG: Provider config:', providers[providerId])
const provider = providers[providerId]
if (!provider.oauth) {
throw new Error(`Provider ${providerId} does not support OAuth`)
}
- // Retrieve PKCE state
- let pkceState: PKCEState
+ // Retrieve PKCE state (single key per provider)
+ const storageKey = `pkce_${providerId}`
+ const pkceStateJson = sessionStorage.getItem(storageKey)
- if (state === 'no-state' && providerId === 'openrouter') {
- // OpenRouter doesn't use state, find the most recent PKCE state for this provider
- const allKeys = Object.keys(sessionStorage)
- const pkceKeys = allKeys.filter((key) => key.startsWith(`pkce_${providerId}_`))
+ console.log('DEBUG: Looking for PKCE key:', storageKey)
- console.log('DEBUG: Found PKCE keys:', pkceKeys)
-
- if (pkceKeys.length === 0) {
- throw new Error('PKCE state not found. Please try again.')
- }
+ if (!pkceStateJson) {
+ throw new Error('PKCE state not found. Please try again.')
+ }
- // Use the most recent one (sort by timestamp)
- const sortedKeys = pkceKeys.sort((a, b) => {
- const aTime = parseInt(a.split('_').pop() || '0')
- const bTime = parseInt(b.split('_').pop() || '0')
- return bTime - aTime // Most recent first
- })
+ const pkceState: PKCEState = JSON.parse(pkceStateJson)
- const pkceStateJson = sessionStorage.getItem(sortedKeys[0])!
- pkceState = JSON.parse(pkceStateJson)
+ console.log('DEBUG: Using PKCE state:', { key: storageKey, state: pkceState })
+ console.log('DEBUG: Code verifier length:', pkceState.code_verifier.length)
+ console.log('DEBUG: Code verifier sample:', pkceState.code_verifier.substring(0, 20) + '...')
- console.log('DEBUG: Using PKCE state:', { key: sortedKeys[0], state: pkceState })
+ // Test: regenerate challenge from verifier to verify it matches server expectation
+ const recomputedChallenge = await generateCodeChallenge(pkceState.code_verifier)
+ console.log('DEBUG: Recomputed challenge from verifier:', recomputedChallenge)
+ console.log('DEBUG: Server reported challenge was: dW2iEvNljlkhcRcryo3Z0GITcJM1liKcHlB5v8CDEu8')
- // Clean up the state
- sessionStorage.removeItem(sortedKeys[0])
- } else {
- const pkceStateJson = sessionStorage.getItem(`pkce_${providerId}_${state}`)
- if (!pkceStateJson) {
- throw new Error('PKCE state not found. Please try again.')
- }
- pkceState = JSON.parse(pkceStateJson)
- console.log('DEBUG: Using PKCE state for state', state, ':', pkceState)
- }
+ // Clean up the state
+ sessionStorage.removeItem(storageKey)
// Exchange code for token
let tokenResponse: Response
@@ -218,18 +224,23 @@ export async function completeOAuthFlow(providerId: SupportedProvider, code: str
duration: `${endTime - startTime}ms`,
})
} else {
- // Standard OAuth2 flow for other providers
+ // Standard OAuth2 flow for other providers (Groq)
const requestBody = new URLSearchParams({
grant_type: 'authorization_code',
code,
redirect_uri: getRedirectUri(providerId),
code_verifier: pkceState.code_verifier,
})
- console.log('DEBUG: Standard OAuth token request:', {
+
+ console.log('DEBUG: Groq token request:', {
url: provider.oauth.tokenUrl,
body: Object.fromEntries(requestBody.entries()),
+ codeVerifierLength: pkceState.code_verifier.length,
+ codeVerifierSample: pkceState.code_verifier.substring(0, 20) + '...',
+ fullCodeVerifier: pkceState.code_verifier, // For debugging
})
+ const startTime = performance.now()
tokenResponse = await fetch(provider.oauth.tokenUrl, {
method: 'POST',
headers: {
@@ -237,11 +248,13 @@ export async function completeOAuthFlow(providerId: SupportedProvider, code: str
},
body: requestBody,
})
+ const endTime = performance.now()
- console.log('DEBUG: Standard OAuth token response:', {
+ console.log('DEBUG: Groq token response:', {
status: tokenResponse.status,
statusText: tokenResponse.statusText,
headers: Object.fromEntries(tokenResponse.headers.entries()),
+ duration: `${endTime - startTime}ms`,
})
}
@@ -263,6 +276,12 @@ export async function completeOAuthFlow(providerId: SupportedProvider, code: str
access_token: tokenData.key,
token_type: 'Bearer',
}
+ } else if (providerId === 'groq') {
+ // Groq returns { api_key: "..." }
+ token = {
+ access_token: tokenData.api_key,
+ token_type: 'Bearer',
+ }
} else {
// Standard OAuth2 response
token = {
@@ -273,12 +292,10 @@ export async function completeOAuthFlow(providerId: SupportedProvider, code: str
}
}
+ console.log('DEBUG: Saving token for', providerId, ':', token)
setOAuthToken(providerId, token)
- // Clean up PKCE state (already cleaned up above for OpenRouter)
- if (state !== 'no-state') {
- sessionStorage.removeItem(`pkce_${providerId}_${state}`)
- }
+ // PKCE state already cleaned up above
}
// Get authentication headers for API calls
@@ -318,3 +335,30 @@ function getRedirectUri(providerId: SupportedProvider): string {
const baseUrl = window.location.origin
return `${baseUrl}/oauth/${providerId}/callback`
}
+
+// Test function to verify PKCE implementation with known values
+export async function testPKCEImplementation(): Promise {
+ console.log('=== TESTING PKCE IMPLEMENTATION ===')
+
+ // Test with a known code verifier (from RFC 7636 example)
+ const testVerifier = 'dBjftJeZ4CVP-mB92K27uhbUJU1p1r_wW1gFWFOEjXk'
+ const expectedChallenge = 'E9Melhoa2OwvFrEMTJguCHaoeK1t8URWbuGJSstw-cM'
+
+ const computedChallenge = await generateCodeChallenge(testVerifier)
+
+ console.log('DEBUG: Test verifier:', testVerifier)
+ console.log('DEBUG: Expected challenge:', expectedChallenge)
+ console.log('DEBUG: Computed challenge:', computedChallenge)
+ console.log('DEBUG: Challenges match:', computedChallenge === expectedChallenge)
+
+ // Test with current implementation
+ const currentVerifier = generateCodeVerifier()
+ const currentChallenge = await generateCodeChallenge(currentVerifier)
+
+ console.log('DEBUG: Current verifier:', currentVerifier)
+ console.log('DEBUG: Current challenge:', currentChallenge)
+ console.log('DEBUG: Verifier length:', currentVerifier.length)
+ console.log('DEBUG: Challenge length:', currentChallenge.length)
+
+ console.log('=== END PKCE TEST ===')
+}