File size: 6,645 Bytes
9d76e23
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
"""
This file defines the data structures (schemas) for the recommender system's API using Pydantic.
It ensures that data exchanged with the API (requests and responses) adheres to a specific format,
providing benefits like automatic data validation, serialization/deserialization,
and clear API documentation.
"""
from pydantic import BaseModel, Field, validator
from typing import List, Optional, Any, Union
from src.config.settings import DEFAULT_K # For default 'k' value

class MsgPayload(BaseModel):
    """
    Represents the payload for a message.
    """
    msg_id: int  # Unique identifier for the message.
    msg_name: str  # Name or description of the message.

class RecommendationRequest(BaseModel):
    """
    Defines the structure for a recommendation request.
    """
    user_id: Optional[str] = None  # Optional unique identifier for the user making the request.
    query: str = Field(..., description="The search query or input text for which recommendations are sought.")
    k: Optional[int] = Field(default=DEFAULT_K, description=f"The number of recommendations to return, defaults to {DEFAULT_K}.")

class UserItemRecommendationRequest(BaseModel):
    """
    Defines the structure for an item-based recommendation request.
    """
    id: str = Field(..., description="The item ID (msid) to get recommendations for.")
    user_id: Optional[str] = Field(None, description="Optional user ID. If not provided, an anonymous ID will be generated.")
    k: Optional[int] = Field(default=DEFAULT_K, description=f"Number of recommendations to return, defaults to {DEFAULT_K}.")

class RetrievedDocument(BaseModel):
    """
    Represents a single document retrieved as part of a recommendation.
    """
    id: str  # Unique identifier for the retrieved document (msid, typically a string).
    hl: str  # Headline of the document.
    synopsis: Optional[str] = None # Synopsis or main content of the document.
    keywords: Optional[str] = None # Keywords associated with the document.
    type: Optional[str] = None  # Optional type or category of the document.
    taxonomy: Optional[List[str]] = None # List of taxonomy terms.
    score: Optional[float] = None # Relevance score of the document.
    seolocation: Optional[str] = None # Optional SEO location or URL.
    dl: Optional[str] = None # Optional deeplink.
    lu: Optional[str] = None # Optional last updated timestamp or string.
    imageid: Optional[str] = None # Optional image identifier.
    imgratio: Optional[str] = None # Optional image ratio (e.g., "16:9").
    imgsize: Optional[str] = None # Optional image size (e.g., "1024x768").
    # summary and smart_tip fields removed as they should only be in summary endpoint response

# Create a new model for documents with summary and smart tip
class RetrievedDocumentWithSummary(RetrievedDocument):
    """
    Represents a document retrieved as part of a recommendation with additional summary and smart tip fields.
    """
    summary: Optional[str] = "" # Generated summary of the article, defaults to empty string
    smart_tip: Optional[Any] = None # Smart tip with related articles and summaries.

class SmartTipSuggestion(BaseModel):
    label: str
    url: str
    # summary: Optional[str] = "" # Removed as per request

class SmartTip(BaseModel):
    title: str
    description: str
    suggestions: List[SmartTipSuggestion] = Field(default_factory=list)

class PastFeedbackItem(BaseModel):
    srcMsid: Optional[str] = ""
    clickedMsid: Optional[str] = ""
    resultMsids: List[str] = Field(default_factory=list)
    # timestamp: Optional[datetime] = None # Optional: if you want to include timestamp

class RecommendationResponse(BaseModel):
    """
    Defines the structure for a recommendation response.
    """
    msid: Optional[str] = None # Made optional to handle cases where it might not be set (e.g. query-based recs)
    retrieved_documents: List[RetrievedDocument] = Field(default_factory=list) # Use RetrievedDocument
    generated_response: Optional[str] = None # Added to match recommender output
    clicked_msid: Optional[Union[str, List[Any]]] = None # To match existing logic in routes.py
    past_feedback: List[PastFeedbackItem] = Field(default_factory=list)

    @validator('retrieved_documents', pre=True, each_item=True)
    def ensure_smart_tip_format(cls, v):
        if isinstance(v, dict) and 'smart_tip' in v and isinstance(v['smart_tip'], str): # Compatibility if old format string is somehow passed
            v['smart_tip'] = None # Or convert to new format if possible, for now, nullify
        return v
    # ...other fields if present...

class OutputData(BaseModel):
    """
    Represents a single data item in the output, often a simplified version of a retrieved document.
    """
    id: str  # Unique identifier for the output data item (msid, typically a string).
    headline: Optional[str] = None  # Optional headline or title for the data item.
    type: Optional[str] = None  # Optional type or category of the data item.

class OutputResponse(BaseModel):
    """
    Defines the structure for a generic output response containing a list of data items.
    """
    data: List[OutputData]  # A list of output data items.

class FeedbackRecommendationRequest(BaseModel):
    user_id: str
    msid: str
    clicked_msid: Optional[str] = None
    k: Optional[int] = 5

# Refine RetrievedDocument.smart_tip type
RetrievedDocument.model_fields['smart_tip'] = Field(default=None, description="Smart tip with related articles and summaries.")
RetrievedDocument.model_rebuild(force=True)

# Update the RecommendationResponse model to use RetrievedDocumentWithSummary for summary endpoint
class RecommendationResponseWithSummary(BaseModel):
    """
    Defines the structure for a recommendation response with summaries and smart tips.
    """
    msid: Optional[str] = None # Made optional to handle cases where it might not be set (e.g. query-based recs)
    retrieved_documents: List[RetrievedDocumentWithSummary] = Field(default_factory=list)
    generated_response: Optional[str] = None # Added to match recommender output
    clicked_msid: Optional[Union[str, List[Any]]] = None # To match existing logic in routes.py
    past_feedback: List[PastFeedbackItem] = Field(default_factory=list)

    @validator('retrieved_documents', pre=True, each_item=True)
    def ensure_smart_tip_format(cls, v):
        if isinstance(v, dict) and 'smart_tip' in v and isinstance(v['smart_tip'], str): # Compatibility if old format string is somehow passed
            v['smart_tip'] = None # Or convert to new format if possible, for now, nullify
        return v