codelion commited on
Commit
3bd40c8
·
verified ·
1 Parent(s): 009fe9e

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +56 -87
app.py CHANGED
@@ -69,110 +69,79 @@ def fetch_search_results(query, stream=False):
69
  return None, f"Error: {error_msg}"
70
 
71
  def stream_search_results(query, page):
72
- """Stream search results incrementally."""
73
  stream_response, error = fetch_search_results(query, stream=True)
74
  if error:
75
  yield f"<p style='color: red; text-align: center;'>{error}</p>"
76
  return
77
 
78
- # Generate header
79
- header = """
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
80
  <html>
81
  <head>
82
  <title>LLM Search Engine</title>
83
  <style>
84
- body { font-family: Arial, sans-serif; margin: 0; padding: 0; color: #202124; }
85
- .header { text-align: center; padding: 20px 0; }
86
- .logo { font-size: 36px; font-weight: bold; }
87
- .logo span:nth-child(1) { color: #4285f4; }
88
- .logo span:nth-child(2) { color: #ea4335; }
89
- .logo span:nth-child(3) { color: #fbbc05; }
90
- .logo span:nth-child(4) { color: #4285f4; }
91
- .logo span:nth-child(5) { color: #34a853; }
92
- .search-box { max-width: 584px; margin: 0 auto 20px; }
93
- .search-box input[type="text"] {
94
- width: 100%; padding: 12px 20px; font-size: 16px;
95
- border: 1px solid #dfe1e5; border-radius: 24px;
96
- box-shadow: 0 1px 6px rgba(32,33,36,0.28); outline: none;
97
- }
98
- .search-box input[type="submit"] {
99
- background-color: #f8f9fa; border: 1px solid #f8f9fa;
100
- border-radius: 4px; color: #3c4043; font-size: 14px;
101
- padding: 10px 16px; margin: 11px 4px; cursor: pointer;
102
- }
103
- .search-box input[type="submit"]:hover {
104
- border: 1px solid #dadce0; box-shadow: 0 1px 2px rgba(0,0,0,0.1);
105
- }
106
- .search-buttons { text-align: center; }
107
- .results { max-width: 652px; margin: 0 auto; }
108
- .search-result { margin-bottom: 28px; }
109
- .search-result a { color: #1a0dab; font-size: 20px; text-decoration: none; }
110
- .search-result a:hover { text-decoration: underline; }
111
- .search-result .url { color: #006621; font-size: 14px; line-height: 20px; }
112
- .search-result p { color: #4d5156; font-size: 14px; line-height: 22px; margin: 0; }
113
- .pagination { text-align: center; margin: 40px 0; }
114
- .pagination a, .pagination span {
115
  color: #1a0dab; font-size: 14px; margin: 0 8px; text-decoration: none;
116
- }
117
- .pagination a:hover { text-decoration: underline; }
118
  </style>
119
  </head>
120
  <body>
121
- <div class="header">
122
- <div class="logo">
123
- <span>L</span><span>L</span><span>M</span><span> </span><span>Search</span>
124
- </div>
125
- </div>
126
- <div class="search-box">
127
- <form method="get" action="/">
128
- <input type="text" name="query" value="{{query}}">
129
- <input type="hidden" name="page" value="1">
130
- <div class="search-buttons">
131
- <input type="submit" name="btn" value="LLM Search">
132
- <input type="submit" name="btn" value="I'm Feeling Lucky">
133
- </div>
134
- </form>
135
- </div>
136
  <div class="results">
137
- <h2 style="font-size: 18px; color: #70757a; margin-bottom: 20px;">Results for '{{query}}' (Page {{page}})</h2>
138
- """.replace('{{query}}', html.escape(query)).replace('{{page}}', str(page))
139
  yield header
140
 
141
- # Stream and parse results
142
- buffer = ""
143
- results = []
144
- for chunk in stream_response:
145
- if chunk.choices[0].delta.content:
146
- buffer += chunk.choices[0].delta.content
147
- try:
148
- # Try to parse the buffer as JSON incrementally
149
- temp_results = json.loads(buffer)
150
- if isinstance(temp_results, list):
151
- results = temp_results
152
- elif isinstance(temp_results, dict) and "results" in temp_results:
153
- results = temp_results["results"]
154
- # Process only new results
155
- for i, result in enumerate(results[len(results) - (len(results) % RESULTS_PER_PAGE):]):
156
- if i >= start_idx and i < end_idx:
157
- title = html.escape(result.get("title", "No title"))
158
- snippet = html.escape(result.get("snippet", "No snippet"))
159
- url = html.escape(result.get("url", "#"))
160
- yield f"""
161
- <div class="search-result">
162
- <a href="{url}" target="_blank">{title}</a>
163
- <div class="url">{url}</div>
164
- <p>{snippet}</p>
165
- </div>
166
- """
167
- except json.JSONDecodeError:
168
- continue # Keep buffering until complete JSON
169
-
170
- # Calculate pagination
171
- start_idx = (page - 1) * RESULTS_PER_PAGE
172
- end_idx = start_idx + RESULTS_PER_PAGE
173
- total_pages = (len(results) + RESULTS_PER_PAGE - 1) // RESULTS_PER_PAGE
174
 
