import { IExecuteFunctions, INodeExecutionData, INodeType, INodeTypeDescription, NodeConnectionType, IDataObject } from 'n8n-workflow'; export class ClaudeVision implements INodeType { description: INodeTypeDescription = { displayName: 'Claude Vision', name: 'claudeVision', icon: 'file:claude_icon.svg', group: ['transform'], version: 1, description: 'Send images to Claude for analysis', defaults: { name: 'Claude Vision', }, inputs: [NodeConnectionType.Main], outputs: [NodeConnectionType.Main], credentials: [ { name: 'claudeApi', required: true, }, ], properties: [ { displayName: 'Model', name: 'model', type: 'options', default: 'claude-sonnet-4-20250514', required: true, description: 'Select a Claude model to use', options: [ { name: 'Claude 3 Opus', value: 'claude-3-opus-20240229', }, { name: 'Claude 3 Sonnet', value: 'claude-3-sonnet-20240229', }, { name: 'Claude Sonnet 4 20250514', value: 'claude-sonnet-4-20250514', }, { name: 'Claude Haiku 3-5 Latest', value: 'claude-3-5-haiku-latest', }, { name: 'Claude Haiku 4 20250514', value: 'claude-haiku-4-20250514', }, ], }, { displayName: 'System Prompt', name: 'systemPrompt', type: 'string', default: '', placeholder: 'Respond only in Spanish.', description: 'System prompt to send with the request', }, { displayName: 'User Prompt', name: 'userPrompt', type: 'string', default: '', required: true, placeholder: 'Describe what you see in these images', description: 'The question or instruction to send to Claude about the images', }, { displayName: 'Image URLs', name: 'imageUrls', type: 'string', default: '', placeholder: 'https://example.com/1.png, https://example.com/2.png', description: 'Comma separated list of image URLs', }, ], }; async execute(this: IExecuteFunctions): Promise { const credentials = await this.getCredentials('claudeApi'); const token = (credentials as IDataObject).apiToken as string; const model = this.getNodeParameter('model', 0) as string; const systemPrompt = this.getNodeParameter('systemPrompt', 0) as string; const userPrompt = this.getNodeParameter('userPrompt', 0) as string; const urlsString = this.getNodeParameter('imageUrls', 0) as string; const urls = urlsString.split(',').map(u => u.trim()).filter(u => !!u); const content = urls.flatMap((url, index) => [ { type: 'text', text: `Image ${index + 1}:`, }, { type: 'image', source: { type: 'url', url, }, }, ]); // Użycie user prompt z parametru zamiast na sztywno content.push({ type: 'text', text: userPrompt }); const body = { model, max_tokens: 1024, system: systemPrompt, messages: [ { role: 'user', content, }, ], } as IDataObject; const options = { method: 'POST' as const, body, headers: { 'Content-Type': 'application/json', 'x-api-key': token, 'anthropic-version': '2023-06-01', }, url: 'https://api.anthropic.com/v1/messages', json: true, }; const response = await this.helpers.httpRequest(options); return [[{ json: response }]]; } }