557 lines
26 KiB
JSON
557 lines
26 KiB
JSON
{
|
||
"id": "uyqbbMRpaHAQtWUP",
|
||
"name": "Character Parser v2",
|
||
"nodes": [
|
||
{
|
||
"parameters": {
|
||
"formTitle": "Chub.ai Character Parsing",
|
||
"formFields": {
|
||
"values": [
|
||
{
|
||
"fieldLabel": "json file",
|
||
"fieldType": "file",
|
||
"multipleFiles": false,
|
||
"acceptFileTypes": "json",
|
||
"requiredField": true
|
||
}
|
||
]
|
||
},
|
||
"options": {}
|
||
},
|
||
"type": "n8n-nodes-base.formTrigger",
|
||
"typeVersion": 2.3,
|
||
"position": [
|
||
224,
|
||
-288
|
||
],
|
||
"id": "e1f235b9-8b5a-47b7-8908-188952fc877e",
|
||
"name": "On form submission",
|
||
"webhookId": "facf5339-c5ee-417e-b2f7-a8e8a2d04ac7"
|
||
},
|
||
{
|
||
"parameters": {},
|
||
"type": "n8n-nodes-base.function",
|
||
"typeVersion": 1,
|
||
"position": [
|
||
592,
|
||
-288
|
||
],
|
||
"id": "81ab6435-8053-4cf4-a6f9-26597f86f0ec",
|
||
"name": "Validate Upload"
|
||
},
|
||
{
|
||
"parameters": {
|
||
"operation": "fromJson",
|
||
"binaryPropertyName": "json_file",
|
||
"options": {}
|
||
},
|
||
"type": "n8n-nodes-base.extractFromFile",
|
||
"typeVersion": 1,
|
||
"position": [
|
||
784,
|
||
-288
|
||
],
|
||
"id": "4f5f4bf2-2a10-46e7-b224-0b95539986fe",
|
||
"name": "Extract from File"
|
||
},
|
||
{
|
||
"parameters": {
|
||
"functionCode": "// Extract character data for LLM processing\nconst characterData = items[0].json.data.data;\n\n// Create structured prompt for LLM\nconst prompt = `You are tasked with extracting character information from a character card and structuring it into a detailed profile format. Parse the following character data and extract specific information:\n\nCharacter Data:\n${JSON.stringify(characterData, null, 2)}\n\nExtract and format the following fields. If information is not explicitly stated, use logical inference from the description. If completely unavailable, use \"Unknown\":\n\n1. Character_Name: The character's full name\n2. Character_Age: Character's age (number or \"Unknown\")\n3. Character_Sex: Male/Female/Other\n4. Character_Ethnicity: Race/ethnicity\n5. Character_Role: Their role in the story\n6. Character_Appearance: Physical description\n7. Core_Traits: Main personality traits (comma separated)\n8. Private_Side: Hidden or less obvious traits\n9. Goal_1: Primary goal or desire\n10. Goal_2: Secondary goal\n11. Cue_1: First emotional cue/mannerism\n12. Cue_2: Second emotional cue/mannerism\n13. Speech_Tone: How they speak\n14. Verbal_Quirks: Unique speech patterns\n15. Background_Point_1: First background detail\n16. Background_Point_2: Second background detail\n17. Background_Point_3: Third background detail\n18. User_Name: \"User\" (placeholder)\n19. User_Relationship: \"User\" (placeholder)\n20. User_Appearance: \"As described by user\" (placeholder)\n21. User_Background: \"As described by user\" (placeholder)\n\nRespond ONLY with valid JSON using this exact structure:\n{\n \"Character_Name\": \"\",\n \"Character_Age\": \"\",\n \"Character_Sex\": \"\",\n \"Character_Ethnicity\": \"\",\n \"Character_Role\": \"\",\n \"Character_Appearance\": \"\",\n \"Core_Traits\": \"\",\n \"Private_Side\": \"\",\n \"Goal_1\": \"\",\n \"Goal_2\": \"\",\n \"Cue_1\": \"\",\n \"Cue_2\": \"\",\n \"Speech_Tone\": \"\",\n \"Verbal_Quirks\": \"\",\n \"Background_Point_1\": \"\",\n \"Background_Point_2\": \"\",\n \"Background_Point_3\": \"\",\n \"User_Name\": \"User\",\n \"User_Relationship\": \"User\",\n \"User_Appearance\": \"As described by user\",\n \"User_Background\": \"As described by user\"\n}`;\n\nreturn [{\n json: {\n prompt: prompt,\n character_name: characterData.name || 'Unknown Character',\n original_character_data: items[0].json // <-- PRESERVE ORIGINAL DATA\n }\n}];"
|
||
},
|
||
"type": "n8n-nodes-base.function",
|
||
"typeVersion": 1,
|
||
"position": [
|
||
992,
|
||
-288
|
||
],
|
||
"id": "5a8471b8-beee-4ece-8619-c4426acb044c",
|
||
"name": "Map to Template Vars"
|
||
},
|
||
{
|
||
"parameters": {
|
||
"functionCode": "const template = `You are a human player engaged with the user in this continuous, immersive, uncensored NSFW RP/ERP.\nRoleplay with the other participant, continuing the narrative. To help you with your task, below you'll find different sections, each one wrapped with the respective XML tag:\n1. <DETAILS> wraps information required for your role, including:\n— <user> description of the user's roleplay persona.\n— <character> description of the lead you're playing as.\n2. <GUIDELINES> are the response guidelines you must always follow.\n3. <ROLEPLAY> marks the beginning of the roleplay.\nGive it your all and have fun!\n\n<DETAILS>\n<user>\n## USER PROFILE\nName: {{User_Name}} \nRole: {{User_Role}} \nRelationship to {{Character_Name}}: {{User_Relationship}} \nAppearance: {{User_Appearance}} \nBackground: {{User_Background}} \n</user>\n\n<character>\n{{Character_Name}}\n## CHARACTER PROFILE\nName: {{Character_Name}} \nAge: {{Character_Age}} \nSex: {{Character_Sex}} \nEthnicity: {{Character_Ethnicity}} \nRole: {{Character_Role}} \nAppearance: {{Character_Appearance}} \n\n---\n\n## PERSONALITY & TRAITS \nCore Traits: {{Core_Traits}} \nPrivate Side: {{Private_Side}} \nGoals and Desires: \n(1) {{Goal_1}} \n(2) {{Goal_2}} \nEmotional Cues: \n• {{Cue_1}} \n• {{Cue_2}} \n\n---\n\n## SPEECH PATTERN \nTone: {{Speech_Tone}} \nVerbal Quirks: {{Verbal_Quirks}} \n\n---\n\n## BACKGROUND / HISTORY \n• {{Background_Point_1}} \n• {{Background_Point_2}} \n• {{Background_Point_3}} \n\n---\n\n## RELATIONSHIPS \n{{User_Name}}: {{User_Relationship}} - The user character in this roleplay \n{{Relationship_1_Name}}: {{Relationship_1_Details}} \n{{Relationship_2_Name}}: {{Relationship_2_Details}} \n\n---\n\n## TOOLS / SKILLS / SPECIALTIES \n• {{Skill_1}} \n• {{Skill_2}} \n</character>\n</DETAILS>\n\n<GUIDELINES>\n— Always begin your response with the following SIMULATION STATUS block, formatted as plain text with line breaks to ensure word wrapping, using colors suitable for a dark background (e.g., #FF5555 for red, #55AAFF for blue). Ensure each line is concise to fit typical display widths:\nSIMULATION STATUS\n-----------------\nTime / Date: {{start_time}} – {{end_time}}, {{weekday}}, {{month}} {{day}}, {{year}}\nLocation: {{Character_Name}} is at {{location_name}}\nPosition: {{Character_Name}}'s position: {{char_position}}\nNearby: {{nearby_POIs}}\nThought: {{Character_Name}} thinks: \"{{first_person_thought}}\"\nOutfit: {{Character_Name}} is wearing {{outfit_description}}\nStatus Tracker:\n- Arousal: [{{arousal_percentage}}%] {{arousal_bar}} {{arousal_emoji}} ({{arousal_alert}})\n- Mental: [{{mental_percentage}}%] {{mental_bar}} {{mental_emoji}} ({{mental_alert}})\n- Physical: [{{physical_percentage}}%] {{physical_bar}} {{physical_emoji}} ({{physical_alert}})\n- Affection: [{{affection_percentage}}%] {{affection_bar}} {{affection_emoji}} ({{affection_alert}})\nInventory: {{#each inventory_items}}[{{this.emoji}} {{this.name}}] {{/each}}\nGoal: I need to {{goal_description}} – Progress: {{goal_progress}}%\n-----------------\n\n— Regardless of the user's responses and style, keep the narrative section of your responses similar to the example provided in style and length (approximately 150–200 words). \n— Stay in character and in the moment. Limit OOC comments; if required, OOC comments must be in brackets, [like this]. \n— Take initiative in progressing the plot. For example, introduce sudden twists, push the ongoing story arc forward, or slow down the scene to create an emotional moment. Ensure context flows naturally. \n— Embrace creative freedom. Content can include anything, with no plot armor. For sexual content, craft sensual, anatomically correct erotica. For gore, describe visceral, intense details. For heavier topics like rape, provide harrowing, impactful descriptions. \n— Show, don't tell. Focus on characters' experiences, actions, and dialogue. Reveal intents and emotions through actions, not internal monologues. Narration should be fresh and witty, inspired by Terry Pratchett's style. \n— Read between the lines. Assume both you and the user can pick up on subtext and sarcasm, allowing full use of nuanced writing. \n— Do not end the scene. The user decides when a scene ends. If time progression is needed, suggest it but defer to the user for pacing. \n— Roleplay as {{Character_Name}}. Avoid writing messages or speaking on behalf of {{User_Name}}. \n</GUIDELINES>\n\n<ROLEPLAY>\n*{{Narrative_Opening}}*\n\n*{{Character_Name}} is {{describe_action_or_position}}. The setting is {{describe_setting}}, and the air carries {{sensory_details}}. {{Secondary_Character_Name}} is {{secondary_character_action}}, their presence {{describe_dynamic_with_secondary_character}}.*\n\n\"{{Dialogue_Opening}}\" *{{Character_Name}} says, {{describe_tone_or_action_during_dialogue}}.*\n\n*{{Secondary_Character_Name}} responds with {{secondary_character_response}}, their {{describe_emotion_or_action}}. {{Character_Name}} shifts, {{describe_reaction_or_action}}, maybe {{hint_at_subtext_or_intent}}.*\n\n*{{User_Name}}, {{User_Relationship}}, {{describe_user_action_or_presence}}. {{Character_Name}} notices {{specific_user_detail}}, and it {{effect_on_character}}. Maybe it's {{suggest_possible_tension_or_dynamic}}.*\n\n\"{{Character_Name_Dialogue}}\" *{{Character_Name}} says, {{describe_delivery_or_body_language}}.* \"{{Continue_Dialogue_or_Tease}}\"\n</ROLEPLAY>`;\n\n// Simpler replacement approach without complex regex\nconst vars = items[0].json.template_vars;\nlet filledTemplate = template;\n\n// Use simple string replace with global flag using split/join\nObject.keys(vars).forEach(key => {\n const placeholder = `{{${key}}}`;\n const value = vars[key] || 'Unknown';\n // Use split/join instead of regex to avoid escaping issues\n filledTemplate = filledTemplate.split(placeholder).join(value);\n});\n\nreturn [{\n json: {\n name: items[0].json.character_name,\n filled_template: filledTemplate,\n original_character_data: items[0].json.original_character_data, // Preserve original data\n greetings: items[0].json.greetings || [] // Pass greetings forward\n }\n}];"
|
||
},
|
||
"type": "n8n-nodes-base.function",
|
||
"typeVersion": 1,
|
||
"position": [
|
||
1776,
|
||
-288
|
||
],
|
||
"id": "744893df-2d92-4ec3-ba16-f34397c9efea",
|
||
"name": "Fill Template"
|
||
},
|
||
{
|
||
"parameters": {
|
||
"jsCode": "// Prepare greetings for Google Sheets with truncation\nconst MAX_CHARS = 49000;\nconst greetings = items[0].json.greetings || [];\nconst greetingsJson = JSON.stringify(greetings);\n\nlet greetingsForSheets;\n\nif (greetingsJson.length > MAX_CHARS) {\n // Truncate and add message\n greetingsForSheets = greetingsJson.substring(0, MAX_CHARS) + '... [TRUNCATED - See Supabase for full greetings]';\n console.log(`Greetings truncated from ${greetingsJson.length} to ${MAX_CHARS} characters`);\n} else {\n greetingsForSheets = greetingsJson;\n console.log(`Greetings size: ${greetingsJson.length} characters (under limit)`);\n}\n\nreturn [{\n json: {\n ...items[0].json,\n greetings_for_sheets: greetingsForSheets\n }\n}];"
|
||
},
|
||
"type": "n8n-nodes-base.code",
|
||
"typeVersion": 2,
|
||
"position": [
|
||
1936,
|
||
-288
|
||
],
|
||
"id": "130e3dcf-e6bd-4346-bcbd-81e0c9eff508",
|
||
"name": "Prepare for Sheets"
|
||
},
|
||
{
|
||
"parameters": {
|
||
"tableId": "character_ai",
|
||
"fieldsUi": {
|
||
"fieldValues": [
|
||
{
|
||
"fieldId": "name",
|
||
"fieldValue": "={{ $json.name }}"
|
||
},
|
||
{
|
||
"fieldId": "json_meta",
|
||
"fieldValue": "={{ JSON.stringify($('Extract from File').item.json.data) }}"
|
||
},
|
||
{
|
||
"fieldId": "template",
|
||
"fieldValue": "={{ $json.filled_template }}"
|
||
},
|
||
{
|
||
"fieldId": "greetings",
|
||
"fieldValue": "={{ JSON.stringify($json.greetings) }}"
|
||
}
|
||
]
|
||
}
|
||
},
|
||
"type": "n8n-nodes-base.supabase",
|
||
"typeVersion": 1,
|
||
"position": [
|
||
1984,
|
||
64
|
||
],
|
||
"id": "bc0cd41d-4a10-48ca-876a-e45c98719dac",
|
||
"name": "Create a row",
|
||
"credentials": {
|
||
"supabaseApi": {
|
||
"id": "lWyf2ikOGHTTwnSU",
|
||
"name": "Supabase account"
|
||
}
|
||
},
|
||
"onError": "continueRegularOutput"
|
||
},
|
||
{
|
||
"parameters": {
|
||
"model": {
|
||
"__rl": true,
|
||
"value": "zai-org/GLM-4.6-turbo",
|
||
"mode": "list",
|
||
"cachedResultName": "zai-org/GLM-4.6-turbo"
|
||
},
|
||
"options": {
|
||
"maxTokens": 5000
|
||
}
|
||
},
|
||
"type": "@n8n/n8n-nodes-langchain.lmChatOpenAi",
|
||
"typeVersion": 1.2,
|
||
"position": [
|
||
1184,
|
||
-64
|
||
],
|
||
"id": "b5bc1251-aea6-440c-b514-055bb667368d",
|
||
"name": "OpenAI Chat Model",
|
||
"credentials": {
|
||
"openAiApi": {
|
||
"id": "SItxcw7hDLHB7DYY",
|
||
"name": "Chutes AI"
|
||
}
|
||
}
|
||
},
|
||
{
|
||
"parameters": {
|
||
"jsCode": "// Try to parse the LLM response\ntry {\n // Handle different possible structures from the LLM node\n let llmResponse;\n\n if (items[0].json.output) {\n // If output is nested in 'output' field\n llmResponse = items[0].json.output;\n } else if (items[0].json.choices && items[0].json.choices[0] && items[0].json.choices[0].message) {\n // Standard OpenAI structure\n llmResponse = items[0].json.choices[0].message.content;\n } else {\n throw new Error(\"Unexpected LLM response structure\");\n }\n\n // Extract JSON from the response (it might have extra text)\n const jsonMatch = llmResponse.match(/```json\\s*([\\s\\S]*?)\\s*```/);\n let jsonString;\n\n if (jsonMatch) {\n // Extract JSON from code block\n jsonString = jsonMatch[1];\n } else {\n // Try to find a JSON object directly\n const objectMatch = llmResponse.match(/\\{[\\s\\S]*\\}/);\n if (objectMatch) {\n jsonString = objectMatch[0];\n } else {\n jsonString = llmResponse;\n }\n }\n\n // Check if JSON is complete\n if (!jsonString.trim().endsWith('}')) {\n throw new Error(\"JSON response appears to be incomplete - check token limits\");\n }\n\n const parsedData = JSON.parse(jsonString);\n\n // Validate required fields and fill missing ones\n const requiredFields = [\n 'Character_Name', 'Character_Age', 'Character_Sex', 'Character_Ethnicity',\n 'User_Name', 'User_Relationship', 'User_Appearance', 'User_Background'\n ];\n\n requiredFields.forEach(field => {\n if (!parsedData[field]) {\n parsedData[field] = field.startsWith('User_') ?\n (field === 'User_Name' ? 'User' : 'As described by user') : 'Unknown';\n }\n });\n\n // Extract greetings from the Extract from File node directly using $node\n const extractData = $('Extract from File').item.json.data;\n let characterData = null;\n \n if (extractData && extractData.data) {\n characterData = extractData.data;\n }\n \n const greetings = [];\n \n if (characterData) {\n // Add first_mes as the default greeting (index 0)\n if (characterData.first_mes) {\n greetings.push({\n index: 0,\n label: \"Default Greeting\",\n text: characterData.first_mes\n });\n }\n \n // Add alternate greetings if they exist\n if (characterData.alternate_greetings && Array.isArray(characterData.alternate_greetings)) {\n characterData.alternate_greetings.forEach((greeting, idx) => {\n greetings.push({\n index: idx + 1,\n label: `Greeting ${idx + 2}`,\n text: greeting\n });\n });\n }\n }\n\n return [{\n json: {\n ...items[0].json, // <-- PRESERVE ALL PREVIOUS DATA INCLUDING original_character_data\n template_vars: parsedData,\n character_name: parsedData.Character_Name || items[0].json.character_name || 'Unknown Character',\n greetings: greetings // <-- ADD GREETINGS DATA\n }\n }];\n\n} catch (error) {\n console.log('Error parsing LLM response:', error.message);\n console.log('Raw LLM Response length:', items[0].json.output?.length || 'N/A');\n console.log('Error details:', error);\n\n return [{\n json: {\n ...items[0].json, // <-- PRESERVE ALL DATA EVEN ON ERROR\n error: \"Failed to parse LLM response: \" + error.message,\n raw_response: items[0].json,\n greetings: [] // <-- EMPTY GREETINGS ON ERROR\n }\n }];\n}"
|
||
},
|
||
"type": "n8n-nodes-base.code",
|
||
"typeVersion": 2,
|
||
"position": [
|
||
1536,
|
||
-288
|
||
],
|
||
"id": "99af011b-914c-4416-9246-dddcdb34fd5c",
|
||
"name": "Parse LLM"
|
||
},
|
||
{
|
||
"parameters": {
|
||
"promptType": "define",
|
||
"text": "={{ $json.prompt }}",
|
||
"options": {}
|
||
},
|
||
"type": "@n8n/n8n-nodes-langchain.agent",
|
||
"typeVersion": 2.2,
|
||
"position": [
|
||
1184,
|
||
-288
|
||
],
|
||
"id": "6bb0afe6-46a0-44f6-8220-18b3289170c4",
|
||
"name": "AI Agent"
|
||
},
|
||
{
|
||
"parameters": {
|
||
"operation": "appendOrUpdate",
|
||
"documentId": {
|
||
"__rl": true,
|
||
"value": "17Bt2Jn6pMdpEkZkaxQKu71jpZFp1YNoI-JQTV0dRt9s",
|
||
"mode": "id"
|
||
},
|
||
"sheetName": {
|
||
"__rl": true,
|
||
"value": 512457501,
|
||
"mode": "list",
|
||
"cachedResultName": "Character_AI",
|
||
"cachedResultUrl": "https://docs.google.com/spreadsheets/d/17Bt2Jn6pMdpEkZkaxQKu71jpZFp1YNoI-JQTV0dRt9s/edit#gid=512457501"
|
||
},
|
||
"columns": {
|
||
"mappingMode": "defineBelow",
|
||
"value": {
|
||
"character_name": "={{ $json.name }}",
|
||
"json_meta": "={{ JSON.stringify($('Extract from File').item.json.data) }}",
|
||
"template": "={{ $json.filled_template }}",
|
||
"greetings": "={{ $json.greetings_for_sheets }}"
|
||
},
|
||
"matchingColumns": [
|
||
"character_name"
|
||
],
|
||
"schema": [
|
||
{
|
||
"id": "character_name",
|
||
"displayName": "character_name",
|
||
"required": false,
|
||
"defaultMatch": false,
|
||
"display": true,
|
||
"type": "string",
|
||
"canBeUsedToMatch": true,
|
||
"removed": false
|
||
},
|
||
{
|
||
"id": "template",
|
||
"displayName": "template",
|
||
"required": false,
|
||
"defaultMatch": false,
|
||
"display": true,
|
||
"type": "string",
|
||
"canBeUsedToMatch": true
|
||
},
|
||
{
|
||
"id": "json_meta",
|
||
"displayName": "json_meta",
|
||
"required": false,
|
||
"defaultMatch": false,
|
||
"display": true,
|
||
"type": "string",
|
||
"canBeUsedToMatch": true
|
||
},
|
||
{
|
||
"id": "greetings",
|
||
"displayName": "greetings",
|
||
"required": false,
|
||
"defaultMatch": false,
|
||
"display": true,
|
||
"type": "string",
|
||
"canBeUsedToMatch": true
|
||
}
|
||
],
|
||
"attemptToConvertTypes": false,
|
||
"convertFieldsToString": false
|
||
},
|
||
"options": {}
|
||
},
|
||
"type": "n8n-nodes-base.googleSheets",
|
||
"typeVersion": 4.7,
|
||
"position": [
|
||
2048,
|
||
-288
|
||
],
|
||
"id": "25117ad6-d5b8-4a71-8636-80cfb8318fd0",
|
||
"name": "Append or update row in sheet",
|
||
"credentials": {
|
||
"googleSheetsOAuth2Api": {
|
||
"id": "qQNqeSAtYrG6Vi5N",
|
||
"name": "Google Sheets account"
|
||
}
|
||
},
|
||
"continueOnFail": true
|
||
},
|
||
{
|
||
"parameters": {
|
||
"operation": "get",
|
||
"tableId": "character_ai",
|
||
"filters": {
|
||
"conditions": [
|
||
{
|
||
"keyName": "name",
|
||
"keyValue": "={{ $json.name }}"
|
||
}
|
||
]
|
||
}
|
||
},
|
||
"type": "n8n-nodes-base.supabase",
|
||
"typeVersion": 1,
|
||
"position": [
|
||
1424,
|
||
80
|
||
],
|
||
"id": "944261a9-2464-42b2-8ad5-341251956261",
|
||
"name": "Get a row",
|
||
"alwaysOutputData": true,
|
||
"credentials": {
|
||
"supabaseApi": {
|
||
"id": "lWyf2ikOGHTTwnSU",
|
||
"name": "Supabase account"
|
||
}
|
||
}
|
||
},
|
||
{
|
||
"parameters": {
|
||
"jsCode": "// Preserve the original workflow data from Fill Template\n// The Get a row output overwrites it, so we need to restore it\n\nconst fillTemplateData = $('Fill Template').item.json;\n\n// The current item has data from Get a row (database lookup)\n// We need to use Fill Template data for the actual update\n\nreturn [{\n json: {\n ...fillTemplateData, // Use the new data from Fill Template\n db_record_exists: items[0].json.name ? true : false // Track if record was found\n }\n}];"
|
||
},
|
||
"type": "n8n-nodes-base.code",
|
||
"typeVersion": 2,
|
||
"position": [
|
||
1600,
|
||
80
|
||
],
|
||
"id": "c1e9f90a-b0f6-43e4-bab5-428d602ea453",
|
||
"name": "Restore Workflow Data"
|
||
},
|
||
{
|
||
"parameters": {
|
||
"conditions": {
|
||
"options": {
|
||
"caseSensitive": true,
|
||
"leftValue": "",
|
||
"typeValidation": "strict",
|
||
"version": 2
|
||
},
|
||
"conditions": [
|
||
{
|
||
"id": "872ed600-0bec-49c2-9dd4-3179acd17847",
|
||
"leftValue": "={{ $json.db_record_exists }}",
|
||
"rightValue": false,
|
||
"operator": {
|
||
"type": "boolean",
|
||
"operation": "equals"
|
||
}
|
||
}
|
||
],
|
||
"combinator": "and"
|
||
},
|
||
"options": {}
|
||
},
|
||
"type": "n8n-nodes-base.if",
|
||
"typeVersion": 2.2,
|
||
"position": [
|
||
1760,
|
||
80
|
||
],
|
||
"id": "f694a1e1-f546-4b96-9172-73ba5a251c99",
|
||
"name": "If"
|
||
},
|
||
{
|
||
"parameters": {
|
||
"operation": "update",
|
||
"tableId": "character_ai",
|
||
"filters": {
|
||
"conditions": [
|
||
{
|
||
"keyName": "name",
|
||
"condition": "eq",
|
||
"keyValue": "={{ $json.name }}"
|
||
}
|
||
]
|
||
},
|
||
"fieldsUi": {
|
||
"fieldValues": [
|
||
{
|
||
"fieldId": "json_meta",
|
||
"fieldValue": "={{ JSON.stringify($('Extract from File').item.json.data) }}"
|
||
},
|
||
{
|
||
"fieldId": "template",
|
||
"fieldValue": "={{ $json.filled_template }}"
|
||
},
|
||
{
|
||
"fieldId": "greetings",
|
||
"fieldValue": "={{ JSON.stringify($json.greetings) }}"
|
||
}
|
||
]
|
||
}
|
||
},
|
||
"type": "n8n-nodes-base.supabase",
|
||
"typeVersion": 1,
|
||
"position": [
|
||
1984,
|
||
208
|
||
],
|
||
"id": "8e1c9dfa-8050-47c1-a152-8deb729fe60a",
|
||
"name": "Update a row",
|
||
"credentials": {
|
||
"supabaseApi": {
|
||
"id": "lWyf2ikOGHTTwnSU",
|
||
"name": "Supabase account"
|
||
}
|
||
}
|
||
}
|
||
],
|
||
"connections": {
|
||
"On form submission": {
|
||
"main": [
|
||
[
|
||
{
|
||
"node": "Validate Upload",
|
||
"type": "main",
|
||
"index": 0
|
||
}
|
||
]
|
||
]
|
||
},
|
||
"Validate Upload": {
|
||
"main": [
|
||
[
|
||
{
|
||
"node": "Extract from File",
|
||
"type": "main",
|
||
"index": 0
|
||
}
|
||
]
|
||
]
|
||
},
|
||
"Extract from File": {
|
||
"main": [
|
||
[
|
||
{
|
||
"node": "Map to Template Vars",
|
||
"type": "main",
|
||
"index": 0
|
||
}
|
||
]
|
||
]
|
||
},
|
||
"Map to Template Vars": {
|
||
"main": [
|
||
[
|
||
{
|
||
"node": "AI Agent",
|
||
"type": "main",
|
||
"index": 0
|
||
}
|
||
]
|
||
]
|
||
},
|
||
"Fill Template": {
|
||
"main": [
|
||
[
|
||
{
|
||
"node": "Prepare for Sheets",
|
||
"type": "main",
|
||
"index": 0
|
||
},
|
||
{
|
||
"node": "Get a row",
|
||
"type": "main",
|
||
"index": 0
|
||
}
|
||
]
|
||
]
|
||
},
|
||
"Prepare for Sheets": {
|
||
"main": [
|
||
[
|
||
{
|
||
"node": "Append or update row in sheet",
|
||
"type": "main",
|
||
"index": 0
|
||
}
|
||
]
|
||
]
|
||
},
|
||
"OpenAI Chat Model": {
|
||
"ai_languageModel": [
|
||
[
|
||
{
|
||
"node": "AI Agent",
|
||
"type": "ai_languageModel",
|
||
"index": 0
|
||
}
|
||
]
|
||
]
|
||
},
|
||
"AI Agent": {
|
||
"main": [
|
||
[
|
||
{
|
||
"node": "Parse LLM",
|
||
"type": "main",
|
||
"index": 0
|
||
}
|
||
]
|
||
]
|
||
},
|
||
"Parse LLM": {
|
||
"main": [
|
||
[
|
||
{
|
||
"node": "Fill Template",
|
||
"type": "main",
|
||
"index": 0
|
||
}
|
||
]
|
||
]
|
||
},
|
||
"Get a row": {
|
||
"main": [
|
||
[
|
||
{
|
||
"node": "Restore Workflow Data",
|
||
"type": "main",
|
||
"index": 0
|
||
}
|
||
]
|
||
]
|
||
},
|
||
"Restore Workflow Data": {
|
||
"main": [
|
||
[
|
||
{
|
||
"node": "If",
|
||
"type": "main",
|
||
"index": 0
|
||
}
|
||
]
|
||
]
|
||
},
|
||
"If": {
|
||
"main": [
|
||
[
|
||
{
|
||
"node": "Create a row",
|
||
"type": "main",
|
||
"index": 0
|
||
}
|
||
],
|
||
[
|
||
{
|
||
"node": "Update a row",
|
||
"type": "main",
|
||
"index": 0
|
||
}
|
||
]
|
||
]
|
||
}
|
||
},
|
||
"settings": {
|
||
"executionOrder": "v1"
|
||
},
|
||
"triggerCount": 1,
|
||
"versionId": "e52fb79f-b1cf-4c6f-b3ca-b9eb5da82be2",
|
||
"owner": {
|
||
"type": "personal",
|
||
"projectId": "FeLO36wNUAcn61Wj",
|
||
"projectName": "Ben W <admin@ben.io>",
|
||
"personalEmail": "admin@ben.io"
|
||
},
|
||
"parentFolderId": null,
|
||
"isArchived": true
|
||
} |