""" Condition 4: Full Pipeline (Attributes + Experts) The complete novelty-seeking system: 1. Attribute decomposition into categories 2. Expert team generation 3. Expert keyword generation for each attribute 4. Description generation for each keyword """ import sys from pathlib import Path # Add backend to path for imports sys.path.insert(0, str(Path(__file__).parent.parent.parent / "backend")) from typing import List, Dict, Any from app.services.llm_service import ollama_provider, extract_json_from_response from app.services.expert_source_service import expert_source_service from app.prompts.attribute_prompt import get_step1_dynamic_attributes_prompt from app.prompts.expert_transformation_prompt import ( get_expert_keyword_generation_prompt, get_single_description_prompt ) from experiments.config import ( MODEL, TEMPERATURE, FIXED_CATEGORIES, EXPERT_COUNT, EXPERT_SOURCE, KEYWORDS_PER_EXPERT, PROMPT_LANGUAGE ) async def generate_ideas( query: str, model: str = None, temperature: float = None, categories: List[str] = None, expert_count: int = None, expert_source: str = None, keywords_per_expert: int = None, lang: str = None ) -> Dict[str, Any]: """ Generate ideas using the full pipeline (C4). Args: query: The object/concept to generate ideas for model: LLM model to use temperature: Generation temperature categories: Categories for attribute decomposition expert_count: Number of experts expert_source: Source of experts keywords_per_expert: Keywords each expert generates per attribute lang: Language for prompts Returns: Dict with ideas and metadata """ model = model or MODEL temperature = temperature or TEMPERATURE categories = categories or FIXED_CATEGORIES expert_count = expert_count or EXPERT_COUNT expert_source = expert_source or EXPERT_SOURCE keywords_per_expert = keywords_per_expert or KEYWORDS_PER_EXPERT lang = lang or PROMPT_LANGUAGE # Step 0: Get experts from curated source experts_data, actual_source = expert_source_service.get_experts( source=expert_source, count=expert_count, language=lang ) # Convert to expected format experts = [ { "id": f"expert-{i}", "name": exp.get("name", "Expert"), "domain": exp.get("domain", ""), "perspective": exp.get("perspective", "") } for i, exp in enumerate(experts_data) ] # Step 1: Generate attributes category_defs = [ {"name": cat, "description": f"Related {cat.lower()} of the object", "order": i} for i, cat in enumerate(categories) ] attr_prompt = get_step1_dynamic_attributes_prompt( query=query, categories=category_defs, lang=lang ) attr_response = await ollama_provider.generate( prompt=attr_prompt, model=model, temperature=temperature ) attributes_by_category = extract_json_from_response(attr_response) # Step 2: Expert keyword generation for each category/attribute all_keywords = [] for category in categories: attrs = attributes_by_category.get(category, []) for attr in attrs: # Generate keywords from all experts for this attribute keyword_prompt = get_expert_keyword_generation_prompt( category=category, attribute=attr, experts=experts, keywords_per_expert=keywords_per_expert, lang=lang ) keyword_response = await ollama_provider.generate( prompt=keyword_prompt, model=model, temperature=temperature ) keyword_result = extract_json_from_response(keyword_response) keywords = keyword_result.get("keywords", []) for kw in keywords: all_keywords.append({ "category": category, "attribute": attr, "keyword": kw.get("keyword", ""), "expert_id": kw.get("expert_id", ""), "expert_name": kw.get("expert_name", "") }) # Step 3: Generate descriptions for each keyword all_ideas = [] for kw_info in all_keywords: # Find expert details expert = next( (e for e in experts if e["id"] == kw_info["expert_id"]), {"name": kw_info["expert_name"], "domain": "", "id": kw_info["expert_id"]} ) desc_prompt = get_single_description_prompt( query=query, keyword=kw_info["keyword"], expert_id=expert["id"], expert_name=expert["name"], expert_domain=expert.get("domain", ""), lang=lang ) desc_response = await ollama_provider.generate( prompt=desc_prompt, model=model, temperature=temperature ) desc_result = extract_json_from_response(desc_response) description = desc_result.get("description", "") all_ideas.append({ "idea": description, "keyword": kw_info["keyword"], "category": kw_info["category"], "attribute": kw_info["attribute"], "expert_name": expert["name"], "expert_domain": expert.get("domain", "") }) return { "condition": "c4_full_pipeline", "query": query, "ideas": [item["idea"] for item in all_ideas], "ideas_with_source": all_ideas, "idea_count": len(all_ideas), "metadata": { "model": model, "temperature": temperature, "prompt_language": lang, "categories": categories, "attributes_by_category": attributes_by_category, "attribute_count": sum(len(v) for v in attributes_by_category.values()), "expert_count": expert_count, "expert_source": actual_source, "keywords_per_expert": keywords_per_expert, "total_keywords": len(all_keywords), "experts": [{"name": e["name"], "domain": e["domain"]} for e in experts], "mechanism": "full_pipeline_attributes_plus_experts" } } # For testing if __name__ == "__main__": import asyncio async def test(): result = await generate_ideas("Chair") print(f"Generated {result['idea_count']} ideas using full pipeline:") print(f" Attributes: {result['metadata']['attribute_count']}") print(f" Experts: {result['metadata']['expert_count']}") print(f" Keywords: {result['metadata']['total_keywords']}") print("\nExperts used:") for exp in result['metadata']['experts']: print(f" - {exp['name']} ({exp['domain']})") print("\nSample ideas:") for i, item in enumerate(result['ideas_with_source'][:5], 1): print(f" {i}. [{item['expert_name']}] {item['keyword']}: {item['idea']}") asyncio.run(test())