Spaces:
Running
on
T4
Running
on
T4
thomasht86
commited on
Upload folder using huggingface_hub
Browse files- backend/vespa_app.py +34 -1
- frontend/app.py +28 -2
- globals.css +56 -0
- main.py +24 -2
- output.css +67 -0
backend/vespa_app.py
CHANGED
@@ -1,6 +1,6 @@
|
|
1 |
import os
|
2 |
import time
|
3 |
-
from typing import
|
4 |
|
5 |
import numpy as np
|
6 |
import torch
|
@@ -276,6 +276,39 @@ class VespaQueryClient:
|
|
276 |
)
|
277 |
return response.json["root"]["children"][0]["fields"]["full_image"]
|
278 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
279 |
async def query_vespa_nearest_neighbor(
|
280 |
self,
|
281 |
query: str,
|
|
|
1 |
import os
|
2 |
import time
|
3 |
+
from typing import Any, Dict, Tuple
|
4 |
|
5 |
import numpy as np
|
6 |
import torch
|
|
|
276 |
)
|
277 |
return response.json["root"]["children"][0]["fields"]["full_image"]
|
278 |
|
279 |
+
async def get_suggestions(self, query: str) -> list:
|
280 |
+
async with self.app.asyncio(connections=1) as session:
|
281 |
+
start = time.perf_counter()
|
282 |
+
yql = f'select questions from {self.VESPA_SCHEMA_NAME} where questions matches "{query}" limit 3'
|
283 |
+
print(yql)
|
284 |
+
response: VespaQueryResponse = await session.query(
|
285 |
+
body={
|
286 |
+
"yql": yql,
|
287 |
+
"ranking": "unranked",
|
288 |
+
"presentation.timing": True,
|
289 |
+
},
|
290 |
+
)
|
291 |
+
assert response.is_successful(), response.json
|
292 |
+
stop = time.perf_counter()
|
293 |
+
print(
|
294 |
+
f"Getting suggestions from Vespa took: {stop - start} s, Vespa reported searchtime was "
|
295 |
+
f"{response.json.get('timing', {}).get('searchtime', -1)} s"
|
296 |
+
)
|
297 |
+
search_results = (
|
298 |
+
response.json["root"]["children"]
|
299 |
+
if "root" in response.json and "children" in response.json["root"]
|
300 |
+
else []
|
301 |
+
)
|
302 |
+
print(response.json)
|
303 |
+
|
304 |
+
questions = [
|
305 |
+
result["fields"]["questions"]
|
306 |
+
for result in search_results
|
307 |
+
if "questions" in result["fields"]
|
308 |
+
]
|
309 |
+
flat_questions = [item for sublist in questions for item in sublist]
|
310 |
+
return flat_questions
|
311 |
+
|
312 |
async def query_vespa_nearest_neighbor(
|
313 |
self,
|
314 |
query: str,
|
frontend/app.py
CHANGED
@@ -84,6 +84,28 @@ toggle_text_content = Script(
|
|
84 |
"""
|
85 |
)
|
86 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
87 |
|
88 |
def SearchBox(with_border=False, query_value="", ranking_value="nn+colpali"):
|
89 |
grid_cls = "grid gap-2 items-center p-3 bg-muted/80 dark:bg-muted/40 w-full"
|
@@ -93,13 +115,16 @@ def SearchBox(with_border=False, query_value="", ranking_value="nn+colpali"):
|
|
93 |
|
94 |
return Form(
|
95 |
Div(
|
96 |
-
Lucide(
|
|
|
|
|
97 |
Input(
|
98 |
placeholder="Enter your search query...",
|
99 |
name="query",
|
100 |
value=query_value,
|
101 |
id="search-input",
|
102 |
-
cls="text-base pl-10 border-transparent ring-offset-transparent ring-0 focus-visible:ring-transparent",
|
|
|
103 |
style="font-size: 1rem",
|
104 |
autofocus=True,
|
105 |
),
|
@@ -140,6 +165,7 @@ def SearchBox(with_border=False, query_value="", ranking_value="nn+colpali"):
|
|
140 |
cls="flex justify-between",
|
141 |
),
|
142 |
check_input_script,
|
|
|
143 |
action=f"/search?query={quote_plus(query_value)}&ranking={quote_plus(ranking_value)}",
|
144 |
method="GET",
|
145 |
hx_get=f"/fetch_results?query={quote_plus(query_value)}&ranking={quote_plus(ranking_value)}",
|
|
|
84 |
"""
|
85 |
)
|
86 |
|
87 |
+
autocomplete_script = Script(
|
88 |
+
"""
|
89 |
+
document.addEventListener('DOMContentLoaded', function() {
|
90 |
+
const input = document.querySelector('#search-input');
|
91 |
+
const awesomplete = new Awesomplete(input, { minChars: 1, maxItems: 5 });
|
92 |
+
|
93 |
+
input.addEventListener('input', function() {
|
94 |
+
if (this.value.length >= 1) {
|
95 |
+
// Use template literals to insert the input value dynamically in the query parameter
|
96 |
+
fetch(`/suggestions?query=${encodeURIComponent(this.value)}`)
|
97 |
+
.then(response => response.json())
|
98 |
+
.then(data => {
|
99 |
+
// Update the Awesomplete list dynamically with fetched suggestions
|
100 |
+
awesomplete.list = data.suggestions;
|
101 |
+
})
|
102 |
+
.catch(err => console.error('Error fetching suggestions:', err));
|
103 |
+
}
|
104 |
+
});
|
105 |
+
});
|
106 |
+
"""
|
107 |
+
)
|
108 |
+
|
109 |
|
110 |
def SearchBox(with_border=False, query_value="", ranking_value="nn+colpali"):
|
111 |
grid_cls = "grid gap-2 items-center p-3 bg-muted/80 dark:bg-muted/40 w-full"
|
|
|
115 |
|
116 |
return Form(
|
117 |
Div(
|
118 |
+
Lucide(
|
119 |
+
icon="search", cls="absolute left-2 top-2 text-muted-foreground z-10"
|
120 |
+
),
|
121 |
Input(
|
122 |
placeholder="Enter your search query...",
|
123 |
name="query",
|
124 |
value=query_value,
|
125 |
id="search-input",
|
126 |
+
cls="text-base pl-10 border-transparent ring-offset-transparent ring-0 focus-visible:ring-transparent awesomplete",
|
127 |
+
data_list="#suggestions",
|
128 |
style="font-size: 1rem",
|
129 |
autofocus=True,
|
130 |
),
|
|
|
165 |
cls="flex justify-between",
|
166 |
),
|
167 |
check_input_script,
|
168 |
+
autocomplete_script,
|
169 |
action=f"/search?query={quote_plus(query_value)}&ranking={quote_plus(ranking_value)}",
|
170 |
method="GET",
|
171 |
hx_get=f"/fetch_results?query={quote_plus(query_value)}&ranking={quote_plus(ranking_value)}",
|
globals.css
CHANGED
@@ -217,3 +217,59 @@ aside {
|
|
217 |
.md-grid-text-column {
|
218 |
@apply md:grid md:grid-rows-subgrid md:row-span-2 md:content-start;
|
219 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
217 |
.md-grid-text-column {
|
218 |
@apply md:grid md:grid-rows-subgrid md:row-span-2 md:content-start;
|
219 |
}
|
220 |
+
|
221 |
+
#search-input[aria-expanded="true"] {
|
222 |
+
border-top: 1px solid hsl(var(--input));
|
223 |
+
border-left: 1px solid hsl(var(--input));
|
224 |
+
border-right: 1px solid hsl(var(--input));
|
225 |
+
border-bottom: none;
|
226 |
+
border-bottom-left-radius: 0;
|
227 |
+
border-bottom-right-radius: 0;
|
228 |
+
}
|
229 |
+
|
230 |
+
.awesomplete {
|
231 |
+
width: 100%;
|
232 |
+
}
|
233 |
+
|
234 |
+
.awesomplete > ul {
|
235 |
+
@apply text-sm space-y-0.5;
|
236 |
+
margin: 0;
|
237 |
+
border-top: none;
|
238 |
+
border-left: 1px solid hsl(var(--input));
|
239 |
+
border-right: 1px solid hsl(var(--input));
|
240 |
+
border-bottom: 1px solid hsl(var(--input));
|
241 |
+
border-radius: 0 0 calc(var(--radius) - 2px) calc(var(--radius) - 2px);
|
242 |
+
background: hsl(var(--background));
|
243 |
+
box-shadow: none;
|
244 |
+
text-shadow: none;
|
245 |
+
}
|
246 |
+
|
247 |
+
.awesomplete > ul:before {
|
248 |
+
display: none;
|
249 |
+
}
|
250 |
+
|
251 |
+
.awesomplete > ul > li:hover {
|
252 |
+
background-color: #B7E2F1;
|
253 |
+
color: #2E2F27;
|
254 |
+
}
|
255 |
+
|
256 |
+
.awesomplete > ul > li[aria-selected="true"] {
|
257 |
+
background-color: #B7E2F1;
|
258 |
+
color: #2E2F27;
|
259 |
+
}
|
260 |
+
|
261 |
+
.awesomplete mark {
|
262 |
+
background-color: #61D790;
|
263 |
+
color: #2E2F27;
|
264 |
+
}
|
265 |
+
|
266 |
+
.awesomplete li:hover mark {
|
267 |
+
background-color: #61D790;
|
268 |
+
color: #2E2F27;
|
269 |
+
}
|
270 |
+
|
271 |
+
.awesomplete li[aria-selected="true"] mark {
|
272 |
+
background-color: #61D790;
|
273 |
+
color: #2E2F27;
|
274 |
+
}
|
275 |
+
|
main.py
CHANGED
@@ -49,19 +49,29 @@ overlayscrollbars_link = Link(
|
|
49 |
overlayscrollbars_js = Script(
|
50 |
src="https://cdnjs.cloudflare.com/ajax/libs/overlayscrollbars/2.10.0/browser/overlayscrollbars.browser.es5.min.js"
|
51 |
)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
52 |
sselink = Script(src="https://unpkg.com/htmx-ext-sse@2.2.1/sse.js")
|
53 |
|
54 |
app, rt = fast_app(
|
55 |
htmlkw={"cls": "grid h-full"},
|
56 |
pico=False,
|
57 |
hdrs=(
|
58 |
-
ShadHead(tw_cdn=False, theme_handle=True),
|
59 |
highlight_js,
|
60 |
highlight_js_theme_link,
|
61 |
highlight_js_theme,
|
62 |
overlayscrollbars_link,
|
63 |
overlayscrollbars_js,
|
|
|
|
|
64 |
sselink,
|
|
|
65 |
),
|
66 |
)
|
67 |
vespa_app: Vespa = VespaQueryClient()
|
@@ -85,7 +95,7 @@ gemini_model = genai.GenerativeModel(
|
|
85 |
)
|
86 |
STATIC_DIR = Path(__file__).parent / "static"
|
87 |
IMG_DIR = STATIC_DIR / "saved"
|
88 |
-
os.makedirs(
|
89 |
|
90 |
|
91 |
@app.on_event("startup")
|
@@ -310,6 +320,18 @@ async def full_image(docid: str, query_id: str, idx: int):
|
|
310 |
)
|
311 |
|
312 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
313 |
async def message_generator(query_id: str, query: str):
|
314 |
images = []
|
315 |
result = None
|
|
|
49 |
overlayscrollbars_js = Script(
|
50 |
src="https://cdnjs.cloudflare.com/ajax/libs/overlayscrollbars/2.10.0/browser/overlayscrollbars.browser.es5.min.js"
|
51 |
)
|
52 |
+
awesomplete_link = Link(
|
53 |
+
rel="stylesheet",
|
54 |
+
href="https://cdnjs.cloudflare.com/ajax/libs/awesomplete/1.1.7/awesomplete.min.css",
|
55 |
+
type="text/css",
|
56 |
+
)
|
57 |
+
awesomplete_js = Script(
|
58 |
+
src="https://cdnjs.cloudflare.com/ajax/libs/awesomplete/1.1.7/awesomplete.min.js"
|
59 |
+
)
|
60 |
sselink = Script(src="https://unpkg.com/htmx-ext-sse@2.2.1/sse.js")
|
61 |
|
62 |
app, rt = fast_app(
|
63 |
htmlkw={"cls": "grid h-full"},
|
64 |
pico=False,
|
65 |
hdrs=(
|
|
|
66 |
highlight_js,
|
67 |
highlight_js_theme_link,
|
68 |
highlight_js_theme,
|
69 |
overlayscrollbars_link,
|
70 |
overlayscrollbars_js,
|
71 |
+
awesomplete_link,
|
72 |
+
awesomplete_js,
|
73 |
sselink,
|
74 |
+
ShadHead(tw_cdn=False, theme_handle=True),
|
75 |
),
|
76 |
)
|
77 |
vespa_app: Vespa = VespaQueryClient()
|
|
|
95 |
)
|
96 |
STATIC_DIR = Path(__file__).parent / "static"
|
97 |
IMG_DIR = STATIC_DIR / "saved"
|
98 |
+
os.makedirs(IMG_DIR, exist_ok=True)
|
99 |
|
100 |
|
101 |
@app.on_event("startup")
|
|
|
320 |
)
|
321 |
|
322 |
|
323 |
+
@rt("/suggestions")
|
324 |
+
async def get_suggestions(request):
|
325 |
+
query = request.query_params.get("query", "").lower().strip()
|
326 |
+
|
327 |
+
if query:
|
328 |
+
suggestions = await vespa_app.get_suggestions(query)
|
329 |
+
if len(suggestions) > 0:
|
330 |
+
return JSONResponse({"suggestions": suggestions})
|
331 |
+
|
332 |
+
return JSONResponse({"suggestions": []})
|
333 |
+
|
334 |
+
|
335 |
async def message_generator(query_id: str, query: str):
|
336 |
images = []
|
337 |
result = None
|
output.css
CHANGED
@@ -758,6 +758,10 @@ body {
|
|
758 |
top: 50%;
|
759 |
}
|
760 |
|
|
|
|
|
|
|
|
|
761 |
.z-50 {
|
762 |
z-index: 50;
|
763 |
}
|
@@ -2077,6 +2081,68 @@ aside {
|
|
2077 |
}
|
2078 |
}
|
2079 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2080 |
:root:has(.data-\[state\=open\]\:no-bg-scroll[data-state="open"]) {
|
2081 |
overflow: hidden;
|
2082 |
}
|
@@ -2695,3 +2761,4 @@ aside {
|
|
2695 |
.\[\&_tr\]\:border-b tr {
|
2696 |
border-bottom-width: 1px;
|
2697 |
}
|
|
|
|
758 |
top: 50%;
|
759 |
}
|
760 |
|
761 |
+
.z-10 {
|
762 |
+
z-index: 10;
|
763 |
+
}
|
764 |
+
|
765 |
.z-50 {
|
766 |
z-index: 50;
|
767 |
}
|
|
|
2081 |
}
|
2082 |
}
|
2083 |
|
2084 |
+
#search-input[aria-expanded="true"] {
|
2085 |
+
border-top: 1px solid hsl(var(--input));
|
2086 |
+
border-left: 1px solid hsl(var(--input));
|
2087 |
+
border-right: 1px solid hsl(var(--input));
|
2088 |
+
border-bottom: none;
|
2089 |
+
border-bottom-left-radius: 0;
|
2090 |
+
border-bottom-right-radius: 0;
|
2091 |
+
}
|
2092 |
+
|
2093 |
+
.awesomplete {
|
2094 |
+
width: 100%;
|
2095 |
+
}
|
2096 |
+
|
2097 |
+
.awesomplete > ul > :not([hidden]) ~ :not([hidden]) {
|
2098 |
+
--tw-space-y-reverse: 0;
|
2099 |
+
margin-top: calc(0.125rem * calc(1 - var(--tw-space-y-reverse)));
|
2100 |
+
margin-bottom: calc(0.125rem * var(--tw-space-y-reverse));
|
2101 |
+
}
|
2102 |
+
|
2103 |
+
.awesomplete > ul {
|
2104 |
+
font-size: 0.875rem;
|
2105 |
+
line-height: 1.25rem;
|
2106 |
+
margin: 0;
|
2107 |
+
border-top: none;
|
2108 |
+
border-left: 1px solid hsl(var(--input));
|
2109 |
+
border-right: 1px solid hsl(var(--input));
|
2110 |
+
border-bottom: 1px solid hsl(var(--input));
|
2111 |
+
border-radius: 0 0 calc(var(--radius) - 2px) calc(var(--radius) - 2px);
|
2112 |
+
background: hsl(var(--background));
|
2113 |
+
box-shadow: none;
|
2114 |
+
text-shadow: none;
|
2115 |
+
}
|
2116 |
+
|
2117 |
+
.awesomplete > ul:before {
|
2118 |
+
display: none;
|
2119 |
+
}
|
2120 |
+
|
2121 |
+
.awesomplete > ul > li:hover {
|
2122 |
+
background-color: #B7E2F1;
|
2123 |
+
color: #2E2F27;
|
2124 |
+
}
|
2125 |
+
|
2126 |
+
.awesomplete > ul > li[aria-selected="true"] {
|
2127 |
+
background-color: #B7E2F1;
|
2128 |
+
color: #2E2F27;
|
2129 |
+
}
|
2130 |
+
|
2131 |
+
.awesomplete mark {
|
2132 |
+
background-color: #61D790;
|
2133 |
+
color: #2E2F27;
|
2134 |
+
}
|
2135 |
+
|
2136 |
+
.awesomplete li:hover mark {
|
2137 |
+
background-color: #61D790;
|
2138 |
+
color: #2E2F27;
|
2139 |
+
}
|
2140 |
+
|
2141 |
+
.awesomplete li[aria-selected="true"] mark {
|
2142 |
+
background-color: #61D790;
|
2143 |
+
color: #2E2F27;
|
2144 |
+
}
|
2145 |
+
|
2146 |
:root:has(.data-\[state\=open\]\:no-bg-scroll[data-state="open"]) {
|
2147 |
overflow: hidden;
|
2148 |
}
|
|
|
2761 |
.\[\&_tr\]\:border-b tr {
|
2762 |
border-bottom-width: 1px;
|
2763 |
}
|
2764 |
+
|