mirror of
https://github.com/n8n-io/n8n.git
synced 2026-09-24 23:22:38 +08:00
feat: Use Sonnet 4.5 for AI Workflow Builder (no-changelog) (#20351)
This commit is contained in:
@@ -4,7 +4,7 @@ import { MemorySaver } from '@langchain/langgraph';
|
||||
import { Client } from 'langsmith/client';
|
||||
import type { INodeTypeDescription } from 'n8n-workflow';
|
||||
|
||||
import { anthropicClaudeSonnet4 } from '../../src/llm-config.js';
|
||||
import { anthropicClaudeSonnet45 } from '../../src/llm-config.js';
|
||||
import { WorkflowBuilderAgent } from '../../src/workflow-builder-agent.js';
|
||||
import { loadNodesFromFile } from '../load-nodes.js';
|
||||
|
||||
@@ -25,7 +25,7 @@ export async function setupLLM(): Promise<BaseChatModel> {
|
||||
if (!apiKey) {
|
||||
throw new Error('N8N_AI_ANTHROPIC_KEY environment variable is required');
|
||||
}
|
||||
return await anthropicClaudeSonnet4({ apiKey });
|
||||
return await anthropicClaudeSonnet45({ apiKey });
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -7,7 +7,7 @@ import type { INodeTypeDescription } from 'n8n-workflow';
|
||||
import { join } from 'path';
|
||||
import pc from 'picocolors';
|
||||
|
||||
import { anthropicClaudeSonnet4 } from '../../src/llm-config';
|
||||
import { anthropicClaudeSonnet45 } from '../../src/llm-config';
|
||||
import type { ChatPayload } from '../../src/workflow-builder-agent';
|
||||
import { WorkflowBuilderAgent } from '../../src/workflow-builder-agent';
|
||||
import type { Violation } from '../types/evaluation';
|
||||
@@ -23,7 +23,7 @@ export async function setupLLM(): Promise<BaseChatModel> {
|
||||
if (!apiKey) {
|
||||
throw new Error('N8N_AI_ANTHROPIC_KEY environment variable is required');
|
||||
}
|
||||
return await anthropicClaudeSonnet4({ apiKey });
|
||||
return await anthropicClaudeSonnet45({ apiKey });
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -9,7 +9,7 @@ import { INodeTypes } from 'n8n-workflow';
|
||||
import type { IUser, INodeTypeDescription } from 'n8n-workflow';
|
||||
|
||||
import { LLMServiceError } from '@/errors';
|
||||
import { anthropicClaudeSonnet4 } from '@/llm-config';
|
||||
import { anthropicClaudeSonnet45 } from '@/llm-config';
|
||||
import { SessionManagerService } from '@/session-manager.service';
|
||||
import { WorkflowBuilderAgent, type ChatPayload } from '@/workflow-builder-agent';
|
||||
|
||||
@@ -41,7 +41,7 @@ export class AiWorkflowBuilderService {
|
||||
authHeaders?: Record<string, string>;
|
||||
apiKey?: string;
|
||||
} = {}): Promise<ChatAnthropic> {
|
||||
return await anthropicClaudeSonnet4({
|
||||
return await anthropicClaudeSonnet45({
|
||||
baseUrl,
|
||||
apiKey,
|
||||
headers: {
|
||||
|
||||
@@ -47,10 +47,10 @@ export const gpt41 = async (config: LLMProviderConfig) => {
|
||||
});
|
||||
};
|
||||
|
||||
export const anthropicClaudeSonnet4 = async (config: LLMProviderConfig) => {
|
||||
export const anthropicClaudeSonnet45 = async (config: LLMProviderConfig) => {
|
||||
const { ChatAnthropic } = await import('@langchain/anthropic');
|
||||
return new ChatAnthropic({
|
||||
model: 'claude-sonnet-4-20250514',
|
||||
const model = new ChatAnthropic({
|
||||
model: 'claude-sonnet-4-5',
|
||||
apiKey: config.apiKey,
|
||||
temperature: 0,
|
||||
maxTokens: MAX_OUTPUT_TOKENS,
|
||||
@@ -59,4 +59,9 @@ export const anthropicClaudeSonnet4 = async (config: LLMProviderConfig) => {
|
||||
defaultHeaders: config.headers,
|
||||
},
|
||||
});
|
||||
|
||||
// Remove Langchain default topP parameter since Sonnet 4.5 doesn't allow setting both temperature and topP
|
||||
delete model.topP;
|
||||
|
||||
return model;
|
||||
};
|
||||
|
||||
+8
-8
@@ -8,7 +8,7 @@ import type { IUser, INodeTypes, INodeTypeDescription } from 'n8n-workflow';
|
||||
|
||||
import { AiWorkflowBuilderService } from '@/ai-workflow-builder-agent.service';
|
||||
import { LLMServiceError } from '@/errors';
|
||||
import { anthropicClaudeSonnet4 } from '@/llm-config';
|
||||
import { anthropicClaudeSonnet45 } from '@/llm-config';
|
||||
import { SessionManagerService } from '@/session-manager.service';
|
||||
import { formatMessages } from '@/utils/stream-processor';
|
||||
import { WorkflowBuilderAgent, type ChatPayload } from '@/workflow-builder-agent';
|
||||
@@ -20,7 +20,7 @@ jest.mock('langsmith');
|
||||
jest.mock('@/workflow-builder-agent');
|
||||
jest.mock('@/session-manager.service');
|
||||
jest.mock('@/llm-config', () => ({
|
||||
anthropicClaudeSonnet4: jest.fn(),
|
||||
anthropicClaudeSonnet45: jest.fn(),
|
||||
}));
|
||||
jest.mock('@/utils/stream-processor', () => ({
|
||||
formatMessages: jest.fn(),
|
||||
@@ -36,8 +36,8 @@ const MockedSessionManagerService = SessionManagerService as jest.MockedClass<
|
||||
typeof SessionManagerService
|
||||
>;
|
||||
|
||||
const anthropicClaudeSonnet4Mock = anthropicClaudeSonnet4 as jest.MockedFunction<
|
||||
typeof anthropicClaudeSonnet4
|
||||
const anthropicClaudeSonnet45Mock = anthropicClaudeSonnet45 as jest.MockedFunction<
|
||||
typeof anthropicClaudeSonnet45
|
||||
>;
|
||||
const formatMessagesMock = formatMessages as jest.MockedFunction<typeof formatMessages>;
|
||||
|
||||
@@ -157,7 +157,7 @@ describe('AiWorkflowBuilderService', () => {
|
||||
return mockAgent;
|
||||
});
|
||||
|
||||
anthropicClaudeSonnet4Mock.mockResolvedValue(mockChatAnthropic);
|
||||
anthropicClaudeSonnet45Mock.mockResolvedValue(mockChatAnthropic);
|
||||
|
||||
// Mock onCreditsUpdated callback
|
||||
mockOnCreditsUpdated = jest.fn();
|
||||
@@ -441,7 +441,7 @@ describe('AiWorkflowBuilderService', () => {
|
||||
|
||||
it('should throw LLMServiceError when model setup fails', async () => {
|
||||
const testError = new Error('Model setup failed');
|
||||
anthropicClaudeSonnet4Mock.mockRejectedValue(testError);
|
||||
anthropicClaudeSonnet45Mock.mockRejectedValue(testError);
|
||||
|
||||
const generator = service.chat(mockPayload, mockUser);
|
||||
|
||||
@@ -450,7 +450,7 @@ describe('AiWorkflowBuilderService', () => {
|
||||
|
||||
it('should include error details in LLMServiceError', async () => {
|
||||
const testError = new Error('Specific error message');
|
||||
anthropicClaudeSonnet4Mock.mockRejectedValue(testError);
|
||||
anthropicClaudeSonnet45Mock.mockRejectedValue(testError);
|
||||
|
||||
const generator = service.chat(mockPayload, mockUser);
|
||||
|
||||
@@ -473,7 +473,7 @@ describe('AiWorkflowBuilderService', () => {
|
||||
const generator = serviceWithoutClient.chat(mockPayload, mockUser);
|
||||
await generator.next();
|
||||
|
||||
expect(anthropicClaudeSonnet4Mock).toHaveBeenCalledWith({
|
||||
expect(anthropicClaudeSonnet45Mock).toHaveBeenCalledWith({
|
||||
baseUrl: undefined,
|
||||
apiKey: 'test-env-key',
|
||||
headers: {
|
||||
|
||||
Reference in New Issue
Block a user