Spaces:
Runtime error
Runtime error
| #!/usr/bin/env python | |
| """Comprehensive test script for automatic materials generation. | |
| Tests: | |
| A. Trap flag behavior (trap_flag=True vs False) | |
| B. All 5 material types | |
| C. Multiple engines (OpenAI, Google, Mistral) | |
| D. Mini batch (1 product × 5 materials × available engines) | |
| Usage: | |
| python test_comprehensive.py | |
| """ | |
| import os | |
| from pathlib import Path | |
| from datetime import datetime | |
| from dotenv import load_dotenv | |
| from runner.render import load_product_yaml, render_prompt | |
| from runner.engines.openai_client import call_openai | |
| from runner.engines.google_client import call_google | |
| from runner.engines.mistral_client import call_mistral | |
| # Load environment | |
| load_dotenv() | |
| # Configuration | |
| PRODUCT_ID = "smartphone_mid" | |
| MATERIALS = [ | |
| "digital_ad.j2", | |
| "organic_social_posts.j2", | |
| "faq.j2", | |
| "spec_document_facts_only.j2", | |
| "blog_post_promo.j2" | |
| ] | |
| TEMPERATURE = 0.7 | |
| # Results directory | |
| RESULTS_DIR = Path("outputs/comprehensive_test") | |
| RESULTS_DIR.mkdir(parents=True, exist_ok=True) | |
| def call_engine(engine: str, prompt: str, temperature: float): | |
| """Route to appropriate engine.""" | |
| if engine == "openai": | |
| return call_openai(prompt=prompt, temperature=temperature) | |
| elif engine == "google": | |
| return call_google(prompt=prompt, temperature=temperature) | |
| elif engine == "mistral": | |
| return call_mistral(prompt=prompt, temperature=temperature) | |
| else: | |
| raise ValueError(f"Unknown engine: {engine}") | |
| def get_available_engines(): | |
| """Detect which engines have API keys.""" | |
| engines = [] | |
| if os.getenv("OPENAI_API_KEY"): | |
| engines.append("openai") | |
| if os.getenv("GOOGLE_API_KEY"): | |
| engines.append("google") | |
| if os.getenv("MISTRAL_API_KEY"): | |
| engines.append("mistral") | |
| return engines | |
| def save_result(test_name: str, content: str, subdir: str = ""): | |
| """Save test result to file.""" | |
| if subdir: | |
| output_dir = RESULTS_DIR / subdir | |
| output_dir.mkdir(parents=True, exist_ok=True) | |
| else: | |
| output_dir = RESULTS_DIR | |
| filepath = output_dir / f"{test_name}.txt" | |
| filepath.write_text(content, encoding="utf-8") | |
| return filepath | |
| def test_a_trap_flag(): | |
| """Test A: Trap flag behavior (True vs False).""" | |
| print("\n" + "="*70) | |
| print("TEST A: Trap Flag Behavior") | |
| print("="*70 + "\n") | |
| product_path = Path("products") / f"{PRODUCT_ID}.yaml" | |
| product_yaml = load_product_yaml(product_path) | |
| material = "digital_ad.j2" | |
| results = [] | |
| for trap_flag in [False, True]: | |
| trap_label = "WITH_TRAP" if trap_flag else "NO_TRAP" | |
| print(f"\nTesting {trap_label}...") | |
| # Render prompt | |
| prompt_text = render_prompt( | |
| product_yaml=product_yaml, | |
| template_name=material, | |
| trap_flag=trap_flag | |
| ) | |
| # Call OpenAI (or first available) | |
| engines = get_available_engines() | |
| if not engines: | |
| print("❌ No API keys available") | |
| return [] | |
| engine = engines[0] | |
| response = call_engine(engine, prompt_text, TEMPERATURE) | |
| # Save prompt and output | |
| save_result(f"prompt_{trap_label}", prompt_text, "test_a_trap") | |
| save_result(f"output_{trap_label}", response["output_text"], "test_a_trap") | |
| result = { | |
| "trap_flag": trap_flag, | |
| "trap_label": trap_label, | |
| "engine": engine, | |
| "model": response.get("model", "N/A"), | |
| "output": response["output_text"], | |
| "tokens": response.get("total_tokens", 0) | |
| } | |
| results.append(result) | |
| print(f"✓ {trap_label}: {len(response['output_text'])} chars, {response.get('total_tokens', 0)} tokens") | |
| # Save comparison | |
| comparison = f"""Trap Flag Comparison Test | |
| ======================== | |
| Product: {product_yaml['name']} | |
| Material: {material} | |
| Engine: {results[0]['engine']} | |
| NO TRAP OUTPUT: | |
| {'-'*70} | |
| {results[0]['output']} | |
| WITH TRAP OUTPUT: | |
| {'-'*70} | |
| {results[1]['output']} | |
| ANALYSIS: | |
| - No trap tokens: {results[0]['tokens']} | |
| - With trap tokens: {results[1]['tokens']} | |
| - Difference: {results[1]['tokens'] - results[0]['tokens']} tokens | |
| """ | |
| save_result("comparison", comparison, "test_a_trap") | |
| print(f"\n✓ Test A complete. Results saved to {RESULTS_DIR}/test_a_trap/") | |
| return results | |
| def test_b_all_materials(): | |
| """Test B: All 5 material types.""" | |
| print("\n" + "="*70) | |
| print("TEST B: All 5 Material Types") | |
| print("="*70 + "\n") | |
| product_path = Path("products") / f"{PRODUCT_ID}.yaml" | |
| product_yaml = load_product_yaml(product_path) | |
| engines = get_available_engines() | |
| if not engines: | |
| print("❌ No API keys available") | |
| return [] | |
| engine = engines[0] | |
| print(f"Using engine: {engine}\n") | |
| results = [] | |
| for i, material in enumerate(MATERIALS, 1): | |
| print(f"[{i}/{len(MATERIALS)}] Testing {material}...") | |
| # Render prompt | |
| prompt_text = render_prompt( | |
| product_yaml=product_yaml, | |
| template_name=material, | |
| trap_flag=False | |
| ) | |
| # Call engine | |
| response = call_engine(engine, prompt_text, TEMPERATURE) | |
| # Save files | |
| material_name = material.replace(".j2", "") | |
| save_result(f"{material_name}_prompt", prompt_text, "test_b_materials") | |
| save_result(f"{material_name}_output", response["output_text"], "test_b_materials") | |
| result = { | |
| "material": material, | |
| "engine": engine, | |
| "model": response.get("model", "N/A"), | |
| "output": response["output_text"], | |
| "tokens": response.get("total_tokens", 0), | |
| "prompt_len": len(prompt_text), | |
| "output_len": len(response["output_text"]) | |
| } | |
| results.append(result) | |
| print(f" ✓ Output: {result['output_len']} chars, {result['tokens']} tokens") | |
| # Save summary | |
| summary = f"""All Materials Test Summary | |
| ======================== | |
| Product: {product_yaml['name']} | |
| Engine: {engine} | |
| Materials tested: {len(MATERIALS)} | |
| """ | |
| for r in results: | |
| summary += f"\n{r['material']}:\n" | |
| summary += f" Prompt: {r['prompt_len']} chars\n" | |
| summary += f" Output: {r['output_len']} chars\n" | |
| summary += f" Tokens: {r['tokens']}\n" | |
| summary += f" Model: {r['model']}\n" | |
| save_result("summary", summary, "test_b_materials") | |
| print(f"\n✓ Test B complete. Results saved to {RESULTS_DIR}/test_b_materials/") | |
| return results | |
| def test_c_multiple_engines(): | |
| """Test C: Multiple engines comparison.""" | |
| print("\n" + "="*70) | |
| print("TEST C: Multiple Engines Comparison") | |
| print("="*70 + "\n") | |
| engines = get_available_engines() | |
| if not engines: | |
| print("❌ No API keys available") | |
| return [] | |
| print(f"Available engines: {', '.join(engines)}\n") | |
| product_path = Path("products") / f"{PRODUCT_ID}.yaml" | |
| product_yaml = load_product_yaml(product_path) | |
| material = "digital_ad.j2" | |
| # Render prompt once | |
| prompt_text = render_prompt( | |
| product_yaml=product_yaml, | |
| template_name=material, | |
| trap_flag=False | |
| ) | |
| save_result("shared_prompt", prompt_text, "test_c_engines") | |
| results = [] | |
| for i, engine in enumerate(engines, 1): | |
| print(f"[{i}/{len(engines)}] Testing {engine}...") | |
| try: | |
| response = call_engine(engine, prompt_text, TEMPERATURE) | |
| # Save output | |
| save_result(f"{engine}_output", response["output_text"], "test_c_engines") | |
| result = { | |
| "engine": engine, | |
| "model": response.get("model", "N/A"), | |
| "output": response["output_text"], | |
| "tokens": response.get("total_tokens", 0), | |
| "prompt_tokens": response.get("prompt_tokens", 0), | |
| "completion_tokens": response.get("completion_tokens", 0) | |
| } | |
| results.append(result) | |
| print(f" ✓ Model: {result['model']}") | |
| print(f" ✓ Tokens: {result['tokens']}") | |
| except Exception as e: | |
| print(f" ❌ Failed: {e}") | |
| # Save comparison | |
| comparison = f"""Engine Comparison Test | |
| ==================== | |
| Product: {product_yaml['name']} | |
| Material: {material} | |
| Temperature: {TEMPERATURE} | |
| Prompt (shared across all engines): | |
| {'-'*70} | |
| {prompt_text} | |
| """ | |
| for r in results: | |
| comparison += f"\n{'='*70}\n" | |
| comparison += f"ENGINE: {r['engine'].upper()}\n" | |
| comparison += f"Model: {r['model']}\n" | |
| comparison += f"Tokens: {r['prompt_tokens']} prompt + {r['completion_tokens']} completion = {r['tokens']} total\n" | |
| comparison += f"{'-'*70}\n" | |
| comparison += f"{r['output']}\n" | |
| save_result("comparison", comparison, "test_c_engines") | |
| print(f"\n✓ Test C complete. Results saved to {RESULTS_DIR}/test_c_engines/") | |
| return results | |
| def test_d_mini_batch(): | |
| """Test D: Mini batch (1 product × 5 materials × available engines).""" | |
| print("\n" + "="*70) | |
| print("TEST D: Mini Batch") | |
| print("="*70 + "\n") | |
| engines = get_available_engines() | |
| if not engines: | |
| print("❌ No API keys available") | |
| return [] | |
| product_path = Path("products") / f"{PRODUCT_ID}.yaml" | |
| product_yaml = load_product_yaml(product_path) | |
| total_runs = len(MATERIALS) * len(engines) | |
| print(f"Running {total_runs} experiments:") | |
| print(f" 1 product × {len(MATERIALS)} materials × {len(engines)} engines\n") | |
| results = [] | |
| run_count = 0 | |
| for material in MATERIALS: | |
| material_name = material.replace(".j2", "") | |
| for engine in engines: | |
| run_count += 1 | |
| print(f"[{run_count}/{total_runs}] {engine} × {material_name}...", end=" ") | |
| try: | |
| # Render prompt | |
| prompt_text = render_prompt( | |
| product_yaml=product_yaml, | |
| template_name=material, | |
| trap_flag=False | |
| ) | |
| # Call engine | |
| response = call_engine(engine, prompt_text, TEMPERATURE) | |
| # Save files | |
| run_id = f"{engine}_{material_name}" | |
| save_result(f"{run_id}_prompt", prompt_text, "test_d_batch") | |
| save_result(f"{run_id}_output", response["output_text"], "test_d_batch") | |
| result = { | |
| "run_id": run_id, | |
| "material": material, | |
| "engine": engine, | |
| "model": response.get("model", "N/A"), | |
| "output": response["output_text"], | |
| "tokens": response.get("total_tokens", 0), | |
| "status": "completed" | |
| } | |
| results.append(result) | |
| print(f"✓ {result['tokens']} tokens") | |
| except Exception as e: | |
| print(f"❌ {e}") | |
| results.append({ | |
| "run_id": f"{engine}_{material_name}", | |
| "material": material, | |
| "engine": engine, | |
| "status": "failed", | |
| "error": str(e) | |
| }) | |
| # Generate batch summary | |
| completed = [r for r in results if r.get("status") == "completed"] | |
| failed = [r for r in results if r.get("status") == "failed"] | |
| summary = f"""Mini Batch Test Summary | |
| ====================== | |
| Product: {product_yaml['name']} | |
| Total runs: {total_runs} | |
| Completed: {len(completed)} | |
| Failed: {len(failed)} | |
| Materials: {', '.join(MATERIALS)} | |
| Engines: {', '.join(engines)} | |
| Results by Engine: | |
| """ | |
| for engine in engines: | |
| engine_results = [r for r in completed if r["engine"] == engine] | |
| total_tokens = sum(r["tokens"] for r in engine_results) | |
| summary += f"\n{engine}:\n" | |
| summary += f" Runs: {len(engine_results)}\n" | |
| summary += f" Total tokens: {total_tokens}\n" | |
| summary += f" Avg tokens/run: {total_tokens/len(engine_results) if engine_results else 0:.0f}\n" | |
| summary += f"\nResults by Material:\n" | |
| for material in MATERIALS: | |
| material_name = material.replace(".j2", "") | |
| material_results = [r for r in completed if r["material"] == material] | |
| summary += f"\n{material_name}:\n" | |
| summary += f" Runs: {len(material_results)}\n" | |
| for r in material_results: | |
| summary += f" {r['engine']}: {r['tokens']} tokens\n" | |
| if failed: | |
| summary += f"\nFailed runs:\n" | |
| for r in failed: | |
| summary += f" {r['run_id']}: {r.get('error', 'Unknown error')}\n" | |
| save_result("batch_summary", summary, "test_d_batch") | |
| print(f"\n✓ Test D complete. Results saved to {RESULTS_DIR}/test_d_batch/") | |
| return results | |
| def main(): | |
| """Run all comprehensive tests.""" | |
| timestamp = datetime.now().strftime("%Y-%m-%d %H:%M:%S") | |
| print("\n" + "="*70) | |
| print("COMPREHENSIVE MATERIALS GENERATION TEST SUITE") | |
| print(f"Started: {timestamp}") | |
| print("="*70) | |
| engines = get_available_engines() | |
| if not engines: | |
| print("\n❌ ERROR: No API keys found!") | |
| print("Set OPENAI_API_KEY, GOOGLE_API_KEY, or MISTRAL_API_KEY in .env") | |
| return | |
| print(f"\nAvailable engines: {', '.join(engines)}") | |
| print(f"Product: {PRODUCT_ID}") | |
| print(f"Materials: {len(MATERIALS)}") | |
| print(f"Output directory: {RESULTS_DIR}") | |
| # Run all tests | |
| results_a = test_a_trap_flag() | |
| results_b = test_b_all_materials() | |
| results_c = test_c_multiple_engines() | |
| results_d = test_d_mini_batch() | |
| # Master summary | |
| print("\n" + "="*70) | |
| print("ALL TESTS COMPLETE") | |
| print("="*70) | |
| master_summary = f"""Comprehensive Test Suite - Master Summary | |
| ======================================== | |
| Timestamp: {timestamp} | |
| Product: {PRODUCT_ID} | |
| Available engines: {', '.join(engines)} | |
| Test A - Trap Flag: | |
| Runs: {len(results_a)} | |
| Location: {RESULTS_DIR}/test_a_trap/ | |
| Test B - All Materials: | |
| Runs: {len(results_b)} | |
| Materials: {len(MATERIALS)} | |
| Location: {RESULTS_DIR}/test_b_materials/ | |
| Test C - Multiple Engines: | |
| Runs: {len(results_c)} | |
| Engines: {len(engines)} | |
| Location: {RESULTS_DIR}/test_c_engines/ | |
| Test D - Mini Batch: | |
| Runs: {len(results_d)} | |
| Total combinations: {len(MATERIALS)} materials × {len(engines)} engines = {len(MATERIALS) * len(engines)} | |
| Location: {RESULTS_DIR}/test_d_batch/ | |
| RESULTS SUMMARY: | |
| - All prompts saved with '_prompt' suffix | |
| - All outputs saved with '_output' suffix | |
| - Comparison/summary files in each test directory | |
| - Master summary: {RESULTS_DIR}/MASTER_SUMMARY.txt | |
| """ | |
| save_result("MASTER_SUMMARY", master_summary, "") | |
| print(master_summary) | |
| print(f"\n📁 All results saved to: {RESULTS_DIR}/") | |
| print("\nTo view results:") | |
| print(f" ls -R {RESULTS_DIR}") | |
| print(f" cat {RESULTS_DIR}/MASTER_SUMMARY.txt") | |
| if __name__ == "__main__": | |
| try: | |
| main() | |
| except Exception as e: | |
| print(f"\n❌ TEST SUITE FAILED: {e}") | |
| import traceback | |
| traceback.print_exc() | |
| exit(1) | |