fix(04): add Prepare Claude Request node for robust API body
Split Claude API call into two nodes: - Prepare Claude Request: Code node that builds the request body - Claude Intent Parser: HTTP Request node that sends the request This fixes the 'model: Field Required' error caused by complex expression evaluation issues in the HTTP Request node's jsonBody. Also updated Parse Intent to get original message from the new node. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
+29
-5
@@ -2066,6 +2066,19 @@
|
|||||||
600
|
600
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"parameters": {
|
||||||
|
"jsCode": "// Build Claude API request body\nconst userMessage = $json.message?.text || '';\n\nconst systemPrompt = `You are a Docker container management assistant. Parse user requests and return ONLY valid JSON.\n\nValid actions:\n- view_logs: User wants to see container logs\n- query_stats: User asks about resource usage (memory, CPU)\n- container_action: User wants to start/stop/restart/update a container\n- container_status: User asks about container status\n- list_containers: User wants to see all containers\n- unknown: Cannot determine intent\n\nRespond with JSON: {\"action\": \"<action>\", \"container\": \"<name or null>\", \"parameters\": {}}\n\nExamples:\n- \"show me plex logs\" -> {\"action\": \"view_logs\", \"container\": \"plex\", \"parameters\": {\"lines\": 50}}\n- \"what's using the most memory?\" -> {\"action\": \"query_stats\", \"container\": null, \"parameters\": {\"metric\": \"memory\", \"sort\": \"desc\"}}\n- \"restart nginx\" -> {\"action\": \"container_action\", \"container\": \"nginx\", \"parameters\": {\"action\": \"restart\"}}\n- \"how's sonarr doing?\" -> {\"action\": \"container_status\", \"container\": \"sonarr\", \"parameters\": {}}\n- \"hello\" -> {\"action\": \"unknown\", \"container\": null, \"parameters\": {\"message\": \"I can help with Docker containers. Try: 'show logs', 'restart plex', or 'what's using memory?'\"}}`;\n\nconst body = {\n model: 'claude-sonnet-4-5-20250929',\n max_tokens: 256,\n system: [{\n type: 'text',\n text: systemPrompt,\n cache_control: { type: 'ephemeral' }\n }],\n messages: [{\n role: 'user',\n content: userMessage\n }]\n};\n\nreturn {\n claudeBody: JSON.stringify(body),\n message: $json.message\n};"
|
||||||
|
},
|
||||||
|
"id": "code-prepare-claude-body",
|
||||||
|
"name": "Prepare Claude Request",
|
||||||
|
"type": "n8n-nodes-base.code",
|
||||||
|
"typeVersion": 2,
|
||||||
|
"position": [
|
||||||
|
900,
|
||||||
|
300
|
||||||
|
]
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"parameters": {
|
"parameters": {
|
||||||
"method": "POST",
|
"method": "POST",
|
||||||
@@ -2082,8 +2095,8 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
"sendBody": true,
|
"sendBody": true,
|
||||||
"contentType": "json",
|
"specifyBody": "json",
|
||||||
"jsonBody": "={{ JSON.stringify({\n \"model\": \"claude-sonnet-4-5-20250929\",\n \"max_tokens\": 256,\n \"system\": [{\n \"type\": \"text\",\n \"text\": \"You are a Docker container management assistant. Parse user requests and return ONLY valid JSON.\\n\\nValid actions:\\n- view_logs: User wants to see container logs\\n- query_stats: User asks about resource usage (memory, CPU)\\n- container_action: User wants to start/stop/restart/update a container\\n- container_status: User asks about container status\\n- list_containers: User wants to see all containers\\n- unknown: Cannot determine intent\\n\\nRespond with JSON: {\\\"action\\\": \\\"<action>\\\", \\\"container\\\": \\\"<name or null>\\\", \\\"parameters\\\": {}}\\n\\nExamples:\\n- \\\"show me plex logs\\\" -> {\\\"action\\\": \\\"view_logs\\\", \\\"container\\\": \\\"plex\\\", \\\"parameters\\\": {\\\"lines\\\": 50}}\\n- \\\"what's using the most memory?\\\" -> {\\\"action\\\": \\\"query_stats\\\", \\\"container\\\": null, \\\"parameters\\\": {\\\"metric\\\": \\\"memory\\\", \\\"sort\\\": \\\"desc\\\"}}\\n- \\\"restart nginx\\\" -> {\\\"action\\\": \\\"container_action\\\", \\\"container\\\": \\\"nginx\\\", \\\"parameters\\\": {\\\"action\\\": \\\"restart\\\"}}\\n- \\\"how's sonarr doing?\\\" -> {\\\"action\\\": \\\"container_status\\\", \\\"container\\\": \\\"sonarr\\\", \\\"parameters\\\": {}}\\n- \\\"hello\\\" -> {\\\"action\\\": \\\"unknown\\\", \\\"container\\\": null, \\\"parameters\\\": {\\\"message\\\": \\\"I can help with Docker containers. Try: 'show logs', 'restart plex', or 'what's using memory?'\\\"}}\",\n \"cache_control\": {\"type\": \"ephemeral\"}\n }],\n \"messages\": [{\n \"role\": \"user\",\n \"content\": $json.message.text\n }]\n}) }}",
|
"jsonBody": "={{ $json.claudeBody }}",
|
||||||
"options": {
|
"options": {
|
||||||
"timeout": 30000,
|
"timeout": 30000,
|
||||||
"retry": {
|
"retry": {
|
||||||
@@ -2097,7 +2110,7 @@
|
|||||||
"type": "n8n-nodes-base.httpRequest",
|
"type": "n8n-nodes-base.httpRequest",
|
||||||
"typeVersion": 4.2,
|
"typeVersion": 4.2,
|
||||||
"position": [
|
"position": [
|
||||||
900,
|
1010,
|
||||||
300
|
300
|
||||||
],
|
],
|
||||||
"credentials": {
|
"credentials": {
|
||||||
@@ -2109,7 +2122,7 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"parameters": {
|
"parameters": {
|
||||||
"jsCode": "// Parse and validate Claude's intent response\nconst response = $input.item.json;\n\n// Claude response structure: { content: [{ type: \"text\", text: \"...\" }] }\nlet intentText = '';\ntry {\n intentText = response.content[0].text;\n} catch (e) {\n return {\n action: 'error',\n error: 'Invalid Claude response structure',\n raw: JSON.stringify(response)\n };\n}\n\n// Parse JSON from Claude's response\nlet intent;\ntry {\n // Claude might wrap JSON in markdown code blocks, strip them\n const cleaned = intentText.replace(/```json\\n?/g, '').replace(/```\\n?/g, '').trim();\n intent = JSON.parse(cleaned);\n} catch (e) {\n return {\n action: 'error',\n error: 'Could not parse intent JSON',\n raw: intentText\n };\n}\n\n// Validate required fields\nconst validActions = ['view_logs', 'query_stats', 'container_action', 'container_status', 'list_containers', 'unknown'];\nif (!intent.action || !validActions.includes(intent.action)) {\n return {\n action: 'unknown',\n error: 'Invalid or missing action',\n parameters: { message: 'I didn\\'t understand that. Try: \"show logs plex\" or \"restart nginx\"' }\n };\n}\n\n// Normalize container name if present\nif (intent.container) {\n intent.container = intent.container.toLowerCase().trim();\n}\n\n// Set defaults for parameters\nintent.parameters = intent.parameters || {};\n\n// Preserve original message for fallback\nintent.original_message = $input.item.json.message || {};\n\nreturn intent;"
|
"jsCode": "// Parse and validate Claude's intent response\nconst response = $input.item.json;\n\n// Claude response structure: { content: [{ type: \"text\", text: \"...\" }] }\nlet intentText = '';\ntry {\n intentText = response.content[0].text;\n} catch (e) {\n return {\n action: 'error',\n error: 'Invalid Claude response structure',\n raw: JSON.stringify(response)\n };\n}\n\n// Parse JSON from Claude's response\nlet intent;\ntry {\n // Claude might wrap JSON in markdown code blocks, strip them\n const cleaned = intentText.replace(/```json\\n?/g, '').replace(/```\\n?/g, '').trim();\n intent = JSON.parse(cleaned);\n} catch (e) {\n return {\n action: 'error',\n error: 'Could not parse intent JSON',\n raw: intentText\n };\n}\n\n// Validate required fields\nconst validActions = ['view_logs', 'query_stats', 'container_action', 'container_status', 'list_containers', 'unknown'];\nif (!intent.action || !validActions.includes(intent.action)) {\n return {\n action: 'unknown',\n error: 'Invalid or missing action',\n parameters: { message: 'I didn\\'t understand that. Try: \"show logs plex\" or \"restart nginx\"' }\n };\n}\n\n// Normalize container name if present\nif (intent.container) {\n intent.container = intent.container.toLowerCase().trim();\n}\n\n// Set defaults for parameters\nintent.parameters = intent.parameters || {};\n\n// Preserve original message for fallback - get from Prepare Claude Request node\nintent.original_message = $('Prepare Claude Request').item.json.message || {};\n\nreturn intent;"
|
||||||
},
|
},
|
||||||
"id": "code-parse-intent",
|
"id": "code-parse-intent",
|
||||||
"name": "Parse Intent",
|
"name": "Parse Intent",
|
||||||
@@ -2411,7 +2424,7 @@
|
|||||||
"main": [
|
"main": [
|
||||||
[
|
[
|
||||||
{
|
{
|
||||||
"node": "Claude Intent Parser",
|
"node": "Prepare Claude Request",
|
||||||
"type": "main",
|
"type": "main",
|
||||||
"index": 0
|
"index": 0
|
||||||
}
|
}
|
||||||
@@ -2419,6 +2432,17 @@
|
|||||||
[]
|
[]
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
"Prepare Claude Request": {
|
||||||
|
"main": [
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"node": "Claude Intent Parser",
|
||||||
|
"type": "main",
|
||||||
|
"index": 0
|
||||||
|
}
|
||||||
|
]
|
||||||
|
]
|
||||||
|
},
|
||||||
"IF Callback Authenticated": {
|
"IF Callback Authenticated": {
|
||||||
"main": [
|
"main": [
|
||||||
[
|
[
|
||||||
|
|||||||
Reference in New Issue
Block a user