mirror of
https://github.com/simstudioai/sim.git
synced 2026-04-06 03:00:16 -04:00
feat(mistal): added mistral as a provider, updated model prices (#1607)
* feat(mistal): added mistral as a provider, updated model prices * remove the ability for a block to reference its own outluts * fixed order of responses for guardrails block
This commit is contained in:
@@ -337,21 +337,21 @@ export const GuardrailsBlock: BlockConfig<GuardrailsResponse> = {
|
||||
},
|
||||
},
|
||||
outputs: {
|
||||
passed: {
|
||||
type: 'boolean',
|
||||
description: 'Whether validation passed (true/false)',
|
||||
input: {
|
||||
type: 'string',
|
||||
description: 'Original input that was validated',
|
||||
},
|
||||
maskedText: {
|
||||
type: 'string',
|
||||
description: 'Text with PII masked (only for PII detection in mask mode)',
|
||||
},
|
||||
validationType: {
|
||||
type: 'string',
|
||||
description: 'Type of validation performed',
|
||||
},
|
||||
input: {
|
||||
type: 'string',
|
||||
description: 'Original input that was validated',
|
||||
},
|
||||
error: {
|
||||
type: 'string',
|
||||
description: 'Error message if validation failed',
|
||||
passed: {
|
||||
type: 'boolean',
|
||||
description: 'Whether validation passed (true/false)',
|
||||
},
|
||||
score: {
|
||||
type: 'number',
|
||||
@@ -366,9 +366,9 @@ export const GuardrailsBlock: BlockConfig<GuardrailsResponse> = {
|
||||
type: 'array',
|
||||
description: 'Detected PII entities (only for PII detection)',
|
||||
},
|
||||
maskedText: {
|
||||
error: {
|
||||
type: 'string',
|
||||
description: 'Text with PII masked (only for PII detection in mask mode)',
|
||||
description: 'Error message if validation failed',
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
@@ -662,6 +662,9 @@ export const TagDropdown: React.FC<TagDropdownProps> = ({
|
||||
const accessibleBlock = blocks[accessibleBlockId]
|
||||
if (!accessibleBlock) continue
|
||||
|
||||
// Skip the current block - blocks cannot reference their own outputs
|
||||
if (accessibleBlockId === blockId) continue
|
||||
|
||||
const blockConfig = getBlock(accessibleBlock.type)
|
||||
|
||||
if (!blockConfig) {
|
||||
|
||||
@@ -16,6 +16,7 @@ const VALID_PROVIDER_IDS: readonly ProviderId[] = [
|
||||
'deepseek',
|
||||
'xai',
|
||||
'cerebras',
|
||||
'mistral',
|
||||
'groq',
|
||||
'ollama',
|
||||
] as const
|
||||
|
||||
@@ -41,6 +41,11 @@ export const TOKENIZATION_CONFIG = {
|
||||
confidence: 'medium',
|
||||
supportedMethods: ['heuristic', 'fallback'],
|
||||
},
|
||||
mistral: {
|
||||
avgCharsPerToken: 4,
|
||||
confidence: 'medium',
|
||||
supportedMethods: ['heuristic', 'fallback'],
|
||||
},
|
||||
groq: {
|
||||
avgCharsPerToken: 4,
|
||||
confidence: 'medium',
|
||||
|
||||
562
apps/sim/providers/mistral/index.ts
Normal file
562
apps/sim/providers/mistral/index.ts
Normal file
@@ -0,0 +1,562 @@
|
||||
import OpenAI from 'openai'
|
||||
import { createLogger } from '@/lib/logs/console/logger'
|
||||
import type { StreamingExecution } from '@/executor/types'
|
||||
import { getProviderDefaultModel, getProviderModels } from '@/providers/models'
|
||||
import type {
|
||||
ProviderConfig,
|
||||
ProviderRequest,
|
||||
ProviderResponse,
|
||||
TimeSegment,
|
||||
} from '@/providers/types'
|
||||
import {
|
||||
prepareToolExecution,
|
||||
prepareToolsWithUsageControl,
|
||||
trackForcedToolUsage,
|
||||
} from '@/providers/utils'
|
||||
import { executeTool } from '@/tools'
|
||||
|
||||
const logger = createLogger('MistralProvider')
|
||||
|
||||
function createReadableStreamFromMistralStream(
|
||||
mistralStream: any,
|
||||
onComplete?: (content: string, usage?: any) => void
|
||||
): ReadableStream {
|
||||
let fullContent = ''
|
||||
let usageData: any = null
|
||||
|
||||
return new ReadableStream({
|
||||
async start(controller) {
|
||||
try {
|
||||
for await (const chunk of mistralStream) {
|
||||
if (chunk.usage) {
|
||||
usageData = chunk.usage
|
||||
}
|
||||
|
||||
const content = chunk.choices[0]?.delta?.content || ''
|
||||
if (content) {
|
||||
fullContent += content
|
||||
controller.enqueue(new TextEncoder().encode(content))
|
||||
}
|
||||
}
|
||||
|
||||
if (onComplete) {
|
||||
onComplete(fullContent, usageData)
|
||||
}
|
||||
|
||||
controller.close()
|
||||
} catch (error) {
|
||||
controller.error(error)
|
||||
}
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* Mistral AI provider configuration
|
||||
*/
|
||||
export const mistralProvider: ProviderConfig = {
|
||||
id: 'mistral',
|
||||
name: 'Mistral AI',
|
||||
description: "Mistral AI's language models",
|
||||
version: '1.0.0',
|
||||
models: getProviderModels('mistral'),
|
||||
defaultModel: getProviderDefaultModel('mistral'),
|
||||
|
||||
executeRequest: async (
|
||||
request: ProviderRequest
|
||||
): Promise<ProviderResponse | StreamingExecution> => {
|
||||
logger.info('Preparing Mistral request', {
|
||||
model: request.model || 'mistral-large-latest',
|
||||
hasSystemPrompt: !!request.systemPrompt,
|
||||
hasMessages: !!request.messages?.length,
|
||||
hasTools: !!request.tools?.length,
|
||||
toolCount: request.tools?.length || 0,
|
||||
hasResponseFormat: !!request.responseFormat,
|
||||
stream: !!request.stream,
|
||||
})
|
||||
|
||||
if (!request.apiKey) {
|
||||
throw new Error('API key is required for Mistral AI')
|
||||
}
|
||||
|
||||
const mistral = new OpenAI({
|
||||
apiKey: request.apiKey,
|
||||
baseURL: 'https://api.mistral.ai/v1',
|
||||
})
|
||||
|
||||
const allMessages = []
|
||||
|
||||
if (request.systemPrompt) {
|
||||
allMessages.push({
|
||||
role: 'system',
|
||||
content: request.systemPrompt,
|
||||
})
|
||||
}
|
||||
|
||||
if (request.context) {
|
||||
allMessages.push({
|
||||
role: 'user',
|
||||
content: request.context,
|
||||
})
|
||||
}
|
||||
|
||||
if (request.messages) {
|
||||
allMessages.push(...request.messages)
|
||||
}
|
||||
|
||||
const tools = request.tools?.length
|
||||
? request.tools.map((tool) => ({
|
||||
type: 'function',
|
||||
function: {
|
||||
name: tool.id,
|
||||
description: tool.description,
|
||||
parameters: tool.parameters,
|
||||
},
|
||||
}))
|
||||
: undefined
|
||||
|
||||
const payload: any = {
|
||||
model: request.model || 'mistral-large-latest',
|
||||
messages: allMessages,
|
||||
}
|
||||
|
||||
if (request.temperature !== undefined) payload.temperature = request.temperature
|
||||
if (request.maxTokens !== undefined) payload.max_tokens = request.maxTokens
|
||||
|
||||
if (request.responseFormat) {
|
||||
payload.response_format = {
|
||||
type: 'json_schema',
|
||||
json_schema: {
|
||||
name: request.responseFormat.name || 'response_schema',
|
||||
schema: request.responseFormat.schema || request.responseFormat,
|
||||
strict: request.responseFormat.strict !== false,
|
||||
},
|
||||
}
|
||||
|
||||
logger.info('Added JSON schema response format to request')
|
||||
}
|
||||
|
||||
let preparedTools: ReturnType<typeof prepareToolsWithUsageControl> | null = null
|
||||
|
||||
if (tools?.length) {
|
||||
preparedTools = prepareToolsWithUsageControl(tools, request.tools, logger, 'mistral')
|
||||
const { tools: filteredTools, toolChoice } = preparedTools
|
||||
|
||||
if (filteredTools?.length && toolChoice) {
|
||||
payload.tools = filteredTools
|
||||
payload.tool_choice = toolChoice
|
||||
|
||||
logger.info('Mistral request configuration:', {
|
||||
toolCount: filteredTools.length,
|
||||
toolChoice:
|
||||
typeof toolChoice === 'string'
|
||||
? toolChoice
|
||||
: toolChoice.type === 'function'
|
||||
? `force:${toolChoice.function.name}`
|
||||
: toolChoice.type === 'tool'
|
||||
? `force:${toolChoice.name}`
|
||||
: toolChoice.type === 'any'
|
||||
? `force:${toolChoice.any?.name || 'unknown'}`
|
||||
: 'unknown',
|
||||
model: request.model || 'mistral-large-latest',
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
const providerStartTime = Date.now()
|
||||
const providerStartTimeISO = new Date(providerStartTime).toISOString()
|
||||
|
||||
try {
|
||||
if (request.stream && (!tools || tools.length === 0)) {
|
||||
logger.info('Using streaming response for Mistral request')
|
||||
|
||||
const streamResponse = await mistral.chat.completions.create({
|
||||
...payload,
|
||||
stream: true,
|
||||
stream_options: { include_usage: true },
|
||||
})
|
||||
|
||||
const tokenUsage = {
|
||||
prompt: 0,
|
||||
completion: 0,
|
||||
total: 0,
|
||||
}
|
||||
|
||||
let _streamContent = ''
|
||||
|
||||
const streamingResult = {
|
||||
stream: createReadableStreamFromMistralStream(streamResponse, (content, usage) => {
|
||||
_streamContent = content
|
||||
streamingResult.execution.output.content = content
|
||||
|
||||
const streamEndTime = Date.now()
|
||||
const streamEndTimeISO = new Date(streamEndTime).toISOString()
|
||||
|
||||
if (streamingResult.execution.output.providerTiming) {
|
||||
streamingResult.execution.output.providerTiming.endTime = streamEndTimeISO
|
||||
streamingResult.execution.output.providerTiming.duration =
|
||||
streamEndTime - providerStartTime
|
||||
|
||||
if (streamingResult.execution.output.providerTiming.timeSegments?.[0]) {
|
||||
streamingResult.execution.output.providerTiming.timeSegments[0].endTime =
|
||||
streamEndTime
|
||||
streamingResult.execution.output.providerTiming.timeSegments[0].duration =
|
||||
streamEndTime - providerStartTime
|
||||
}
|
||||
}
|
||||
|
||||
if (usage) {
|
||||
const newTokens = {
|
||||
prompt: usage.prompt_tokens || tokenUsage.prompt,
|
||||
completion: usage.completion_tokens || tokenUsage.completion,
|
||||
total: usage.total_tokens || tokenUsage.total,
|
||||
}
|
||||
|
||||
streamingResult.execution.output.tokens = newTokens
|
||||
}
|
||||
}),
|
||||
execution: {
|
||||
success: true,
|
||||
output: {
|
||||
content: '',
|
||||
model: request.model,
|
||||
tokens: tokenUsage,
|
||||
toolCalls: undefined,
|
||||
providerTiming: {
|
||||
startTime: providerStartTimeISO,
|
||||
endTime: new Date().toISOString(),
|
||||
duration: Date.now() - providerStartTime,
|
||||
timeSegments: [
|
||||
{
|
||||
type: 'model',
|
||||
name: 'Streaming response',
|
||||
startTime: providerStartTime,
|
||||
endTime: Date.now(),
|
||||
duration: Date.now() - providerStartTime,
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
logs: [],
|
||||
metadata: {
|
||||
startTime: providerStartTimeISO,
|
||||
endTime: new Date().toISOString(),
|
||||
duration: Date.now() - providerStartTime,
|
||||
},
|
||||
},
|
||||
} as StreamingExecution
|
||||
|
||||
return streamingResult as StreamingExecution
|
||||
}
|
||||
|
||||
const initialCallTime = Date.now()
|
||||
|
||||
const originalToolChoice = payload.tool_choice
|
||||
|
||||
const forcedTools = preparedTools?.forcedTools || []
|
||||
let usedForcedTools: string[] = []
|
||||
|
||||
const checkForForcedToolUsage = (
|
||||
response: any,
|
||||
toolChoice: string | { type: string; function?: { name: string }; name?: string; any?: any }
|
||||
) => {
|
||||
if (typeof toolChoice === 'object' && response.choices[0]?.message?.tool_calls) {
|
||||
const toolCallsResponse = response.choices[0].message.tool_calls
|
||||
const result = trackForcedToolUsage(
|
||||
toolCallsResponse,
|
||||
toolChoice,
|
||||
logger,
|
||||
'mistral',
|
||||
forcedTools,
|
||||
usedForcedTools
|
||||
)
|
||||
hasUsedForcedTool = result.hasUsedForcedTool
|
||||
usedForcedTools = result.usedForcedTools
|
||||
}
|
||||
}
|
||||
|
||||
let currentResponse = await mistral.chat.completions.create(payload)
|
||||
const firstResponseTime = Date.now() - initialCallTime
|
||||
|
||||
let content = currentResponse.choices[0]?.message?.content || ''
|
||||
const tokens = {
|
||||
prompt: currentResponse.usage?.prompt_tokens || 0,
|
||||
completion: currentResponse.usage?.completion_tokens || 0,
|
||||
total: currentResponse.usage?.total_tokens || 0,
|
||||
}
|
||||
const toolCalls = []
|
||||
const toolResults = []
|
||||
const currentMessages = [...allMessages]
|
||||
let iterationCount = 0
|
||||
const MAX_ITERATIONS = 10
|
||||
|
||||
let modelTime = firstResponseTime
|
||||
let toolsTime = 0
|
||||
|
||||
let hasUsedForcedTool = false
|
||||
|
||||
const timeSegments: TimeSegment[] = [
|
||||
{
|
||||
type: 'model',
|
||||
name: 'Initial response',
|
||||
startTime: initialCallTime,
|
||||
endTime: initialCallTime + firstResponseTime,
|
||||
duration: firstResponseTime,
|
||||
},
|
||||
]
|
||||
|
||||
checkForForcedToolUsage(currentResponse, originalToolChoice)
|
||||
|
||||
while (iterationCount < MAX_ITERATIONS) {
|
||||
const toolCallsInResponse = currentResponse.choices[0]?.message?.tool_calls
|
||||
if (!toolCallsInResponse || toolCallsInResponse.length === 0) {
|
||||
break
|
||||
}
|
||||
|
||||
logger.info(
|
||||
`Processing ${toolCallsInResponse.length} tool calls (iteration ${iterationCount + 1}/${MAX_ITERATIONS})`
|
||||
)
|
||||
|
||||
const toolsStartTime = Date.now()
|
||||
|
||||
for (const toolCall of toolCallsInResponse) {
|
||||
try {
|
||||
const toolName = toolCall.function.name
|
||||
const toolArgs = JSON.parse(toolCall.function.arguments)
|
||||
|
||||
const tool = request.tools?.find((t) => t.id === toolName)
|
||||
if (!tool) continue
|
||||
|
||||
const toolCallStartTime = Date.now()
|
||||
|
||||
const { toolParams, executionParams } = prepareToolExecution(tool, toolArgs, request)
|
||||
const result = await executeTool(toolName, executionParams, true)
|
||||
const toolCallEndTime = Date.now()
|
||||
const toolCallDuration = toolCallEndTime - toolCallStartTime
|
||||
|
||||
timeSegments.push({
|
||||
type: 'tool',
|
||||
name: toolName,
|
||||
startTime: toolCallStartTime,
|
||||
endTime: toolCallEndTime,
|
||||
duration: toolCallDuration,
|
||||
})
|
||||
|
||||
let resultContent: any
|
||||
if (result.success) {
|
||||
toolResults.push(result.output)
|
||||
resultContent = result.output
|
||||
} else {
|
||||
resultContent = {
|
||||
error: true,
|
||||
message: result.error || 'Tool execution failed',
|
||||
tool: toolName,
|
||||
}
|
||||
}
|
||||
|
||||
toolCalls.push({
|
||||
name: toolName,
|
||||
arguments: toolParams,
|
||||
startTime: new Date(toolCallStartTime).toISOString(),
|
||||
endTime: new Date(toolCallEndTime).toISOString(),
|
||||
duration: toolCallDuration,
|
||||
result: resultContent,
|
||||
success: result.success,
|
||||
})
|
||||
|
||||
currentMessages.push({
|
||||
role: 'assistant',
|
||||
content: null,
|
||||
tool_calls: [
|
||||
{
|
||||
id: toolCall.id,
|
||||
type: 'function',
|
||||
function: {
|
||||
name: toolName,
|
||||
arguments: toolCall.function.arguments,
|
||||
},
|
||||
},
|
||||
],
|
||||
})
|
||||
|
||||
currentMessages.push({
|
||||
role: 'tool',
|
||||
tool_call_id: toolCall.id,
|
||||
content: JSON.stringify(resultContent),
|
||||
})
|
||||
} catch (error) {
|
||||
logger.error('Error processing tool call:', {
|
||||
error,
|
||||
toolName: toolCall?.function?.name,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
const thisToolsTime = Date.now() - toolsStartTime
|
||||
toolsTime += thisToolsTime
|
||||
|
||||
const nextPayload = {
|
||||
...payload,
|
||||
messages: currentMessages,
|
||||
}
|
||||
|
||||
if (typeof originalToolChoice === 'object' && hasUsedForcedTool && forcedTools.length > 0) {
|
||||
const remainingTools = forcedTools.filter((tool) => !usedForcedTools.includes(tool))
|
||||
|
||||
if (remainingTools.length > 0) {
|
||||
nextPayload.tool_choice = {
|
||||
type: 'function',
|
||||
function: { name: remainingTools[0] },
|
||||
}
|
||||
logger.info(`Forcing next tool: ${remainingTools[0]}`)
|
||||
} else {
|
||||
nextPayload.tool_choice = 'auto'
|
||||
logger.info('All forced tools have been used, switching to auto tool_choice')
|
||||
}
|
||||
}
|
||||
|
||||
const nextModelStartTime = Date.now()
|
||||
|
||||
currentResponse = await mistral.chat.completions.create(nextPayload)
|
||||
|
||||
checkForForcedToolUsage(currentResponse, nextPayload.tool_choice)
|
||||
|
||||
const nextModelEndTime = Date.now()
|
||||
const thisModelTime = nextModelEndTime - nextModelStartTime
|
||||
|
||||
timeSegments.push({
|
||||
type: 'model',
|
||||
name: `Model response (iteration ${iterationCount + 1})`,
|
||||
startTime: nextModelStartTime,
|
||||
endTime: nextModelEndTime,
|
||||
duration: thisModelTime,
|
||||
})
|
||||
|
||||
modelTime += thisModelTime
|
||||
|
||||
if (currentResponse.choices[0]?.message?.content) {
|
||||
content = currentResponse.choices[0].message.content
|
||||
}
|
||||
|
||||
if (currentResponse.usage) {
|
||||
tokens.prompt += currentResponse.usage.prompt_tokens || 0
|
||||
tokens.completion += currentResponse.usage.completion_tokens || 0
|
||||
tokens.total += currentResponse.usage.total_tokens || 0
|
||||
}
|
||||
|
||||
iterationCount++
|
||||
}
|
||||
|
||||
if (request.stream && iterationCount > 0) {
|
||||
logger.info('Using streaming for final response after tool calls')
|
||||
|
||||
const streamingPayload = {
|
||||
...payload,
|
||||
messages: currentMessages,
|
||||
tool_choice: 'auto',
|
||||
stream: true,
|
||||
stream_options: { include_usage: true },
|
||||
}
|
||||
|
||||
const streamResponse = await mistral.chat.completions.create(streamingPayload)
|
||||
|
||||
let _streamContent = ''
|
||||
|
||||
const streamingResult = {
|
||||
stream: createReadableStreamFromMistralStream(streamResponse, (content, usage) => {
|
||||
_streamContent = content
|
||||
streamingResult.execution.output.content = content
|
||||
|
||||
if (usage) {
|
||||
const newTokens = {
|
||||
prompt: usage.prompt_tokens || tokens.prompt,
|
||||
completion: usage.completion_tokens || tokens.completion,
|
||||
total: usage.total_tokens || tokens.total,
|
||||
}
|
||||
|
||||
streamingResult.execution.output.tokens = newTokens
|
||||
}
|
||||
}),
|
||||
execution: {
|
||||
success: true,
|
||||
output: {
|
||||
content: '',
|
||||
model: request.model,
|
||||
tokens: {
|
||||
prompt: tokens.prompt,
|
||||
completion: tokens.completion,
|
||||
total: tokens.total,
|
||||
},
|
||||
toolCalls:
|
||||
toolCalls.length > 0
|
||||
? {
|
||||
list: toolCalls,
|
||||
count: toolCalls.length,
|
||||
}
|
||||
: undefined,
|
||||
providerTiming: {
|
||||
startTime: providerStartTimeISO,
|
||||
endTime: new Date().toISOString(),
|
||||
duration: Date.now() - providerStartTime,
|
||||
modelTime: modelTime,
|
||||
toolsTime: toolsTime,
|
||||
firstResponseTime: firstResponseTime,
|
||||
iterations: iterationCount + 1,
|
||||
timeSegments: timeSegments,
|
||||
},
|
||||
},
|
||||
logs: [],
|
||||
metadata: {
|
||||
startTime: providerStartTimeISO,
|
||||
endTime: new Date().toISOString(),
|
||||
duration: Date.now() - providerStartTime,
|
||||
},
|
||||
},
|
||||
} as StreamingExecution
|
||||
|
||||
return streamingResult as StreamingExecution
|
||||
}
|
||||
|
||||
const providerEndTime = Date.now()
|
||||
const providerEndTimeISO = new Date(providerEndTime).toISOString()
|
||||
const totalDuration = providerEndTime - providerStartTime
|
||||
|
||||
return {
|
||||
content,
|
||||
model: request.model,
|
||||
tokens,
|
||||
toolCalls: toolCalls.length > 0 ? toolCalls : undefined,
|
||||
toolResults: toolResults.length > 0 ? toolResults : undefined,
|
||||
timing: {
|
||||
startTime: providerStartTimeISO,
|
||||
endTime: providerEndTimeISO,
|
||||
duration: totalDuration,
|
||||
modelTime: modelTime,
|
||||
toolsTime: toolsTime,
|
||||
firstResponseTime: firstResponseTime,
|
||||
iterations: iterationCount + 1,
|
||||
timeSegments: timeSegments,
|
||||
},
|
||||
}
|
||||
} catch (error) {
|
||||
const providerEndTime = Date.now()
|
||||
const providerEndTimeISO = new Date(providerEndTime).toISOString()
|
||||
const totalDuration = providerEndTime - providerStartTime
|
||||
|
||||
logger.error('Error in Mistral request:', {
|
||||
error,
|
||||
duration: totalDuration,
|
||||
})
|
||||
|
||||
const enhancedError = new Error(error instanceof Error ? error.message : String(error))
|
||||
// @ts-ignore - Adding timing property to the error
|
||||
enhancedError.timing = {
|
||||
startTime: providerStartTimeISO,
|
||||
endTime: providerEndTimeISO,
|
||||
duration: totalDuration,
|
||||
}
|
||||
|
||||
throw enhancedError
|
||||
}
|
||||
},
|
||||
}
|
||||
@@ -15,6 +15,7 @@ import {
|
||||
DeepseekIcon,
|
||||
GeminiIcon,
|
||||
GroqIcon,
|
||||
MistralIcon,
|
||||
OllamaIcon,
|
||||
OpenAIIcon,
|
||||
OpenRouterIcon,
|
||||
@@ -359,13 +360,25 @@ export const PROVIDER_DEFINITIONS: Record<string, ProviderDefinition> = {
|
||||
id: 'anthropic',
|
||||
name: 'Anthropic',
|
||||
description: "Anthropic's Claude models",
|
||||
defaultModel: 'claude-sonnet-4-0',
|
||||
defaultModel: 'claude-sonnet-4-5',
|
||||
modelPatterns: [/^claude/],
|
||||
icon: AnthropicIcon,
|
||||
capabilities: {
|
||||
toolUsageControl: true,
|
||||
},
|
||||
models: [
|
||||
{
|
||||
id: 'claude-sonnet-4-5',
|
||||
pricing: {
|
||||
input: 3.0,
|
||||
cachedInput: 1.5,
|
||||
output: 15.0,
|
||||
updatedAt: '2025-10-11',
|
||||
},
|
||||
capabilities: {
|
||||
temperature: { min: 0, max: 1 },
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'claude-sonnet-4-0',
|
||||
pricing: {
|
||||
@@ -378,6 +391,18 @@ export const PROVIDER_DEFINITIONS: Record<string, ProviderDefinition> = {
|
||||
temperature: { min: 0, max: 1 },
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'claude-opus-4-1',
|
||||
pricing: {
|
||||
input: 15.0,
|
||||
cachedInput: 7.5,
|
||||
output: 75.0,
|
||||
updatedAt: '2025-10-11',
|
||||
},
|
||||
capabilities: {
|
||||
temperature: { min: 0, max: 1 },
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'claude-opus-4-0',
|
||||
pricing: {
|
||||
@@ -514,10 +539,46 @@ export const PROVIDER_DEFINITIONS: Record<string, ProviderDefinition> = {
|
||||
{
|
||||
id: 'grok-4-latest',
|
||||
pricing: {
|
||||
input: 5.0,
|
||||
cachedInput: 2.5,
|
||||
output: 25.0,
|
||||
updatedAt: '2025-07-10',
|
||||
input: 3.0,
|
||||
cachedInput: 1.5,
|
||||
output: 15.0,
|
||||
updatedAt: '2025-10-11',
|
||||
},
|
||||
capabilities: {
|
||||
temperature: { min: 0, max: 1 },
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'grok-4-fast-reasoning',
|
||||
pricing: {
|
||||
input: 0.2,
|
||||
cachedInput: 0.25,
|
||||
output: 0.5,
|
||||
updatedAt: '2025-10-11',
|
||||
},
|
||||
capabilities: {
|
||||
temperature: { min: 0, max: 1 },
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'grok-4-fast-non-reasoning',
|
||||
pricing: {
|
||||
input: 0.2,
|
||||
cachedInput: 0.25,
|
||||
output: 0.5,
|
||||
updatedAt: '2025-10-11',
|
||||
},
|
||||
capabilities: {
|
||||
temperature: { min: 0, max: 1 },
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'grok-code-fast-1',
|
||||
pricing: {
|
||||
input: 0.2,
|
||||
cachedInput: 0.25,
|
||||
output: 1.5,
|
||||
updatedAt: '2025-10-11',
|
||||
},
|
||||
capabilities: {
|
||||
temperature: { min: 0, max: 1 },
|
||||
@@ -560,13 +621,39 @@ export const PROVIDER_DEFINITIONS: Record<string, ProviderDefinition> = {
|
||||
toolUsageControl: false,
|
||||
},
|
||||
models: [
|
||||
{
|
||||
id: 'cerebras/llama-3.1-8b',
|
||||
pricing: {
|
||||
input: 0.1,
|
||||
output: 0.1,
|
||||
updatedAt: '2025-10-11',
|
||||
},
|
||||
capabilities: {},
|
||||
},
|
||||
{
|
||||
id: 'cerebras/llama-3.1-70b',
|
||||
pricing: {
|
||||
input: 0.6,
|
||||
output: 0.6,
|
||||
updatedAt: '2025-10-11',
|
||||
},
|
||||
capabilities: {},
|
||||
},
|
||||
{
|
||||
id: 'cerebras/llama-3.3-70b',
|
||||
pricing: {
|
||||
input: 0.94,
|
||||
cachedInput: 0.47,
|
||||
output: 0.94,
|
||||
updatedAt: '2025-03-21',
|
||||
input: 0.6,
|
||||
output: 0.6,
|
||||
updatedAt: '2025-10-11',
|
||||
},
|
||||
capabilities: {},
|
||||
},
|
||||
{
|
||||
id: 'cerebras/llama-4-scout-17b-16e-instruct',
|
||||
pricing: {
|
||||
input: 0.11,
|
||||
output: 0.34,
|
||||
updatedAt: '2025-10-11',
|
||||
},
|
||||
capabilities: {},
|
||||
},
|
||||
@@ -576,7 +663,7 @@ export const PROVIDER_DEFINITIONS: Record<string, ProviderDefinition> = {
|
||||
id: 'groq',
|
||||
name: 'Groq',
|
||||
description: "Groq's LLM models with high-performance inference",
|
||||
defaultModel: 'groq/openai/gpt-oss-120b',
|
||||
defaultModel: 'groq/llama-3.3-70b-versatile',
|
||||
modelPatterns: [/^groq/],
|
||||
icon: GroqIcon,
|
||||
capabilities: {
|
||||
@@ -587,9 +674,8 @@ export const PROVIDER_DEFINITIONS: Record<string, ProviderDefinition> = {
|
||||
id: 'groq/openai/gpt-oss-120b',
|
||||
pricing: {
|
||||
input: 0.15,
|
||||
cachedInput: 0.075,
|
||||
output: 0.75,
|
||||
updatedAt: '2025-08-05',
|
||||
updatedAt: '2025-10-11',
|
||||
},
|
||||
capabilities: {},
|
||||
},
|
||||
@@ -597,19 +683,8 @@ export const PROVIDER_DEFINITIONS: Record<string, ProviderDefinition> = {
|
||||
id: 'groq/openai/gpt-oss-20b',
|
||||
pricing: {
|
||||
input: 0.01,
|
||||
cachedInput: 0.005,
|
||||
output: 0.25,
|
||||
updatedAt: '2025-08-05',
|
||||
},
|
||||
capabilities: {},
|
||||
},
|
||||
{
|
||||
id: 'groq/gemma2-9b-it',
|
||||
pricing: {
|
||||
input: 0.04,
|
||||
cachedInput: 0.02,
|
||||
output: 0.04,
|
||||
updatedAt: '2025-08-05',
|
||||
updatedAt: '2025-10-11',
|
||||
},
|
||||
capabilities: {},
|
||||
},
|
||||
@@ -617,19 +692,71 @@ export const PROVIDER_DEFINITIONS: Record<string, ProviderDefinition> = {
|
||||
id: 'groq/llama-3.1-8b-instant',
|
||||
pricing: {
|
||||
input: 0.05,
|
||||
cachedInput: 0.025,
|
||||
output: 0.08,
|
||||
updatedAt: '2025-08-05',
|
||||
updatedAt: '2025-10-11',
|
||||
},
|
||||
capabilities: {},
|
||||
},
|
||||
{
|
||||
id: 'groq/llama-3.3-70b-versatile',
|
||||
pricing: {
|
||||
input: 0.35,
|
||||
cachedInput: 0.175,
|
||||
output: 0.61,
|
||||
updatedAt: '2025-08-05',
|
||||
input: 0.59,
|
||||
output: 0.79,
|
||||
updatedAt: '2025-10-11',
|
||||
},
|
||||
capabilities: {},
|
||||
},
|
||||
{
|
||||
id: 'groq/llama-4-scout-17b-instruct',
|
||||
pricing: {
|
||||
input: 0.11,
|
||||
output: 0.34,
|
||||
updatedAt: '2025-10-11',
|
||||
},
|
||||
capabilities: {},
|
||||
},
|
||||
{
|
||||
id: 'groq/llama-4-maverick-17b-instruct',
|
||||
pricing: {
|
||||
input: 0.5,
|
||||
output: 0.77,
|
||||
updatedAt: '2025-10-11',
|
||||
},
|
||||
capabilities: {},
|
||||
},
|
||||
{
|
||||
id: 'groq/meta-llama/llama-4-maverick-17b-128e-instruct',
|
||||
pricing: {
|
||||
input: 0.5,
|
||||
output: 0.77,
|
||||
updatedAt: '2025-10-11',
|
||||
},
|
||||
capabilities: {},
|
||||
},
|
||||
{
|
||||
id: 'groq/gemma2-9b-it',
|
||||
pricing: {
|
||||
input: 0.04,
|
||||
output: 0.04,
|
||||
updatedAt: '2025-10-11',
|
||||
},
|
||||
capabilities: {},
|
||||
},
|
||||
{
|
||||
id: 'groq/deepseek-r1-distill-llama-70b',
|
||||
pricing: {
|
||||
input: 0.59,
|
||||
output: 0.79,
|
||||
updatedAt: '2025-10-11',
|
||||
},
|
||||
capabilities: {},
|
||||
},
|
||||
{
|
||||
id: 'groq/moonshotai/kimi-k2-instruct',
|
||||
pricing: {
|
||||
input: 1.0,
|
||||
output: 3.0,
|
||||
updatedAt: '2025-10-11',
|
||||
},
|
||||
capabilities: {},
|
||||
},
|
||||
@@ -637,31 +764,177 @@ export const PROVIDER_DEFINITIONS: Record<string, ProviderDefinition> = {
|
||||
id: 'groq/meta-llama/llama-guard-4-12b',
|
||||
pricing: {
|
||||
input: 0.2,
|
||||
cachedInput: 0.1,
|
||||
output: 0.2,
|
||||
updatedAt: '2025-08-05',
|
||||
updatedAt: '2025-10-11',
|
||||
},
|
||||
capabilities: {},
|
||||
},
|
||||
],
|
||||
},
|
||||
mistral: {
|
||||
id: 'mistral',
|
||||
name: 'Mistral AI',
|
||||
description: "Mistral AI's language models",
|
||||
defaultModel: 'mistral-large-latest',
|
||||
modelPatterns: [/^mistral/, /^magistral/, /^open-mistral/, /^codestral/, /^ministral/],
|
||||
icon: MistralIcon,
|
||||
capabilities: {
|
||||
toolUsageControl: true,
|
||||
},
|
||||
models: [
|
||||
{
|
||||
id: 'groq/deepseek-r1-distill-llama-70b',
|
||||
id: 'mistral-large-latest',
|
||||
pricing: {
|
||||
input: 0.58,
|
||||
cachedInput: 0.29,
|
||||
output: 0.99,
|
||||
updatedAt: '2025-08-05',
|
||||
input: 2.0,
|
||||
output: 6.0,
|
||||
updatedAt: '2025-10-11',
|
||||
},
|
||||
capabilities: {
|
||||
temperature: { min: 0, max: 1 },
|
||||
},
|
||||
capabilities: {},
|
||||
},
|
||||
{
|
||||
id: 'groq/meta-llama/llama-4-maverick-17b-128e-instruct',
|
||||
id: 'mistral-large-2411',
|
||||
pricing: {
|
||||
input: 2.0,
|
||||
output: 6.0,
|
||||
updatedAt: '2025-10-11',
|
||||
},
|
||||
capabilities: {
|
||||
temperature: { min: 0, max: 1 },
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'magistral-medium-latest',
|
||||
pricing: {
|
||||
input: 2.0,
|
||||
output: 5.0,
|
||||
updatedAt: '2025-10-11',
|
||||
},
|
||||
capabilities: {
|
||||
temperature: { min: 0, max: 1 },
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'magistral-medium-2509',
|
||||
pricing: {
|
||||
input: 2.0,
|
||||
output: 5.0,
|
||||
updatedAt: '2025-10-11',
|
||||
},
|
||||
capabilities: {
|
||||
temperature: { min: 0, max: 1 },
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'mistral-medium-latest',
|
||||
pricing: {
|
||||
input: 0.4,
|
||||
output: 2.0,
|
||||
updatedAt: '2025-10-11',
|
||||
},
|
||||
capabilities: {
|
||||
temperature: { min: 0, max: 1 },
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'mistral-medium-2508',
|
||||
pricing: {
|
||||
input: 0.4,
|
||||
output: 2.0,
|
||||
updatedAt: '2025-10-11',
|
||||
},
|
||||
capabilities: {
|
||||
temperature: { min: 0, max: 1 },
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'mistral-small-latest',
|
||||
pricing: {
|
||||
input: 0.2,
|
||||
cachedInput: 0.1,
|
||||
output: 0.6,
|
||||
updatedAt: '2025-08-05',
|
||||
updatedAt: '2025-10-11',
|
||||
},
|
||||
capabilities: {
|
||||
temperature: { min: 0, max: 1 },
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'mistral-small-2506',
|
||||
pricing: {
|
||||
input: 0.2,
|
||||
output: 0.6,
|
||||
updatedAt: '2025-10-11',
|
||||
},
|
||||
capabilities: {
|
||||
temperature: { min: 0, max: 1 },
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'open-mistral-nemo',
|
||||
pricing: {
|
||||
input: 0.15,
|
||||
output: 0.15,
|
||||
updatedAt: '2025-10-11',
|
||||
},
|
||||
capabilities: {
|
||||
temperature: { min: 0, max: 1 },
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'codestral-latest',
|
||||
pricing: {
|
||||
input: 0.3,
|
||||
output: 0.9,
|
||||
updatedAt: '2025-10-11',
|
||||
},
|
||||
capabilities: {
|
||||
temperature: { min: 0, max: 1 },
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'codestral-2508',
|
||||
pricing: {
|
||||
input: 0.3,
|
||||
output: 0.9,
|
||||
updatedAt: '2025-10-11',
|
||||
},
|
||||
capabilities: {
|
||||
temperature: { min: 0, max: 1 },
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'ministral-8b-latest',
|
||||
pricing: {
|
||||
input: 0.1,
|
||||
output: 0.1,
|
||||
updatedAt: '2025-10-11',
|
||||
},
|
||||
capabilities: {
|
||||
temperature: { min: 0, max: 1 },
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'ministral-8b-2410',
|
||||
pricing: {
|
||||
input: 0.1,
|
||||
output: 0.1,
|
||||
updatedAt: '2025-10-11',
|
||||
},
|
||||
capabilities: {
|
||||
temperature: { min: 0, max: 1 },
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'ministral-3b-latest',
|
||||
pricing: {
|
||||
input: 0.04,
|
||||
output: 0.04,
|
||||
updatedAt: '2025-10-11',
|
||||
},
|
||||
capabilities: {
|
||||
temperature: { min: 0, max: 1 },
|
||||
},
|
||||
capabilities: {},
|
||||
},
|
||||
],
|
||||
},
|
||||
|
||||
@@ -9,6 +9,7 @@ export type ProviderId =
|
||||
| 'xai'
|
||||
| 'cerebras'
|
||||
| 'groq'
|
||||
| 'mistral'
|
||||
| 'ollama'
|
||||
| 'openrouter'
|
||||
|
||||
|
||||
@@ -248,6 +248,7 @@ describe('Model Capabilities', () => {
|
||||
const supportedProviders = [
|
||||
'openai',
|
||||
'azure-openai',
|
||||
'mistral',
|
||||
'anthropic',
|
||||
'deepseek',
|
||||
'xai',
|
||||
|
||||
@@ -6,6 +6,7 @@ import { cerebrasProvider } from '@/providers/cerebras'
|
||||
import { deepseekProvider } from '@/providers/deepseek'
|
||||
import { googleProvider } from '@/providers/google'
|
||||
import { groqProvider } from '@/providers/groq'
|
||||
import { mistralProvider } from '@/providers/mistral'
|
||||
import {
|
||||
getComputerUseModels,
|
||||
getEmbeddingModelPricing,
|
||||
@@ -84,6 +85,11 @@ export const providers: Record<
|
||||
models: getProviderModelsFromDefinitions('groq'),
|
||||
modelPatterns: PROVIDER_DEFINITIONS.groq.modelPatterns,
|
||||
},
|
||||
mistral: {
|
||||
...mistralProvider,
|
||||
models: getProviderModelsFromDefinitions('mistral'),
|
||||
modelPatterns: PROVIDER_DEFINITIONS.mistral.modelPatterns,
|
||||
},
|
||||
'azure-openai': {
|
||||
...azureOpenAIProvider,
|
||||
models: getProviderModelsFromDefinitions('azure-openai'),
|
||||
|
||||
Reference in New Issue
Block a user