File size: 3,040 Bytes
456aba5
 
 
 
56a777c
456aba5
56a777c
 
456aba5
 
 
 
 
 
56a777c
 
 
456aba5
 
 
 
 
 
 
 
56a777c
 
456aba5
 
feddcd9
456aba5
 
 
 
 
 
 
 
feddcd9
 
456aba5
56a777c
 
456aba5
56a777c
 
 
 
 
 
456aba5
 
 
 
 
 
 
56a777c
 
feddcd9
 
 
 
56a777c
feddcd9
56a777c
feddcd9
56a777c
feddcd9
56a777c
 
 
 
845e1f0
56a777c
 
845e1f0
 
56a777c
 
feddcd9
456aba5
 
 
845e1f0
56a777c
456aba5
56a777c
456aba5
56a777c
456aba5
56a777c
 
 
 
 
 
456aba5
56a777c
feddcd9
56a777c
 
456aba5
56a777c
456aba5
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
#!/usr/bin/env python3
# -*- coding: utf-8 -*-

"""
qa.py — QA + SUMMARY via Groq uniquement (pas de LLM local)

- Pas de fallback llama.cpp (trop lent).
- Si GROQ_API_KEY n'est pas défini, on lève une erreur explicite.
"""

from __future__ import annotations

import os
from dataclasses import dataclass
from typing import List, Dict

from src.resources import generate_chat, is_groq_enabled


# ==================== CONFIG ====================

@dataclass(frozen=True)
class QAConfig:
    qa_top_k_final: int = int(os.environ.get("QA_TOP_K_FINAL", "2"))
    qa_doc_max_chars: int = int(os.environ.get("QA_DOC_MAX_CHARS", "700"))
    qa_max_tokens: int = int(os.environ.get("QA_MAX_TOKENS", "220"))
    qa_temperature: float = float(os.environ.get("QA_TEMPERATURE", "0.1"))


# ==================== TEXT UTILS ====================

def truncate_text(s: str, n: int) -> str:
    if not s:
        return ""
    s = s.strip()
    return s if len(s) <= n else s[:n].rstrip() + "\n[...]\n"


# ==================== PROMPTS ====================

def build_qa_prompt_fast(question: str, context: str, sources: List[str]) -> str:
    src = ", ".join(sources) if sources else "N/A"
    return f"""Tu es un assistant juridique francophone. Tu aides à comprendre le Code de l'éducation (France).

RÈGLES STRICTES :
- Réponds uniquement en français.
- Appuie-toi en priorité sur le CONTEXTE fourni.
- Si l'information n'est pas dans le contexte, dis-le explicitement.
- Réponse courte et pratique (6 à 10 phrases maximum).
- Ne cite pas de sources externes (sites, lois non fournies, jurisprudence, etc.).

QUESTION :
{question}

CONTEXTE :
{context}

Termine par une ligne :
Sources (articles) : {src}
"""


def build_summary_prompt(article_id: str, article_text: str) -> str:
    return f"""Tu es un assistant juridique francophone.

LANGUE : réponds uniquement en français.

TÂCHE : résumer fidèlement un article du Code de l'éducation à partir du texte fourni.

RÈGLES STRICTES :
- N'invente rien. Si une information n'est pas dans le texte, ne l'ajoute pas.
- 4 puces maximum.
- 1 seule phrase courte par puce.
- Chaque puce ≤ 12 mots.
- Ne pas numéroter. Ne pas écrire "Puce 1", "Puce 2".
- Commence chaque ligne par "- ".
- 45 mots maximum au total.
- Ne rien écrire en dehors des puces.

ARTICLE {article_id} (texte / extraits fournis) :
{article_text}
"""



# ==================== GENERATION (GROQ ONLY) ====================

def llm_generate_qa(prompt: str, cfg: QAConfig | None = None) -> str:
    """
    Génération via Groq uniquement.
    """
    if not is_groq_enabled():
        raise RuntimeError(
            "Groq n'est pas configuré : variable GROQ_API_KEY manquante. "
            "Ajoute GROQ_API_KEY (et optionnellement GROQ_MODEL) dans l'environnement."
        )

    cfg = cfg or QAConfig()
    messages: List[Dict[str, str]] = [{"role": "user", "content": prompt}]

    return generate_chat(
        messages,
        max_tokens=cfg.qa_max_tokens,
        temperature=cfg.qa_temperature,
    )