175
- # Pagination links
 
176
  encoded_query = quote(query)
177
  prev_link = f'<a href="/?query={encoded_query}&page={page-1}&btn=LLM+Search">Previous</a>' if page > 1 else '<span>Previous</span>'
178
  next_link = f'<a href="/?query={encoded_query}&page={page+1}&btn=LLM+Search">Next</a>' if page < total_pages else '<span>Next</span>'
 
69
  return None, f"Error: {error_msg}"
70
 
71
  def stream_search_results(query, page):
72
+ """Stream search results incrementally after accumulating the full response."""
73
  stream_response, error = fetch_search_results(query, stream=True)
74
  if error:
75
  yield f"<p style='color: red; text-align: center;'>{error}</p>"
76
  return
77
 
78
+ # Step 1: Accumulate the full response
79
+ buffer = ""
80
+ for chunk in stream_response:
81
+ if chunk.choices[0].delta.content:
82
+ buffer += chunk.choices[0].delta.content
83
+
84
+ # Step 2: Parse the full JSON response
85
+ try:
86
+ results = json.loads(buffer)
87
+ if isinstance(results, dict) and "results" in results:
88
+ results = results["results"]
89
+ elif not isinstance(results, list):
90
+ yield "<p style='color: red; text-align: center;'>Error: Unexpected JSON structure.</p>"
91
+ return
92
+ except json.JSONDecodeError:
93
+ yield "<p style='color: red; text-align: center;'>Error: Failed to parse JSON response.</p>"
94
+ return
95
+
96
+ # Step 3: Calculate pagination
97
+ RESULTS_PER_PAGE = 10 # Adjust this as needed
98
+ start_idx = (page - 1) * RESULTS_PER_PAGE
99
+ end_idx = min(start_idx + RESULTS_PER_PAGE, len(results))
100
+ total_pages = (len(results) + RESULTS_PER_PAGE - 1) // RESULTS_PER_PAGE
101
+
102
+ # Step 4: Generate and yield the header
103
+ header = f"""
104
  <html>
105
  <head>
106
  <title>LLM Search Engine</title>
107
  <style>
108
+ body {{ font-family: Arial, sans-serif; margin: 0; padding: 0; color: #202124; }}
109
+ .results {{ max-width: 652px; margin: 0 auto; }}
110
+ .search-result {{ margin-bottom: 28px; }}
111
+ .search-result a {{ color: #1a0dab; font-size: 20px; text-decoration: none; }}
112
+ .search-result a:hover {{ text-decoration: underline; }}
113
+ .search-result .url {{ color: #006621; font-size: 14px; line-height: 20px; }}
114
+ .search-result p {{ color: #4d5156; font-size: 14px; line-height: 22px; margin: 0; }}
115
+ .pagination {{ text-align: center; margin: 40px 0; }}
116
+ .pagination a, .pagination span {{
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
117
  color: #1a0dab; font-size: 14px; margin: 0 8px; text-decoration: none;
118
+ }}
119
+ .pagination a:hover {{ text-decoration: underline; }}
120
  </style>
121
  </head>
122
  <body>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
123
  <div class="results">
124
+ <h2 style="font-size: 18px; color: #70757a; margin-bottom: 20px;">Results for '{query}' (Page {page} of {total_pages})</h2>
125
+ """
126
  yield header
127
 
128
+ # Step 5: Yield each result for the current page
129
+ for i in range(start_idx, end_idx):
130
+ if i < len(results):
131
+ result = results[i]
132
+ title = result.get("title", "No title")
133
+ snippet = result.get("snippet", "No snippet")
134
+ url = result.get("url", "#")
135
+ yield f"""
136
+ <div class="search-result">
137
+ <a href="{url}" target="_blank">{title}</a>
138
+ <div class="url">{url}</div>
139
+ <p>{snippet}</p>
140
+ </div>
141
+ """
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
142
 
143
+ # Step 6: Yield pagination links
144
+ from urllib.parse import quote
145
  encoded_query = quote(query)
146
  prev_link = f'<a href="/?query={encoded_query}&page={page-1}&btn=LLM+Search">Previous</a>' if page > 1 else '<span>Previous</span>'
147
  next_link = f'<a href="/?query={encoded_query}&page={page+1}&btn=LLM+Search">Next</a>' if page < total_pages else '<span>Next</span>'