import json with open('C:/Users/fam1n/projects/ajarbot/bfk_workflow.json', 'r', encoding='utf-8') as f: wf = json.load(f) for node in wf['nodes']: if node['name'] == 'AI Generate Hooks': # Fix: Use JSON.stringify() to properly escape the transcript text # so special characters (quotes, newlines) don't break the JSON body. # .slice(1,-1) removes the outer quotes that JSON.stringify adds. new_body = '={{ JSON.stringify({\n' \ ' "contents": [{\n' \ ' "parts": [{\n' \ ' "text": "You are a TikTok content expert for a blended family cooking channel called BlendedFamilyKitchen. Given this transcript from a cooking video, generate:\\n1) Three hook text options (max 8 words each, attention-grabbing, food-focused)\\n2) A caption/description with relevant hashtags (mix of popular and niche)\\n3) Three title options\\n\\nFormat your response as JSON with keys: hooks (array of 3 strings), caption (string), titles (array of 3 strings).\\n\\nTranscript: " + ($("Whisper Transcribe").item.json.data || $("Whisper Transcribe").item.json.text || "No transcript available")\n' \ ' }]\n' \ ' }],\n' \ ' "generationConfig": {\n' \ ' "temperature": 0.8,\n' \ ' "maxOutputTokens": 500,\n' \ ' "responseMimeType": "application/json"\n' \ ' }\n' \ '}) }}' node['parameters']['jsonBody'] = new_body # Also need to change contentType to 'raw' and set rawContentType # Actually the better approach: set specifyBody to 'string' mode # But simplest: use the expression-based JSON approach with JSON.stringify # wrapping the whole object so n8n sends it as a properly escaped JSON string # Actually, the cleanest n8n fix: keep specifyBody as "json" but use # the expression wrapper approach node['parameters']['specifyBody'] = 'string' node['parameters']['body'] = new_body del node['parameters']['jsonBody'] # Hmm, let me reconsider. The "json" mode with jsonBody expects valid JSON. # The issue is the expression injects raw text. JSON.stringify wrapping the # whole thing as an expression should work. # Actually simplest: keep specifyBody=json, but wrap entire object in JSON.stringify # n8n will parse the expression result. If expression returns a string (from JSON.stringify), # n8n's json mode should send it as-is. # # But wait - the error says "JSON parameter needs to be valid JSON" # This means n8n validates the jsonBody template BEFORE expression evaluation. # The raw transcript chars break the JSON template structure. # # Real fix: use specifyBody = "json" but build the body using n8n's # built-in JSON key-value pairs, not a raw string. Or use a Code node. # # Cleanest approach: Insert a Set node before this one that builds the prompt # text safely, then reference it. But that changes workflow structure. # # Simplest working fix: keep raw JSON mode but use JSON.stringify on the # transcript portion only, and use proper escaping. # Let me just do it right: node['parameters'] = { "method": "POST", "url": "=https://generativelanguage.googleapis.com/v1beta/models/gemini-2.5-flash:generateContent?key=AIzaSyDQPi9kXbVpxW790RBuUCeC8t_UgyxX7m4", "sendHeaders": True, "headerParameters": { "parameters": [ {"name": "Content-Type", "value": "application/json"} ] }, "sendBody": True, "specifyBody": "json", "jsonBody": "={{ JSON.stringify({ contents: [{ parts: [{ text: \"You are a TikTok content expert for a blended family cooking channel called BlendedFamilyKitchen. Given this transcript from a cooking video, generate:\\n1) Three hook text options (max 8 words each, attention-grabbing, food-focused)\\n2) A caption/description with relevant hashtags (mix of popular and niche)\\n3) Three title options\\n\\nFormat your response as JSON with keys: hooks (array of 3 strings), caption (string), titles (array of 3 strings).\\n\\nTranscript: \" + String($json.srt_content || $json.text || $('Whisper Transcribe').item.json.data || $('Whisper Transcribe').item.json.text || 'No transcript available') }] }], generationConfig: { temperature: 0.8, maxOutputTokens: 500, responseMimeType: \"application/json\" } }) }}", "options": {} } print("Updated AI Generate Hooks node parameters") print(f"New jsonBody: {node['parameters']['jsonBody'][:200]}...") break with open('C:/Users/fam1n/projects/ajarbot/bfk_workflow_fixed.json', 'w', encoding='utf-8') as f: json.dump(wf, f) print("\nSaved fixed workflow to bfk_workflow_fixed.json")