models.py
 1  """Data models for web search results."""
 2  
 3  from typing import List, Optional, Dict, Any
 4  from pydantic import BaseModel, Field, HttpUrl
 5  
 6  
 7  class SearchSource(BaseModel):
 8      """Individual search result source."""
 9      
10      link: str = Field(..., description="URL of the source")
11      title: str = Field(..., description="Title of the source")
12      snippet: str = Field(..., description="Text snippet from the source")
13      attribution: Optional[str] = Field(None, description="Attribution/domain name")
14      imageUrl: Optional[str] = Field(None, description="Optional image URL")
15      processed: bool = Field(default=False, description="Whether source has been cited")
16      
17      class Config:
18          json_schema_extra = {
19              "example": {
20                  "link": "https://example.com/article",
21                  "title": "Example Article",
22                  "snippet": "This is an example snippet from the article...",
23                  "attribution": "example.com",
24                  "processed": False
25              }
26          }
27  
28  
29  class ImageResult(BaseModel):
30      """Image search result."""
31      
32      imageUrl: str = Field(..., description="URL of the image")
33      title: Optional[str] = Field(None, description="Title or description of the image")
34      link: str = Field(..., description="Source page URL")
35      
36      class Config:
37          json_schema_extra = {
38              "example": {
39                  "imageUrl": "https://example.com/image.jpg",
40                  "title": "Example Image",
41                  "link": "https://example.com/page"
42              }
43          }
44  
45  
46  class SearchResult(BaseModel):
47      """Complete search result from a search provider."""
48      
49      success: bool = Field(..., description="Whether the search was successful")
50      query: str = Field(..., description="The search query")
51      organic: List[SearchSource] = Field(default_factory=list, description="Organic search results")
52      topStories: List[SearchSource] = Field(default_factory=list, description="Top news stories")
53      images: List[ImageResult] = Field(default_factory=list, description="Image results")
54      answerBox: Optional[str] = Field(None, description="Direct answer if available")
55      error: Optional[str] = Field(None, description="Error message if search failed")
56      metadata: Dict[str, Any] = Field(default_factory=dict, description="Additional metadata")
57      
58      class Config:
59          json_schema_extra = {
60              "example": {
61                  "success": True,
62                  "query": "latest AI developments",
63                  "organic": [
64                      {
65                          "link": "https://example.com/ai-news",
66                          "title": "Latest AI Developments",
67                          "snippet": "Recent advances in artificial intelligence...",
68                          "attribution": "example.com"
69                      }
70                  ],
71                  "topStories": [],
72                  "images": [],
73                  "answerBox": None,
74                  "error": None
75              }
76          }
77  
78  
79  class SearchResultData(BaseModel):
80      """Search result data with turn information for citations."""
81      
82      turn: int = Field(..., description="Search turn number (0-based)")
83      organic: List[SearchSource] = Field(default_factory=list)
84      topStories: List[SearchSource] = Field(default_factory=list)
85      images: List[ImageResult] = Field(default_factory=list)
86      references: List[Dict[str, Any]] = Field(default_factory=list, description="Additional references")
87      answerBox: Optional[str] = None
88      
89      class Config:
90          json_schema_extra = {
91              "example": {
92                  "turn": 0,
93                  "organic": [],
94                  "topStories": [],
95                  "images": [],
96                  "references": [],
97                  "answerBox": None
98              }
99          }