From 69297d990b8171c68dc95b31f345fb999b12208c Mon Sep 17 00:00:00 2001 From: Floke Date: Sat, 3 Jan 2026 13:01:32 +0000 Subject: [PATCH] Fix: Resolved [object Object] report bug, enforced German output language across all phases, and documented troubleshooting steps --- gtm-architect/App.tsx | 4 +-- gtm_architect_orchestrator.py | 61 ++++++++++++++++++++++++++++++++--- 2 files changed, 58 insertions(+), 7 deletions(-) diff --git a/gtm-architect/App.tsx b/gtm-architect/App.tsx index 8a129b31..d450495e 100644 --- a/gtm-architect/App.tsx +++ b/gtm-architect/App.tsx @@ -298,9 +298,9 @@ const App: React.FC = () => { }; const generateFullReportMarkdown = (): string => { - if (!state.phase5Result) return ""; + if (!state.phase5Result || !state.phase5Result.report) return ""; - let fullReport = state.phase5Result; + let fullReport = state.phase5Result.report; if (state.phase6Result) { fullReport += `\n\n# SALES ENABLEMENT & VISUALS (PHASE 6)\n\n`; diff --git a/gtm_architect_orchestrator.py b/gtm_architect_orchestrator.py index 17ce79d1..d153c9b4 100644 --- a/gtm_architect_orchestrator.py +++ b/gtm_architect_orchestrator.py @@ -1,4 +1,3 @@ - import argparse import base64 import json @@ -21,7 +20,7 @@ LOG_DIR = "Log_from_docker" if not os.path.exists(LOG_DIR): os.makedirs(LOG_DIR) -ORCHESTRATOR_VERSION = "1.1.0" +ORCHESTRATOR_VERSION = "1.2.0" # Bump version for language fix run_timestamp = datetime.now().strftime("%y-%m-%d_%H-%M-%S") log_file_path = os.path.join(LOG_DIR, f"{run_timestamp}_gtm_orchestrator_run.log") @@ -33,11 +32,12 @@ logging.basicConfig( logging.StreamHandler(sys.stdout) ] ) -logging.info(f"GTM Architect Orchestrator v{{ORCHESTRATOR_VERSION}} ({{run_timestamp}}) starting...") +logging.info(f"GTM Architect Orchestrator v{ORCHESTRATOR_VERSION} ({run_timestamp}) starting...") # !!! CRITICAL FIX: Load API keys at the very beginning !!! # This ensures Config.API_KEYS is populated before any AI functions are called. Config.load_api_keys() + def log_and_save(project_id, step_name, data_type, content): logging.info(f"Project {project_id} - Step: {step_name} - Type: {data_type}") filename = f"{run_timestamp}_{step_name}_{data_type}.txt" @@ -63,6 +63,7 @@ def get_system_instruction(lang): Führe eine interne Plausibilitätsprüfung durch, bevor du eine Antwort gibst. Verwende "Wackler Symbiosis" als internes Framework für die Analyse von Produkt-Synergien. Nutze das "Hybrid Service Logic" Konzept, um zu bewerten, ob ein Produkt mit einer Dienstleistung kombiniert werden muss (z.B. bei hohen Wartungsanforderungen). + WICHTIG: Antworte immer in der vom User geforderten Sprache (Deutsch), auch wenn der Input Englisch ist. """ else: # Default to English return """ @@ -76,6 +77,12 @@ def get_system_instruction(lang): Use the "Hybrid Service Logic" concept to evaluate if a product needs to be combined with a service (e.g., due to high maintenance requirements). """ +def get_output_lang_instruction(lang): + """Returns a strong instruction to enforce the output language.""" + if lang == 'de': + return "ACHTUNG: Die gesamte Ausgabe (JSON-Werte, Texte, Analysen) MUSS in DEUTSCH sein. Übersetze englische Input-Daten." + return "IMPORTANT: The entire output MUST be in ENGLISH." + # --- ORCHESTRATOR PHASES --- def phase1(payload): @@ -96,6 +103,8 @@ def phase1(payload): logging.info("Input is raw text. Analyzing directly.") sys_instr = get_system_instruction(lang) + lang_instr = get_output_lang_instruction(lang) + prompt = f""" PHASE 1: PRODUCT ANALYSIS & CONSTRAINTS Input: "{analysis_content}" @@ -103,6 +112,9 @@ def phase1(payload): 1. Extract and CONSOLIDATE technical features into 8-12 high-level core capabilities or value propositions. Group minor specs (e.g., specific ports like USB/Ethernet) into broader categories (e.g., "Connectivity & Integration"). Do NOT list every single hardware spec individually. Focus on what matters for the buyer. 2. Define hard constraints (e.g., physical dimensions, max payload, environment limitations). 3. Check for internal portfolio conflicts (hypothetical product "Scrubber 5000"). + + {lang_instr} + Output JSON format ONLY: {{"features": [], "constraints": [], "conflictCheck": {{"hasConflict": false, "details": "", "relatedProduct": ""}}, "rawAnalysis": ""}} """ log_and_save(project_id, "phase1", "prompt", prompt) @@ -115,7 +127,6 @@ def phase1(payload): return data except json.JSONDecodeError: logging.error(f"Failed to decode JSON from Gemini response in phase1: {response}") - # Return a structured error that the frontend can display error_response = { "error": "Die Antwort des KI-Modells war kein gültiges JSON. Das passiert manchmal bei hoher Auslastung. Bitte versuchen Sie es in Kürze erneut.", "details": response @@ -129,10 +140,15 @@ def phase2(payload): project_id = payload.get('projectId') sys_instr = get_system_instruction(lang) + lang_instr = get_output_lang_instruction(lang) + prompt = f""" PHASE 2: IDEAL CUSTOMER PROFILE (ICP) & DATA PROXIES Product Context: {json.dumps(phase1_data)} Task: 1. Identify top 3 ICPs (Ideal Customer Profiles/Industries). 2. Define data proxies for identifying these ICPs online. + + {lang_instr} + Output JSON format ONLY: {{"icps": [{{"name": "", "rationale": ""}}], "dataProxies": [{{"target": "", "method": ""}}]}} """ log_and_save(project_id, "phase2", "prompt", prompt) @@ -148,10 +164,15 @@ def phase3(payload): project_id = payload.get('projectId') sys_instr = get_system_instruction(lang) + lang_instr = get_output_lang_instruction(lang) + prompt = f""" PHASE 3: WHALE HUNTING Target ICPs (Industries): {json.dumps(phase2_data.get('icps'))} Task: 1. Group 'Whales' (Key Accounts) strictly by ICP industries. 2. Identify 3-5 concrete top companies in DACH market per industry. 3. Define Buying Center Roles. + + {lang_instr} + Output JSON format ONLY: {{"whales": [{{"industry": "", "accounts": []}}], "roles": []}} """ log_and_save(project_id, "phase3", "prompt", prompt) @@ -168,6 +189,8 @@ def phase4(payload): project_id = payload.get('projectId') sys_instr = get_system_instruction(lang) + lang_instr = get_output_lang_instruction(lang) + all_accounts = [] for w in phase3_data.get('whales', []): all_accounts.extend(w.get('accounts', [])) @@ -178,6 +201,9 @@ def phase4(payload): Target Industries: {json.dumps([w.get('industry') for w in phase3_data.get('whales', [])])} Product Features: {json.dumps(phase1_data.get('features'))} Task: 1. Develop specific "Angle" per target/industry. 2. Consistency Check against Product Matrix. 3. **IMPORTANT:** Apply "Hybrid Service Logic" if constraints exist! + + {lang_instr} + Output JSON format ONLY: {{"strategyMatrix": [{{"segment": "", "painPoint": "", "angle": "", "differentiation": ""}}]}} """ log_and_save(project_id, "phase4", "prompt", prompt) @@ -196,6 +222,8 @@ def phase5(payload): project_id = payload.get('projectId') sys_instr = get_system_instruction(lang) + lang_instr = get_output_lang_instruction(lang) + prompt = f""" PHASE 5: ASSET GENERATION & FINAL REPORT CONTEXT DATA: @@ -207,6 +235,9 @@ def phase5(payload): 1. Create a "GTM STRATEGY REPORT" in Markdown. 2. Report Structure: Executive Summary, Product Analysis, Target Audience, Target Accounts, Strategy Matrix, Assets. 3. Hybrid-Check: Ensure "Hybrid Service Logic" is visible. + + {lang_instr} + Output: Return strictly MARKDOWN formatted text. Start with "# GTM STRATEGY REPORT". """ log_and_save(project_id, "phase5", "prompt", prompt) @@ -223,10 +254,15 @@ def phase6(payload): project_id = payload.get('projectId') sys_instr = get_system_instruction(lang) + lang_instr = get_output_lang_instruction(lang) + prompt = f""" PHASE 6: SALES ENABLEMENT & VISUALS CONTEXT: - Product Features: {json.dumps(phase1_data.get('features'))} - Personas: {json.dumps(phase3_data.get('roles'))} - Strategy: {json.dumps(phase4_data.get('strategyMatrix'))} TASK: 1. Anticipate Friction & Objections. 2. Formulate Battlecards. 3. Create Visual Prompts. + + {lang_instr} + Output JSON format ONLY: {{"battlecards": [{{"persona": "", "objection": "", "responseScript": ""}}], "visualPrompts": [{{"title": "", "context": "", "prompt": ""}}]}} """ log_and_save(project_id, "phase6", "prompt", prompt) @@ -243,11 +279,16 @@ def phase7(payload): project_id = payload.get('projectId') sys_instr = get_system_instruction(lang) + lang_instr = get_output_lang_instruction(lang) + prompt = f""" PHASE 7: VERTICAL LANDING PAGE COPY (Conversion Optimization) ICPs: {json.dumps(phase2_data.get('icps'))} Strategy: {json.dumps(phase4_data.get('strategyMatrix'))} TASK: 1. Transform generic features into specific benefits for the Top 2 ICPs. 2. Apply "Wackler Symbiosis". 3. Create Landing Page Drafts (Hero Section). + + {lang_instr} + Output JSON format ONLY: {{"landingPages": [{{"industry": "", "headline": "", "subline": "", "bullets": [], "cta": ""}}]}} """ log_and_save(project_id, "phase7", "prompt", prompt) @@ -264,10 +305,15 @@ def phase8(payload): project_id = payload.get('projectId') sys_instr = get_system_instruction(lang) + lang_instr = get_output_lang_instruction(lang) + prompt = f""" PHASE 8: BUSINESS CASE BUILDER (The CFO Pitch) Input: ICPs: {json.dumps(phase2_data.get('icps'))}, Features: {json.dumps(phase1_data.get('features'))} TASK: 1. Estimate labor costs/pain points. 2. Compare against Robot Leasing (approx 330-600€/month). 3. Develop ROI logic. 4. Create "Financial Argumentation Guide" for each ICP. + + {lang_instr} + Output JSON format ONLY: {{"businessCases": [{{"industry": "", "costDriver": "", "efficiencyGain": "", "riskArgument": ""}}]}} """ log_and_save(project_id, "phase8", "prompt", prompt) @@ -284,11 +330,16 @@ def phase9(payload): project_id = payload.get('projectId') sys_instr = get_system_instruction(lang) + lang_instr = get_output_lang_instruction(lang) + prompt = f""" PHASE 9: THE "FEATURE-TO-VALUE" TRANSLATOR Input Features: {json.dumps(phase1_data.get('features'))} Strategy Pains: {json.dumps([s.get('painPoint') for s in phase4_data.get('strategyMatrix', [])])} TASK: 1. Take a tech feature. 2. Ask "So what?". 3. Ask "So what?" again. 4. Formulate benefit without jargon. Create a table. + + {lang_instr} + Output JSON format ONLY: {{"techTranslations": [{{"feature": "", "story": "", "headline": ""}}]}} """ log_and_save(project_id, "phase9", "prompt", prompt) @@ -361,4 +412,4 @@ def main(): sys.exit(1) if __name__ == "__main__": - main() + main() \ No newline at end of file