mirror of
https://github.com/lobehub/lobe-chat.git
synced 2026-06-16 12:36:07 +00:00
💄 style: support openrouter claude 3.7 sonnet reasoning (#6806)
* feat: openrouter reasoning * test: add test
This commit is contained in:
@@ -137,6 +137,31 @@ const openrouterChatModels: AIChatModelCard[] = [
|
||||
releasedAt: '2024-06-20',
|
||||
type: 'chat',
|
||||
},
|
||||
{
|
||||
abilities: {
|
||||
functionCall: true,
|
||||
reasoning: true,
|
||||
vision: true,
|
||||
},
|
||||
contextWindowTokens: 200_000,
|
||||
description:
|
||||
'Claude 3.7 Sonnet 是 Anthropic 迄今为止最智能的模型,也是市场上首个混合推理模型。Claude 3.7 Sonnet 可以产生近乎即时的响应或延长的逐步思考,用户可以清晰地看到这些过程。Sonnet 特别擅长编程、数据科学、视觉处理、代理任务。',
|
||||
displayName: 'Claude 3.7 Sonnet',
|
||||
enabled: true,
|
||||
id: 'anthropic/claude-3.7-sonnet',
|
||||
maxOutput: 8192,
|
||||
pricing: {
|
||||
cachedInput: 0.3,
|
||||
input: 3,
|
||||
output: 15,
|
||||
writeCacheInput: 3.75,
|
||||
},
|
||||
releasedAt: '2025-02-24',
|
||||
settings: {
|
||||
extendParams: ['enableReasoning', 'reasoningBudgetToken'],
|
||||
},
|
||||
type: 'chat',
|
||||
},
|
||||
{
|
||||
abilities: {
|
||||
functionCall: true,
|
||||
@@ -258,7 +283,7 @@ const openrouterChatModels: AIChatModelCard[] = [
|
||||
id: 'deepseek/deepseek-r1:free',
|
||||
releasedAt: '2025-01-20',
|
||||
type: 'chat',
|
||||
},
|
||||
},
|
||||
{
|
||||
abilities: {
|
||||
vision: true,
|
||||
|
||||
@@ -92,6 +92,39 @@ describe('LobeOpenRouterAI', () => {
|
||||
expect(result).toBeInstanceOf(Response);
|
||||
});
|
||||
|
||||
it('should add reasoning field when thinking is enabled', async () => {
|
||||
// Arrange
|
||||
const mockStream = new ReadableStream();
|
||||
const mockResponse = Promise.resolve(mockStream);
|
||||
|
||||
(instance['client'].chat.completions.create as Mock).mockResolvedValue(mockResponse);
|
||||
|
||||
// Act
|
||||
const result = await instance.chat({
|
||||
messages: [{ content: 'Hello', role: 'user' }],
|
||||
model: 'mistralai/mistral-7b-instruct:free',
|
||||
temperature: 0.7,
|
||||
thinking: {
|
||||
type: 'enabled',
|
||||
budget_tokens: 1500,
|
||||
},
|
||||
});
|
||||
|
||||
// Assert
|
||||
expect(instance['client'].chat.completions.create).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
messages: [{ content: 'Hello', role: 'user' }],
|
||||
model: 'mistralai/mistral-7b-instruct:free',
|
||||
reasoning: {
|
||||
max_tokens: 1500,
|
||||
},
|
||||
temperature: 0.7,
|
||||
}),
|
||||
{ headers: { Accept: '*/*' } },
|
||||
);
|
||||
expect(result).toBeInstanceOf(Response);
|
||||
});
|
||||
|
||||
describe('Error', () => {
|
||||
it('should return OpenRouterBizError with an openai error response when OpenAI.APIError is thrown', async () => {
|
||||
// Arrange
|
||||
|
||||
@@ -2,7 +2,7 @@ import type { ChatModelCard } from '@/types/llm';
|
||||
|
||||
import { ModelProvider } from '../types';
|
||||
import { LobeOpenAICompatibleFactory } from '../utils/openaiCompatibleFactory';
|
||||
import { OpenRouterModelCard, OpenRouterModelExtraInfo } from './type';
|
||||
import { OpenRouterModelCard, OpenRouterModelExtraInfo, OpenRouterReasoning } from './type';
|
||||
|
||||
const formatPrice = (price: string) => {
|
||||
if (price === '-1') return undefined;
|
||||
@@ -13,10 +13,19 @@ export const LobeOpenRouterAI = LobeOpenAICompatibleFactory({
|
||||
baseURL: 'https://openrouter.ai/api/v1',
|
||||
chatCompletion: {
|
||||
handlePayload: (payload) => {
|
||||
const { thinking } = payload;
|
||||
|
||||
let reasoning: OpenRouterReasoning = {};
|
||||
if (thinking?.type === 'enabled') {
|
||||
reasoning = {
|
||||
max_tokens: thinking.budget_tokens,
|
||||
};
|
||||
}
|
||||
|
||||
return {
|
||||
...payload,
|
||||
include_reasoning: true,
|
||||
model: payload.enabledSearch ? `${payload.model}:online` : payload.model,
|
||||
reasoning,
|
||||
stream: payload.stream ?? true,
|
||||
} as any;
|
||||
},
|
||||
|
||||
@@ -37,3 +37,22 @@ export interface OpenRouterModelExtraInfo {
|
||||
endpoint?: OpenRouterModelEndpoint;
|
||||
slug: string;
|
||||
}
|
||||
|
||||
interface OpenRouterOpenAIReasoning {
|
||||
effort: 'high' | 'medium' | 'low';
|
||||
exclude?: boolean;
|
||||
}
|
||||
|
||||
interface OpenRouterAnthropicReasoning {
|
||||
exclude?: boolean;
|
||||
max_tokens: number;
|
||||
}
|
||||
|
||||
interface OpenRouterCommonReasoning {
|
||||
exclude?: boolean;
|
||||
}
|
||||
|
||||
export type OpenRouterReasoning =
|
||||
| OpenRouterOpenAIReasoning
|
||||
| OpenRouterAnthropicReasoning
|
||||
| OpenRouterCommonReasoning;
|
||||
|
||||
Reference in New Issue
Block a user