Spaces:
Sleeping
Sleeping
| from __future__ import annotations | |
| import os | |
| from collections.abc import Iterable | |
| from dotenv import load_dotenv | |
| from pydantic_ai import Agent | |
| from pydantic_ai.models.groq import GroqModel | |
| from backend.document_store import store | |
| load_dotenv() | |
| SYSTEM_PROMPT = ( | |
| "Tu es Hianatra, un assistant pédagogique. " | |
| "Réponds uniquement à partir du contexte fourni. " | |
| "Si la réponse n'est pas dans le contexte, dis-le clairement." | |
| ) | |
| FREE_GROQ_MODELS = [ | |
| "llama-3.1-8b-instant", | |
| "llama-3.3-70b-versatile", | |
| "qwen/qwen3-32b", | |
| ] | |
| def _get_groq_model_name() -> str: | |
| return os.getenv("GROQ_MODEL", "llama-3.3-70b-versatile") | |
| def _dedupe(values: Iterable[str]) -> list[str]: | |
| seen: set[str] = set() | |
| out: list[str] = [] | |
| for value in values: | |
| if value in seen: | |
| continue | |
| seen.add(value) | |
| out.append(value) | |
| return out | |
| def get_available_models() -> list[str]: | |
| configured = os.getenv("GROQ_FREE_MODELS", "") | |
| configured_list = [m.strip() for m in configured.split(",") if m.strip()] | |
| return _dedupe([_get_groq_model_name(), *configured_list, *FREE_GROQ_MODELS]) | |
| def get_default_model() -> str: | |
| return _get_groq_model_name() | |
| def _build_model_settings( | |
| temperature: float | None, | |
| top_p: float | None, | |
| max_tokens: int | None, | |
| ) -> dict[str, float | int]: | |
| settings: dict[str, float | int] = {} | |
| if temperature is not None: | |
| settings["temperature"] = float(max(0.0, min(2.0, temperature))) | |
| if top_p is not None: | |
| settings["top_p"] = float(max(0.0, min(1.0, top_p))) | |
| if max_tokens is not None: | |
| settings["max_tokens"] = int(max(64, min(4096, max_tokens))) | |
| return settings | |
| def _build_agent(model_name: str) -> Agent: | |
| return Agent( | |
| GroqModel(model_name), | |
| system_prompt=SYSTEM_PROMPT, | |
| ) | |
| agent = _build_agent(_get_groq_model_name()) | |
| def _extract_agent_text(result: object) -> str: | |
| """Compat pydantic-ai: .data (ancien) -> .output (récent).""" | |
| for attr in ("output", "data", "result"): | |
| if hasattr(result, attr): | |
| value = getattr(result, attr) | |
| if value is None: | |
| continue | |
| return value if isinstance(value, str) else str(value) | |
| return str(result) | |
| async def ask_with_rag( | |
| question: str, | |
| k: int = 4, | |
| model_name: str | None = None, | |
| temperature: float | None = 0.2, | |
| top_p: float | None = 0.95, | |
| max_tokens: int | None = 1024, | |
| ) -> tuple[str, list[str], str]: | |
| relevant_chunks = store.search(question, k=k) | |
| if not relevant_chunks: | |
| return ( | |
| "Aucun document chargé. Veuillez uploader un document d'abord.", | |
| [], | |
| model_name or _get_groq_model_name(), | |
| ) | |
| context = "\n\n---\n\n".join(relevant_chunks) | |
| prompt = ( | |
| f"Contexte du document :\n{context}\n\n" | |
| f"Question : {question}\n\n" | |
| "Réponds de manière claire et précise en te basant sur le contexte ci-dessus." | |
| ) | |
| selected_model = (model_name or _get_groq_model_name()).strip() | |
| selected_agent = agent if selected_model == _get_groq_model_name() else _build_agent(selected_model) | |
| settings = _build_model_settings(temperature, top_p, max_tokens) | |
| if settings: | |
| try: | |
| result = await selected_agent.run(prompt, model_settings=settings) | |
| return _extract_agent_text(result), relevant_chunks, selected_model | |
| except TypeError: | |
| # Compat anciennes versions de pydantic-ai | |
| pass | |
| result = await selected_agent.run(prompt) | |
| return _extract_agent_text(result), relevant_chunks, selected_model | |