# model.py import os import json from langchain_huggingface import HuggingFaceEndpoint, ChatHuggingFace from langchain_openai import ChatOpenAI from langchain_community.document_loaders import PyPDFLoader from crewai import Agent, Task, Crew, Process # --- 1. LLM Factory Engine --- def initialize_llm(provider, selected_model, user_token=None): """ Initializes and returns the appropriate LangChain chat wrapper depending on the chosen provider (Hugging Face or OpenAI). """ if provider == "Hugging Face Serverless (Default)": # Resolve token priority: Explicit user token -> Env variable secrets token = user_token if user_token else (os.getenv("HUGGINGFACEHUB_API_TOKEN") or os.getenv("HF_TOKEN")) if not token: raise ValueError("Hugging Face API Token missing! Please provide it in the sidebar or Space Secrets.") base_endpoint = HuggingFaceEndpoint( repo_id=selected_model, task="text-generation", huggingfacehub_api_token=token, max_new_tokens=1500, temperature=0.2 ) return ChatHuggingFace(llm=base_endpoint) elif provider == "OpenAI (Optional Override)": token = user_token if user_token else os.getenv("OPENAI_API_KEY") if not token: raise ValueError("OpenAI API Key missing! Please provide it in the sidebar input field.") return ChatOpenAI(model=selected_model, api_key=token, temperature=0.1) else: raise ValueError(f"Unknown provider type: {provider}") # --- 2. Advanced Document Context Parser --- def parse_uploaded_file_content(file_name, file_bytes): """ Extracts raw text strings from multiple formats: .pdf, .ipynb, .py, .txt """ # Write to a temporary file locally so loaders can reference it with open(file_name, "wb") as temp_file: temp_file.write(file_bytes) try: if file_name.endswith(".pdf"): loader = PyPDFLoader(file_name) pages = loader.load_and_split() extracted_text = "\n".join([page.page_content for page in pages]) elif file_name.endswith(".ipynb"): notebook_data = json.loads(file_bytes.decode("utf-8")) code_lines = [] for cell in notebook_data.get("cells", []): if cell.get("cell_type") == "code": code_lines.append("".join(cell.get("source", []))) extracted_text = "\n# --- Notebook Cell Output ---\n".join(code_lines) else: # Fallback text decoder for standard code scripts (.py, .txt, .md) extracted_text = file_bytes.decode("utf-8") # Context cleaning/wrapping return f"\n\n--- REFERENCE DOCUMENT ATTACHED ({file_name}) ---\n{extracted_text}\n" except Exception as parse_error: return f"\n\n[Error parsing document data from {file_name}: {str(parse_error)}]\n" finally: # Guarantee local filesystem cleanup if os.path.exists(file_name): os.remove(file_name) # --- 3. Specialist Agent Configurations Catalog --- AGENT_PERSONAS = { "Personal Motivator": { "role": "High-Performance Life Coach", "backstory": "An inspiring professional who helps users crush creative blockages, break down bad habits, and build an unstoppable focus mindset.", "goal": "Inject intense enthusiasm, structured routines, and motivational insight into the user prompt." }, "Code Reviewer": { "role": "Principal Software Engineer & Architect", "backstory": "An expert with decades of experience tracking down edge-cases, semantic errors, bad performance bottlenecks, and structural refactoring improvements.", "goal": "Thoroughly review structural files or algorithms to provide high-quality code changes." }, "Technical Researcher": { "role": "Lead Data Analytics Scholar", "backstory": "A detail-oriented analyst who excels at parsing complex raw documents, compiling literature overviews, and summarizing trends accurately.", "goal": "Identify hidden insights from context and write clear technical documentation summaries." }, "Physics Formula Explainer": { "role": "Theoretical Physics Professor", "backstory": "An educator who makes complex physical theories, mathematical proofs, and equations easy to understand by using intuitive analogies and practical real-world applications.", "goal": "Deconstruct complex physics models and equations step-by-step into clear, understandable explanations." }, "General Purpose Assistant": { "role": "Contextual Problem Solver", "backstory": "A versatile technical assistant skilled at answering cross-functional development questions clearly.", "goal": "Fulfill the user request precisely while adjusting to the provided documentation constraints." } } # --- 4. Main Multi-Agent Execution Orchestrator --- def run_agent_pipeline(provider, model_name, token, persona_key, user_prompt, context_text): """ Coordinates the selected Agent Persona alongside a secondary Quality Assurance Auditor to review and polish the output before displaying it. """ # 1. Instantiate the chosen LLM runtime llm = initialize_llm(provider, model_name, user_token=token) # 2. Extract Persona Definitions persona = AGENT_PERSONAS[persona_key] # 3. Assemble Specialist Agent specialist_agent = Agent( role=persona["role"], backstory=persona["backstory"], goal=persona["goal"], llm=llm, verbose=True, allow_delegation=False ) # 4. Assemble Independent QA Critic Agent auditor_agent = Agent( role="Senior QA & Compliance Auditor", backstory="An unyielding editor focused on correcting hallucinations, logic formatting errors, typos, and completeness issues.", goal="Critique the primary specialist's generated draft response and output a polished, final version.", llm=llm, verbose=True ) # 5. Define Task Blueprints complete_task_description = f"{user_prompt}\n\n{context_text}" generation_task = Task( description=f"Address the user requirements thoroughly using your specific persona parameters.\nRequirements:\n{complete_task_description}", agent=specialist_agent, expected_output="A rich, highly comprehensive response matching your assigned persona specialty." ) review_task = Task( description="Review the generated response for accuracy, clarity, and formatting. Correct any typos or logical errors, and format it cleanly in Markdown.", agent=auditor_agent, context=[generation_task], expected_output="The final, polished markdown response, safe for production delivery." ) # 6. Execute via Sequential Flow Pipeline crew_system = Crew( agents=[specialist_agent, auditor_agent], tasks=[generation_task, review_task], process=Process.sequential, verbose=True ) return crew_system.kickoff()