File size: 3,876 Bytes
01543cd
f09f2a0
333cede
 
 
 
 
 
01543cd
 
e866313
333cede
 
 
01543cd
333cede
6e2e53b
d604f12
 
333cede
 
 
 
28408e1
6e2e53b
333cede
 
 
 
6e2e53b
 
01543cd
6e2e53b
 
 
 
df0f53d
6e2e53b
333cede
 
01543cd
 
 
 
 
 
df0f53d
01543cd
 
 
333cede
 
 
 
28408e1
 
 
 
333cede
d641174
6e2e53b
725d55d
6e2e53b
333cede
 
 
 
 
 
01543cd
 
 
333cede
 
 
 
 
 
 
 
 
8555bb7
 
 
01543cd
 
 
8555bb7
 
 
 
 
 
 
 
 
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
"""Pydantic models for API requests and responses."""
from dataclasses import dataclass
from typing import Optional

from pydantic import BaseModel, Field, ConfigDict


class QuestionInput(BaseModel):
    """Input model for a user question."""
    question: str = Field(..., min_length=1, max_length=500, description="Free-text question")
    similarity_threshold: Optional[float] = Field(
        None,
        ge=0.0,
        le=1.0,
        description="Desired cosine similarity threshold between 0.0 and 1.0.",
    )
    top_k: Optional[int] = None
    cross_encodeur_gap_threshold: Optional[float] = None
    cross_encodeur_confidence_threshold: Optional[float] = None

    model_config = ConfigDict(
        json_schema_extra={
            "example": {
                "question": "Comment je deviens membre ?",
                "threshold": 0.85
            }
        }
    )

@dataclass(slots=True)
class SemanticSearchCandidate:
    """Candidate produced by semantic search."""
    formulation: str
    theme: str
    response: str
    similarity_score: float
    cross_encoder_logit: float | None = None
    cross_encoder_score: float | None = None

class AnswerOutput(BaseModel):
    """Output model for a FAQ answer."""
    question: str = Field(..., description="User question")
    formulation: str = Field(..., description="FAQ formulation matched in the knowledge base")
    answer: str = Field(..., description="Answer found in the FAQ knowledge base")
    theme: str = Field(..., description="Answer theme or category")
    similarity_score: float = Field(..., ge=0.0, le=1.0, description="Cosine similarity score in [0, 1]")
    cross_encoder_logit: Optional[float] = Field(None, description="Cross-encoder logit score")
    cross_encoder_score: Optional[float] = Field(None, description="Cross-encoder score")
    confidence: bool = Field(..., description="True if the score meets the confidence threshold, otherwise False")
    list_candidates: Optional[list[SemanticSearchCandidate]] = Field(None, description="List of semantic search candidates")

    model_config = ConfigDict(
        json_schema_extra={
            "example": {
                "question": "Comment je deviens membre ?",
                "formulation": "Comment devenir membre de GOT ?",
                "answer": "Tu peux soit 'tinscrire, soit commencer ta candidature depuis le bouton « Inscription » du site. Tu renseigneras ton profil, tes compétences et les domaines qui t’intéressent afin que l’équipe puisse étudier ta candidature.",
                "theme": "comment_rejoindre_guild_open_tech",
                "similarity_score": 0.889,
                "cross_encoder_logit": 2.5,
                "cross_encoder_score": 0.95,
                "confidence": True,
                "list_candidates": []
            }
        }
    )


class HealthResponse(BaseModel):
    """Health-check response model."""
    status: str = Field(..., description="Service status")
    faq_count: int = Field(..., description="Number of FAQ entries in the knowledge base")

    model_config = ConfigDict(
        json_schema_extra={
            "example": {
                "status": "ok",
                "faq_count": 3
            }
        }
    )


class ConfigResponse(BaseModel):
    """Configuration response model."""
    embeddings_model_name: str = Field(..., description="Name of the embedding model used")
    cross_encoder_model_name: str = Field(..., description="Name of the cross-encoder model used")

    model_config = ConfigDict(
        json_schema_extra={
            "example": {
                "embeddings_model_name": "intfloat/multilingual-e5-small",
                "cross_encoder_model_name": "cross-encoder/ms-marco-MiniLM-L6-v2",
            }
        }
    )