Spaces:
Runtime error
Runtime error
Update main.py
Browse files
main.py
CHANGED
|
@@ -1,15 +1,14 @@
|
|
| 1 |
import os
|
|
|
|
| 2 |
import asyncio
|
| 3 |
import logging
|
| 4 |
-
import json
|
| 5 |
-
import torch
|
| 6 |
-
import numpy as np
|
| 7 |
-
import httpx
|
| 8 |
-
import uvicorn
|
| 9 |
-
|
| 10 |
from contextlib import asynccontextmanager
|
| 11 |
from urllib.parse import urlparse
|
| 12 |
|
|
|
|
|
|
|
|
|
|
|
|
|
| 13 |
from bs4 import BeautifulSoup
|
| 14 |
from trafilatura import extract
|
| 15 |
from ddgs import DDGS
|
|
@@ -19,11 +18,71 @@ from fastapi.middleware.cors import CORSMiddleware
|
|
| 19 |
from fastapi.staticfiles import StaticFiles
|
| 20 |
|
| 21 |
from pydantic import BaseModel
|
| 22 |
-
from sentence_transformers import SentenceTransformer
|
| 23 |
from transformers import (
|
| 24 |
AutoTokenizer,
|
| 25 |
AutoModelForCausalLM,
|
| 26 |
-
AutoConfig
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 27 |
)
|
| 28 |
|
| 29 |
|
|
@@ -33,7 +92,7 @@ from transformers import (
|
|
| 33 |
|
| 34 |
logging.basicConfig(
|
| 35 |
level=logging.INFO,
|
| 36 |
-
format="%(asctime)s - %(levelname)s - %(message)s"
|
| 37 |
)
|
| 38 |
|
| 39 |
logger = logging.getLogger("infinitygrm")
|
|
@@ -45,45 +104,44 @@ logger = logging.getLogger("infinitygrm")
|
|
| 45 |
|
| 46 |
engine_state = {}
|
| 47 |
|
| 48 |
-
# Temporary in-memory session storage
|
| 49 |
-
# Replace with Redis later if needed.
|
| 50 |
session_store = {}
|
| 51 |
|
|
|
|
|
|
|
| 52 |
|
| 53 |
# ============================================================
|
| 54 |
# REASONING EXAMPLES
|
| 55 |
#
|
| 56 |
-
# These
|
| 57 |
#
|
| 58 |
-
#
|
| 59 |
-
# - how to identify the problem type
|
| 60 |
-
# - how to reject tempting wrong approaches
|
| 61 |
-
# - how to identify useful state/invariants
|
| 62 |
-
# - how to verify an answer
|
| 63 |
-
# - how to reason about complexity
|
| 64 |
#
|
| 65 |
-
#
|
|
|
|
| 66 |
# ============================================================
|
| 67 |
|
| 68 |
REASONING_EXAMPLES = [
|
| 69 |
|
| 70 |
{
|
| 71 |
-
"category": "
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 72 |
"problem": """
|
| 73 |
Given an array of integers that may contain negative numbers,
|
| 74 |
find the longest contiguous subarray whose sum equals k.
|
| 75 |
""",
|
| 76 |
"reasoning": """
|
| 77 |
-
Key observation:
|
| 78 |
-
|
| 79 |
-
adding an element can decrease the sum and removing an element can
|
| 80 |
-
increase it.
|
| 81 |
|
| 82 |
-
Use prefix sums.
|
|
|
|
| 83 |
|
| 84 |
-
|
| 85 |
-
|
| 86 |
-
index gives the longest possible subarray.
|
| 87 |
""",
|
| 88 |
"answer": """
|
| 89 |
Use a hashmap of prefix sums.
|
|
@@ -92,234 +150,275 @@ prefix = {0: -1}
|
|
| 92 |
running_sum = 0
|
| 93 |
best = 0
|
| 94 |
|
| 95 |
-
For each index
|
| 96 |
running_sum += nums[i]
|
| 97 |
|
| 98 |
if running_sum - k exists:
|
| 99 |
-
|
| 100 |
|
| 101 |
-
store running_sum only if it has not
|
| 102 |
|
| 103 |
Time: O(n) average.
|
| 104 |
Space: O(n).
|
| 105 |
-
"""
|
| 106 |
},
|
| 107 |
|
| 108 |
{
|
| 109 |
"category": "graphs",
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 110 |
"problem": """
|
| 111 |
A directed graph has positive edge costs. You may use a discount
|
| 112 |
exactly once, causing one chosen edge to cost floor(cost / 2).
|
| 113 |
Find the minimum cost from s to t.
|
| 114 |
""",
|
| 115 |
"reasoning": """
|
| 116 |
-
The
|
| 117 |
-
|
| 118 |
|
| 119 |
-
Therefore
|
| 120 |
|
| 121 |
-
|
| 122 |
-
(node,
|
| 123 |
-
(node,
|
| 124 |
|
| 125 |
-
From
|
| 126 |
-
|
| 127 |
|
| 128 |
-
|
|
|
|
| 129 |
""",
|
| 130 |
"answer": """
|
| 131 |
-
Dijkstra over
|
| 132 |
-
|
| 133 |
-
unused -> unused with cost c
|
| 134 |
-
unused -> used with cost c // 2
|
| 135 |
-
used -> used with cost c
|
| 136 |
|
| 137 |
-
|
| 138 |
-
|
| 139 |
-
|
| 140 |
-
Space: O(n+m).
|
| 141 |
-
"""
|
| 142 |
},
|
| 143 |
|
| 144 |
{
|
| 145 |
-
"category": "
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 146 |
"problem": """
|
| 147 |
Determine whether a directed graph contains a cycle in O(V+E).
|
| 148 |
""",
|
| 149 |
"reasoning": """
|
| 150 |
-
A visited set
|
| 151 |
-
|
| 152 |
-
A node can be completely processed without forming a cycle, while
|
| 153 |
-
another edge can point back to a node currently being explored.
|
| 154 |
|
| 155 |
Use three states:
|
| 156 |
0 = unvisited
|
| 157 |
1 = currently exploring
|
| 158 |
2 = completely processed
|
| 159 |
|
| 160 |
-
|
|
|
|
|
|
|
|
|
|
| 161 |
""",
|
| 162 |
"answer": """
|
| 163 |
Use DFS with three colors/states.
|
| 164 |
|
| 165 |
-
Time: O(V+E).
|
| 166 |
Space: O(V).
|
| 167 |
-
"""
|
| 168 |
},
|
| 169 |
|
| 170 |
{
|
| 171 |
"category": "data_structures",
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 172 |
"problem": """
|
| 173 |
Find the first non-repeating character in a string.
|
| 174 |
""",
|
| 175 |
"reasoning": """
|
| 176 |
The answer depends on both frequency and original order.
|
| 177 |
|
| 178 |
-
First count every character.
|
| 179 |
-
|
| 180 |
-
frequency is one.
|
| 181 |
""",
|
| 182 |
"answer": """
|
| 183 |
Use a frequency hashmap followed by a second scan.
|
| 184 |
|
| 185 |
Time: O(n).
|
| 186 |
Space: O(k), where k is the number of distinct characters.
|
| 187 |
-
"""
|
| 188 |
},
|
| 189 |
|
| 190 |
{
|
| 191 |
-
"category": "
|
|
|
|
|
|
|
|
|
|
|
|
|
| 192 |
"problem": """
|
| 193 |
Given a sorted array, determine whether two numbers sum to target.
|
| 194 |
""",
|
| 195 |
"reasoning": """
|
| 196 |
-
|
| 197 |
-
possibilities at once.
|
| 198 |
|
| 199 |
-
If
|
| 200 |
-
If
|
| 201 |
-
If
|
| 202 |
""",
|
| 203 |
"answer": """
|
| 204 |
Use two pointers.
|
| 205 |
|
| 206 |
Time: O(n).
|
| 207 |
Space: O(1).
|
| 208 |
-
"""
|
| 209 |
},
|
| 210 |
|
| 211 |
{
|
| 212 |
-
"category": "
|
|
|
|
|
|
|
|
|
|
|
|
|
| 213 |
"problem": """
|
| 214 |
Find the shortest path in an unweighted graph.
|
| 215 |
""",
|
| 216 |
"reasoning": """
|
| 217 |
Every edge has equal cost.
|
| 218 |
|
| 219 |
-
|
| 220 |
-
the
|
| 221 |
-
|
| 222 |
""",
|
| 223 |
"answer": """
|
| 224 |
Use BFS with a queue.
|
| 225 |
|
| 226 |
-
Time: O(V+E).
|
| 227 |
Space: O(V).
|
| 228 |
-
"""
|
| 229 |
},
|
| 230 |
|
| 231 |
{
|
| 232 |
-
"category": "
|
|
|
|
|
|
|
|
|
|
|
|
|
| 233 |
"problem": """
|
| 234 |
Find the shortest path in a graph with positive weighted edges.
|
| 235 |
""",
|
| 236 |
"reasoning": """
|
| 237 |
BFS is not sufficient because edges have different costs.
|
| 238 |
|
| 239 |
-
|
| 240 |
-
|
|
|
|
| 241 |
""",
|
| 242 |
"answer": """
|
| 243 |
Use Dijkstra with a min-heap.
|
| 244 |
|
| 245 |
Typical complexity:
|
| 246 |
-
O((V+E) log V).
|
| 247 |
-
"""
|
| 248 |
},
|
| 249 |
|
| 250 |
{
|
| 251 |
-
"category": "
|
|
|
|
|
|
|
|
|
|
|
|
|
| 252 |
"problem": """
|
| 253 |
A graph has only edge weights 0 and 1. Find shortest paths.
|
| 254 |
""",
|
| 255 |
"reasoning": """
|
| 256 |
-
Ordinary BFS treats
|
| 257 |
|
| 258 |
-
Dijkstra works, but
|
|
|
|
| 259 |
|
| 260 |
-
|
| 261 |
-
When traversing a one-cost edge, add it to the back.
|
| 262 |
""",
|
| 263 |
"answer": """
|
| 264 |
Use 0-1 BFS.
|
| 265 |
|
| 266 |
-
Time: O(V+E).
|
| 267 |
Space: O(V).
|
| 268 |
-
"""
|
| 269 |
},
|
| 270 |
|
| 271 |
{
|
| 272 |
"category": "dynamic_programming",
|
|
|
|
|
|
|
|
|
|
|
|
|
| 273 |
"problem": """
|
| 274 |
Given positive integers, maximize the sum without selecting adjacent
|
| 275 |
elements.
|
| 276 |
""",
|
| 277 |
"reasoning": """
|
| 278 |
-
At
|
| 279 |
|
| 280 |
-
Skip
|
| 281 |
-
|
| 282 |
-
be taken.
|
| 283 |
|
| 284 |
-
|
|
|
|
|
|
|
|
|
|
| 285 |
|
| 286 |
dp[i] = max(dp[i-1], dp[i-2] + nums[i])
|
| 287 |
|
| 288 |
-
Only the previous two
|
| 289 |
""",
|
| 290 |
"answer": """
|
| 291 |
-
Use dynamic programming with
|
| 292 |
|
| 293 |
Time: O(n).
|
| 294 |
Space: O(1).
|
| 295 |
-
"""
|
| 296 |
},
|
| 297 |
|
| 298 |
{
|
| 299 |
"category": "binary_search",
|
|
|
|
|
|
|
|
|
|
|
|
|
| 300 |
"problem": """
|
| 301 |
Find the minimum integer x for which a feasibility condition becomes
|
| 302 |
true.
|
| 303 |
""",
|
| 304 |
"reasoning": """
|
| 305 |
-
Binary search is
|
|
|
|
| 306 |
|
| 307 |
-
If x is feasible,
|
| 308 |
-
|
| 309 |
|
| 310 |
-
Search for the first
|
| 311 |
""",
|
| 312 |
"answer": """
|
| 313 |
Use binary search on the answer.
|
| 314 |
|
| 315 |
-
|
| 316 |
|
| 317 |
-
|
| 318 |
-
|
|
|
|
| 319 |
},
|
| 320 |
|
| 321 |
{
|
| 322 |
"category": "algorithm_selection",
|
|
|
|
|
|
|
|
|
|
|
|
|
| 323 |
"problem": """
|
| 324 |
Should binary search automatically be used whenever an answer is a
|
| 325 |
number?
|
|
@@ -327,21 +426,24 @@ number?
|
|
| 327 |
"reasoning": """
|
| 328 |
No.
|
| 329 |
|
| 330 |
-
|
| 331 |
-
The fact that the answer is numeric is not enough.
|
| 332 |
|
| 333 |
-
|
| 334 |
-
|
| 335 |
""",
|
| 336 |
"answer": """
|
| 337 |
Do not automatically use binary search.
|
| 338 |
|
| 339 |
-
|
| 340 |
-
"""
|
| 341 |
},
|
| 342 |
|
| 343 |
{
|
| 344 |
"category": "debugging",
|
|
|
|
|
|
|
|
|
|
|
|
|
| 345 |
"problem": """
|
| 346 |
A function returns the first unique character but returns None too
|
| 347 |
early.
|
|
@@ -356,18 +458,22 @@ Find the bug.
|
|
| 356 |
"reasoning": """
|
| 357 |
The return None statement is inside the loop.
|
| 358 |
|
| 359 |
-
Therefore the function
|
| 360 |
-
|
| 361 |
|
| 362 |
-
The
|
| 363 |
""",
|
| 364 |
"answer": """
|
| 365 |
Move return None outside the loop.
|
| 366 |
-
"""
|
| 367 |
},
|
| 368 |
|
| 369 |
{
|
| 370 |
"category": "complexity",
|
|
|
|
|
|
|
|
|
|
|
|
|
| 371 |
"problem": """
|
| 372 |
What is the complexity of:
|
| 373 |
|
|
@@ -377,32 +483,36 @@ for x in values:
|
|
| 377 |
result.append(x)
|
| 378 |
""",
|
| 379 |
"reasoning": """
|
| 380 |
-
Membership testing in a Python list is O(n).
|
| 381 |
|
| 382 |
-
That operation
|
| 383 |
|
| 384 |
-
Therefore the worst-case complexity is O(n²).
|
| 385 |
""",
|
| 386 |
"answer": """
|
| 387 |
Worst case:
|
|
|
|
| 388 |
Time: O(n²)
|
| 389 |
Space: O(n)
|
| 390 |
|
| 391 |
-
A set can
|
| 392 |
-
"""
|
| 393 |
},
|
| 394 |
|
| 395 |
{
|
| 396 |
"category": "optimization",
|
|
|
|
|
|
|
|
|
|
|
|
|
| 397 |
"problem": """
|
| 398 |
Remove duplicates from a list while preserving original order.
|
| 399 |
""",
|
| 400 |
"reasoning": """
|
| 401 |
-
|
| 402 |
-
|
| 403 |
|
| 404 |
-
|
| 405 |
-
preserves order.
|
| 406 |
""",
|
| 407 |
"answer": """
|
| 408 |
seen = set()
|
|
@@ -415,11 +525,15 @@ for x in values:
|
|
| 415 |
|
| 416 |
Average time: O(n).
|
| 417 |
Space: O(n).
|
| 418 |
-
"""
|
| 419 |
},
|
| 420 |
|
| 421 |
{
|
| 422 |
"category": "stacks",
|
|
|
|
|
|
|
|
|
|
|
|
|
| 423 |
"problem": """
|
| 424 |
Determine whether parentheses, brackets, and braces are correctly
|
| 425 |
balanced and nested.
|
|
@@ -429,120 +543,125 @@ The most recently opened bracket must be the first bracket closed.
|
|
| 429 |
|
| 430 |
That is last-in-first-out behavior, so a stack is appropriate.
|
| 431 |
|
| 432 |
-
|
| 433 |
-
|
| 434 |
""",
|
| 435 |
"answer": """
|
| 436 |
Use a stack.
|
| 437 |
|
| 438 |
Time: O(n).
|
| 439 |
Space: O(n).
|
| 440 |
-
"""
|
| 441 |
},
|
| 442 |
|
| 443 |
{
|
| 444 |
"category": "sliding_window",
|
|
|
|
|
|
|
|
|
|
|
|
|
| 445 |
"problem": """
|
| 446 |
Find the longest substring without repeating characters.
|
| 447 |
""",
|
| 448 |
"reasoning": """
|
| 449 |
Maintain a window containing unique characters.
|
| 450 |
|
| 451 |
-
When a
|
| 452 |
-
|
| 453 |
|
| 454 |
-
|
| 455 |
-
a constant number of times.
|
| 456 |
""",
|
| 457 |
"answer": """
|
| 458 |
-
Use a sliding window
|
| 459 |
|
| 460 |
Time: O(n).
|
| 461 |
Space: O(k).
|
| 462 |
-
"""
|
| 463 |
},
|
| 464 |
|
| 465 |
{
|
| 466 |
-
"category": "
|
|
|
|
|
|
|
|
|
|
|
|
|
| 467 |
"problem": """
|
| 468 |
Find the kth largest element without fully sorting the array.
|
| 469 |
""",
|
| 470 |
"reasoning": """
|
| 471 |
-
Full sorting
|
| 472 |
-
statistic.
|
| 473 |
|
| 474 |
-
Quickselect partitions around a pivot and only
|
| 475 |
-
|
| 476 |
""",
|
| 477 |
"answer": """
|
| 478 |
-
|
| 479 |
-
space, depending on implementation.
|
| 480 |
-
"""
|
| 481 |
-
},
|
| 482 |
|
| 483 |
-
|
| 484 |
-
|
| 485 |
-
"problem": """
|
| 486 |
-
Determine whether an undirected graph is connected.
|
| 487 |
-
""",
|
| 488 |
-
"reasoning": """
|
| 489 |
-
Starting from any vertex, perform DFS or BFS.
|
| 490 |
-
|
| 491 |
-
If every vertex is reached, the graph is connected.
|
| 492 |
-
Otherwise, at least one vertex belongs to another component.
|
| 493 |
""",
|
| 494 |
-
"answer": """
|
| 495 |
-
DFS or BFS.
|
| 496 |
-
|
| 497 |
-
Time: O(V+E).
|
| 498 |
-
Space: O(V).
|
| 499 |
-
"""
|
| 500 |
},
|
| 501 |
|
| 502 |
{
|
| 503 |
"category": "trees",
|
|
|
|
|
|
|
|
|
|
|
|
|
| 504 |
"problem": """
|
| 505 |
Find the maximum depth of a binary tree.
|
| 506 |
""",
|
| 507 |
"reasoning": """
|
| 508 |
The depth of a node is one plus the maximum depth of its children.
|
| 509 |
|
| 510 |
-
This
|
| 511 |
-
|
|
|
|
|
|
|
| 512 |
""",
|
| 513 |
"answer": """
|
| 514 |
-
|
| 515 |
|
| 516 |
Time: O(n).
|
| 517 |
-
Space: O(h), where h is tree height.
|
| 518 |
-
"""
|
| 519 |
},
|
| 520 |
|
| 521 |
{
|
| 522 |
"category": "trees",
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 523 |
"problem": """
|
| 524 |
Determine whether a binary tree is height-balanced.
|
| 525 |
""",
|
| 526 |
"reasoning": """
|
| 527 |
-
|
| 528 |
traverse the same subtrees.
|
| 529 |
|
| 530 |
-
|
| 531 |
|
| 532 |
-
Return failure immediately
|
| 533 |
""",
|
| 534 |
"answer": """
|
| 535 |
Use postorder DFS.
|
| 536 |
|
| 537 |
-
Return height or a sentinel indicating imbalance.
|
| 538 |
|
| 539 |
Time: O(n).
|
| 540 |
Space: O(h).
|
| 541 |
-
"""
|
| 542 |
},
|
| 543 |
|
| 544 |
{
|
| 545 |
"category": "greedy",
|
|
|
|
|
|
|
|
|
|
|
|
|
| 546 |
"problem": """
|
| 547 |
Can you always solve an optimization problem greedily by taking the
|
| 548 |
locally cheapest option?
|
|
@@ -550,90 +669,108 @@ locally cheapest option?
|
|
| 550 |
"reasoning": """
|
| 551 |
No.
|
| 552 |
|
| 553 |
-
A local choice
|
| 554 |
|
| 555 |
A greedy algorithm needs a proof that the local choice is safe,
|
| 556 |
-
|
| 557 |
""",
|
| 558 |
"answer": """
|
| 559 |
Do not assume greedy works.
|
| 560 |
-
|
| 561 |
-
|
|
|
|
| 562 |
},
|
| 563 |
|
| 564 |
{
|
| 565 |
"category": "algorithm_selection",
|
|
|
|
|
|
|
|
|
|
|
|
|
| 566 |
"problem": """
|
| 567 |
-
|
| 568 |
-
|
| 569 |
-
|
| 570 |
""",
|
| 571 |
"reasoning": """
|
| 572 |
Not necessarily.
|
| 573 |
|
| 574 |
-
If
|
| 575 |
information is part of the state.
|
| 576 |
|
| 577 |
-
For example, whether a discount has been used
|
| 578 |
-
|
| 579 |
""",
|
| 580 |
"answer": """
|
| 581 |
Expand the state representation.
|
| 582 |
|
| 583 |
-
Use
|
| 584 |
-
|
|
|
|
|
|
|
|
|
|
| 585 |
},
|
| 586 |
|
| 587 |
{
|
| 588 |
"category": "verification",
|
|
|
|
|
|
|
|
|
|
|
|
|
| 589 |
"problem": """
|
| 590 |
How should a newly written algorithm be tested?
|
| 591 |
""",
|
| 592 |
"reasoning": """
|
| 593 |
-
|
| 594 |
|
| 595 |
-
|
| 596 |
-
- smallest valid
|
| 597 |
- empty input when allowed
|
| 598 |
-
-
|
| 599 |
- negative values
|
| 600 |
- boundary values
|
| 601 |
-
-
|
| 602 |
-
-
|
| 603 |
-
-
|
| 604 |
-
- large inputs
|
|
|
|
| 605 |
""",
|
| 606 |
"answer": """
|
| 607 |
-
|
| 608 |
-
"""
|
| 609 |
},
|
| 610 |
|
| 611 |
{
|
| 612 |
"category": "complexity",
|
|
|
|
|
|
|
|
|
|
| 613 |
"problem": """
|
| 614 |
-
An algorithm performs
|
| 615 |
binary-search step. What happens to the complexity?
|
| 616 |
""",
|
| 617 |
"reasoning": """
|
| 618 |
-
|
| 619 |
|
| 620 |
If Dijkstra costs O(E log V) and binary search performs O(log C)
|
| 621 |
iterations, the total is:
|
| 622 |
|
| 623 |
O(E log V log C)
|
| 624 |
-
|
| 625 |
-
It cannot simply be described as O(E log V).
|
| 626 |
""",
|
| 627 |
"answer": """
|
| 628 |
-
Multiply
|
| 629 |
|
| 630 |
Total:
|
| 631 |
O(E log V log C).
|
| 632 |
-
"""
|
| 633 |
},
|
| 634 |
|
| 635 |
{
|
| 636 |
-
"category": "
|
|
|
|
|
|
|
|
|
|
|
|
|
| 637 |
"problem": """
|
| 638 |
Write a Python function that merges overlapping intervals.
|
| 639 |
""",
|
|
@@ -643,139 +780,172 @@ Sort intervals by starting point.
|
|
| 643 |
After sorting, the next interval either overlaps the current interval
|
| 644 |
or starts after it ends.
|
| 645 |
|
| 646 |
-
|
| 647 |
-
|
| 648 |
""",
|
| 649 |
"answer": """
|
| 650 |
-
Sort
|
| 651 |
|
| 652 |
Time: O(n log n).
|
| 653 |
Space: O(n) for the output.
|
| 654 |
-
"""
|
| 655 |
},
|
| 656 |
|
| 657 |
{
|
| 658 |
-
"category": "
|
|
|
|
|
|
|
|
|
|
|
|
|
| 659 |
"problem": """
|
| 660 |
-
|
| 661 |
non-alphanumeric characters and ignoring case.
|
| 662 |
""",
|
| 663 |
"reasoning": """
|
| 664 |
-
|
| 665 |
-
|
|
|
|
|
|
|
| 666 |
|
| 667 |
-
|
| 668 |
-
Compare normalized characters.
|
| 669 |
""",
|
| 670 |
"answer": """
|
| 671 |
-
|
| 672 |
|
| 673 |
Time: O(n).
|
| 674 |
-
|
| 675 |
-
"""
|
| 676 |
},
|
| 677 |
|
| 678 |
{
|
| 679 |
"category": "debugging",
|
|
|
|
|
|
|
|
|
|
|
|
|
| 680 |
"problem": """
|
| 681 |
A recursive function works on small inputs but crashes on very large
|
| 682 |
inputs.
|
| 683 |
""",
|
| 684 |
"reasoning": """
|
| 685 |
-
The algorithm may be correct
|
| 686 |
-
depth
|
|
|
|
|
|
|
| 687 |
|
| 688 |
-
|
| 689 |
-
|
| 690 |
""",
|
| 691 |
"answer": """
|
| 692 |
-
Check recursion depth.
|
| 693 |
-
|
| 694 |
-
"""
|
| 695 |
},
|
| 696 |
|
| 697 |
{
|
| 698 |
"category": "graphs",
|
|
|
|
|
|
|
|
|
|
|
|
|
| 699 |
"problem": """
|
| 700 |
Why can't ordinary Dijkstra handle negative edge weights?
|
| 701 |
""",
|
| 702 |
"reasoning": """
|
| 703 |
-
Dijkstra
|
| 704 |
-
|
| 705 |
|
| 706 |
-
A negative edge can
|
| 707 |
-
assumption.
|
| 708 |
""",
|
| 709 |
"answer": """
|
| 710 |
-
Negative
|
| 711 |
-
|
| 712 |
-
|
|
|
|
| 713 |
},
|
| 714 |
|
| 715 |
{
|
| 716 |
"category": "math",
|
|
|
|
|
|
|
|
|
|
|
|
|
| 717 |
"problem": """
|
| 718 |
How can you determine whether an integer is a power of two?
|
| 719 |
""",
|
| 720 |
"reasoning": """
|
| 721 |
-
A positive power of two has exactly one bit set
|
| 722 |
|
| 723 |
For positive n:
|
|
|
|
| 724 |
n & (n - 1) == 0
|
| 725 |
-
|
|
|
|
| 726 |
""",
|
| 727 |
"answer": """
|
| 728 |
Check:
|
| 729 |
|
| 730 |
n > 0 and (n & (n - 1)) == 0
|
| 731 |
-
"""
|
| 732 |
},
|
| 733 |
|
| 734 |
{
|
| 735 |
"category": "algorithm_selection",
|
|
|
|
|
|
|
|
|
|
|
|
|
| 736 |
"problem": """
|
| 737 |
-
You are given a problem
|
| 738 |
uses O(n²). What should you investigate?
|
| 739 |
""",
|
| 740 |
"reasoning": """
|
| 741 |
-
|
| 742 |
|
| 743 |
-
Look for
|
| 744 |
-
|
| 745 |
-
|
| 746 |
""",
|
| 747 |
"answer": """
|
| 748 |
-
|
| 749 |
formulation.
|
| 750 |
-
"""
|
| 751 |
},
|
| 752 |
|
| 753 |
{
|
| 754 |
"category": "problem_solving",
|
|
|
|
|
|
|
|
|
|
|
|
|
| 755 |
"problem": """
|
| 756 |
-
A solution works on
|
| 757 |
-
|
| 758 |
""",
|
| 759 |
"reasoning": """
|
| 760 |
-
Check assumptions.
|
| 761 |
|
| 762 |
-
Typical causes:
|
| 763 |
-
incorrect
|
| 764 |
empty inputs,
|
| 765 |
-
|
| 766 |
-
|
| 767 |
incorrect state transitions,
|
| 768 |
wrong complexity,
|
| 769 |
-
or an algorithm that only works
|
| 770 |
""",
|
| 771 |
"answer": """
|
| 772 |
Use adversarial and edge-case testing and verify the algorithm's
|
| 773 |
invariants.
|
| 774 |
-
"""
|
| 775 |
},
|
| 776 |
|
| 777 |
{
|
| 778 |
"category": "invariants",
|
|
|
|
|
|
|
|
|
|
|
|
|
| 779 |
"problem": """
|
| 780 |
What is an invariant in an algorithm?
|
| 781 |
""",
|
|
@@ -783,41 +953,49 @@ What is an invariant in an algorithm?
|
|
| 783 |
An invariant is a property that remains true throughout the relevant
|
| 784 |
part of the algorithm.
|
| 785 |
|
| 786 |
-
|
| 787 |
errors.
|
| 788 |
""",
|
| 789 |
"answer": """
|
| 790 |
-
State the property that must remain true after every
|
| 791 |
-
state transition.
|
| 792 |
-
"""
|
| 793 |
},
|
| 794 |
|
| 795 |
{
|
| 796 |
-
"category": "
|
|
|
|
|
|
|
|
|
|
|
|
|
| 797 |
"problem": """
|
| 798 |
How do you design a dynamic programming solution to an unfamiliar
|
| 799 |
problem?
|
| 800 |
""",
|
| 801 |
"reasoning": """
|
| 802 |
-
Start by identifying the smallest information
|
| 803 |
subproblem.
|
| 804 |
|
| 805 |
Then define:
|
| 806 |
1. state
|
| 807 |
2. transition
|
| 808 |
3. base cases
|
| 809 |
-
4.
|
| 810 |
5. final state
|
| 811 |
|
| 812 |
-
Avoid
|
| 813 |
""",
|
| 814 |
"answer": """
|
| 815 |
-
Define the state and transition before
|
| 816 |
-
"""
|
| 817 |
},
|
| 818 |
|
| 819 |
{
|
| 820 |
-
"category": "
|
|
|
|
|
|
|
|
|
|
|
|
|
| 821 |
"problem": """
|
| 822 |
A program produces the right answer but is too slow. What should
|
| 823 |
you do?
|
|
@@ -828,34 +1006,43 @@ Correctness and efficiency are separate properties.
|
|
| 828 |
First identify the dominant operation using complexity analysis or
|
| 829 |
profiling.
|
| 830 |
|
| 831 |
-
Then
|
|
|
|
| 832 |
""",
|
| 833 |
"answer": """
|
| 834 |
Find the dominant bottleneck, determine its complexity, and improve
|
| 835 |
-
the algorithm or data structure
|
| 836 |
-
"""
|
| 837 |
},
|
| 838 |
|
| 839 |
{
|
| 840 |
-
"category": "
|
|
|
|
|
|
|
|
|
|
|
|
|
| 841 |
"problem": """
|
| 842 |
Why should a dictionary store the earliest index of a prefix sum when
|
| 843 |
finding the longest subarray with a target sum?
|
| 844 |
""",
|
| 845 |
"reasoning": """
|
| 846 |
-
For a fixed ending index
|
| 847 |
subarray.
|
| 848 |
|
| 849 |
-
|
| 850 |
-
|
| 851 |
""",
|
| 852 |
"answer": """
|
| 853 |
-
Keep the earliest occurrence of
|
| 854 |
-
"""
|
| 855 |
},
|
| 856 |
|
| 857 |
{
|
| 858 |
"category": "logic",
|
|
|
|
|
|
|
|
|
|
|
|
|
| 859 |
"problem": """
|
| 860 |
A statement says: If A is true, then B is true. Does B being true
|
| 861 |
prove A is true?
|
|
@@ -863,59 +1050,46 @@ prove A is true?
|
|
| 863 |
"reasoning": """
|
| 864 |
No.
|
| 865 |
|
| 866 |
-
The implication
|
| 867 |
|
| 868 |
-
B may be true for
|
| 869 |
-
consequent.
|
| 870 |
""",
|
| 871 |
"answer": """
|
| 872 |
No. A -> B does not imply B -> A.
|
| 873 |
-
"""
|
| 874 |
-
},
|
| 875 |
-
|
| 876 |
-
{
|
| 877 |
-
"category": "coding",
|
| 878 |
-
"problem": """
|
| 879 |
-
A Python function needs to process a stream containing millions of
|
| 880 |
-
items, but all items cannot fit comfortably in memory. What should
|
| 881 |
-
you consider?
|
| 882 |
-
""",
|
| 883 |
-
"reasoning": """
|
| 884 |
-
Avoid loading the entire stream at once.
|
| 885 |
-
|
| 886 |
-
Process incrementally, keeping only the state needed to produce the
|
| 887 |
-
answer.
|
| 888 |
-
|
| 889 |
-
This is a streaming algorithm.
|
| 890 |
""",
|
| 891 |
-
"answer": """
|
| 892 |
-
Process items one at a time or in bounded batches.
|
| 893 |
-
"""
|
| 894 |
},
|
| 895 |
|
| 896 |
{
|
| 897 |
"category": "systems",
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 898 |
"problem": """
|
| 899 |
Twenty users may request inference from the same GPU-hosted language
|
| 900 |
model simultaneously. How can the server avoid exhausting GPU
|
| 901 |
memory?
|
| 902 |
""",
|
| 903 |
"reasoning": """
|
| 904 |
-
|
|
|
|
| 905 |
|
| 906 |
-
|
| 907 |
-
where supported.
|
| 908 |
|
| 909 |
-
|
| 910 |
-
active requests and sequence lengths.
|
| 911 |
""",
|
| 912 |
"answer": """
|
| 913 |
Use bounded concurrency, request queuing, and controlled batching.
|
| 914 |
-
"""
|
| 915 |
},
|
| 916 |
|
| 917 |
{
|
| 918 |
-
"category": "
|
|
|
|
|
|
|
|
|
|
|
|
|
| 919 |
"problem": """
|
| 920 |
An algorithm's proof assumes all edge weights are positive, but the
|
| 921 |
implementation accepts negative weights. Is the proof still valid?
|
|
@@ -923,32 +1097,162 @@ implementation accepts negative weights. Is the proof still valid?
|
|
| 923 |
"reasoning": """
|
| 924 |
No.
|
| 925 |
|
| 926 |
-
The implementation
|
| 927 |
-
|
| 928 |
|
| 929 |
-
Correctness guarantees only apply
|
|
|
|
| 930 |
""",
|
| 931 |
"answer": """
|
| 932 |
-
|
| 933 |
whose correctness supports negative weights.
|
| 934 |
-
"""
|
| 935 |
-
}
|
| 936 |
-
|
| 937 |
]
|
| 938 |
|
| 939 |
|
| 940 |
# ============================================================
|
| 941 |
-
#
|
| 942 |
# ============================================================
|
| 943 |
|
| 944 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 945 |
|
| 946 |
|
| 947 |
def build_reasoning_embeddings():
|
| 948 |
-
|
| 949 |
-
Build embeddings once during startup so we don't repeatedly
|
| 950 |
-
encode the entire example library for every user request.
|
| 951 |
-
"""
|
| 952 |
global reasoning_embeddings
|
| 953 |
|
| 954 |
if not REASONING_EXAMPLES:
|
|
@@ -960,37 +1264,28 @@ def build_reasoning_embeddings():
|
|
| 960 |
if embed_model is None:
|
| 961 |
return
|
| 962 |
|
| 963 |
-
texts = [
|
| 964 |
-
|
| 965 |
-
|
| 966 |
-
|
| 967 |
-
example["category"]
|
| 968 |
-
+ "\n"
|
| 969 |
-
+ example["problem"]
|
| 970 |
-
+ "\n"
|
| 971 |
-
+ example["reasoning"]
|
| 972 |
-
)
|
| 973 |
-
texts.append(combined)
|
| 974 |
|
| 975 |
reasoning_embeddings = embed_model.encode(
|
| 976 |
texts,
|
| 977 |
normalize_embeddings=True,
|
| 978 |
-
convert_to_numpy=True
|
|
|
|
| 979 |
)
|
| 980 |
|
| 981 |
logger.info(
|
| 982 |
-
|
|
|
|
| 983 |
)
|
| 984 |
|
| 985 |
|
| 986 |
def retrieve_reasoning_examples(
|
| 987 |
query: str,
|
| 988 |
-
top_k: int = 4
|
| 989 |
) -> list:
|
| 990 |
-
"""
|
| 991 |
-
Retrieve only the most relevant examples instead of inserting
|
| 992 |
-
the entire example library into every prompt.
|
| 993 |
-
"""
|
| 994 |
|
| 995 |
global reasoning_embeddings
|
| 996 |
|
|
@@ -1003,50 +1298,120 @@ def retrieve_reasoning_examples(
|
|
| 1003 |
return REASONING_EXAMPLES[:top_k]
|
| 1004 |
|
| 1005 |
try:
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1006 |
query_embedding = embed_model.encode(
|
| 1007 |
[query],
|
| 1008 |
normalize_embeddings=True,
|
| 1009 |
-
convert_to_numpy=True
|
|
|
|
| 1010 |
)[0]
|
| 1011 |
|
| 1012 |
-
if reasoning_embeddings is None:
|
| 1013 |
-
build_reasoning_embeddings()
|
| 1014 |
-
|
| 1015 |
scores = np.dot(
|
| 1016 |
reasoning_embeddings,
|
| 1017 |
-
query_embedding
|
| 1018 |
)
|
| 1019 |
|
| 1020 |
-
|
|
|
|
|
|
|
| 1021 |
|
| 1022 |
selected = []
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1023 |
|
| 1024 |
-
|
| 1025 |
-
|
| 1026 |
-
|
| 1027 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1028 |
|
| 1029 |
return selected
|
| 1030 |
|
| 1031 |
except Exception as e:
|
|
|
|
| 1032 |
logger.warning(
|
| 1033 |
-
|
|
|
|
| 1034 |
)
|
| 1035 |
|
| 1036 |
-
return
|
| 1037 |
|
| 1038 |
|
| 1039 |
def format_reasoning_examples(
|
| 1040 |
query: str,
|
| 1041 |
-
top_k: int = 4
|
| 1042 |
) -> str:
|
| 1043 |
-
"""
|
| 1044 |
-
Convert retrieved examples into compact prompt context.
|
| 1045 |
-
"""
|
| 1046 |
|
| 1047 |
examples = retrieve_reasoning_examples(
|
| 1048 |
query,
|
| 1049 |
-
top_k=top_k
|
| 1050 |
)
|
| 1051 |
|
| 1052 |
if not examples:
|
|
@@ -1060,14 +1425,16 @@ def format_reasoning_examples(
|
|
| 1060 |
f"""
|
| 1061 |
EXAMPLE {i}
|
| 1062 |
CATEGORY: {example["category"]}
|
|
|
|
|
|
|
| 1063 |
|
| 1064 |
PROBLEM:
|
| 1065 |
{example["problem"].strip()}
|
| 1066 |
|
| 1067 |
-
|
| 1068 |
{example["reasoning"].strip()}
|
| 1069 |
|
| 1070 |
-
|
| 1071 |
{example["answer"].strip()}
|
| 1072 |
""".strip()
|
| 1073 |
)
|
|
@@ -1076,433 +1443,90 @@ EXPECTED APPROACH:
|
|
| 1076 |
|
| 1077 |
|
| 1078 |
# ============================================================
|
| 1079 |
-
#
|
| 1080 |
# ============================================================
|
| 1081 |
|
| 1082 |
-
|
| 1083 |
-
async def lifespan(app: FastAPI):
|
| 1084 |
-
|
| 1085 |
-
logger.info(
|
| 1086 |
-
"Initializing neural weights and engine dependencies..."
|
| 1087 |
-
)
|
| 1088 |
-
|
| 1089 |
-
device = (
|
| 1090 |
-
"cuda"
|
| 1091 |
-
if torch.cuda.is_available()
|
| 1092 |
-
else "cpu"
|
| 1093 |
-
)
|
| 1094 |
-
|
| 1095 |
-
dtype = (
|
| 1096 |
-
torch.float16
|
| 1097 |
-
if device == "cuda"
|
| 1098 |
-
else torch.float32
|
| 1099 |
-
)
|
| 1100 |
-
|
| 1101 |
-
model_name = "Qwen/Qwen2.5-3B-Instruct"
|
| 1102 |
-
|
| 1103 |
-
logger.info(
|
| 1104 |
-
f"Loading model: {model_name}"
|
| 1105 |
-
)
|
| 1106 |
-
|
| 1107 |
-
tokenizer = AutoTokenizer.from_pretrained(
|
| 1108 |
-
model_name
|
| 1109 |
-
)
|
| 1110 |
-
|
| 1111 |
-
tokenizer.pad_token = tokenizer.eos_token
|
| 1112 |
-
|
| 1113 |
-
config = AutoConfig.from_pretrained(
|
| 1114 |
-
model_name
|
| 1115 |
-
)
|
| 1116 |
-
|
| 1117 |
-
config.pad_token_id = tokenizer.eos_token_id
|
| 1118 |
-
|
| 1119 |
-
model = AutoModelForCausalLM.from_pretrained(
|
| 1120 |
-
model_name,
|
| 1121 |
-
config=config,
|
| 1122 |
-
torch_dtype=dtype,
|
| 1123 |
-
device_map="auto" if device == "cuda" else None
|
| 1124 |
-
).eval()
|
| 1125 |
-
|
| 1126 |
-
embed_model = SentenceTransformer(
|
| 1127 |
-
"sentence-transformers/all-MiniLM-L6-v2",
|
| 1128 |
-
device=device
|
| 1129 |
-
)
|
| 1130 |
-
|
| 1131 |
-
engine_state["tokenizer"] = tokenizer
|
| 1132 |
-
engine_state["model"] = model
|
| 1133 |
-
engine_state["embed_model"] = embed_model
|
| 1134 |
-
|
| 1135 |
-
engine_state["max_history"] = 15
|
| 1136 |
-
|
| 1137 |
-
# Number of relevant examples inserted into each prompt.
|
| 1138 |
-
engine_state["reasoning_examples"] = 4
|
| 1139 |
-
|
| 1140 |
-
logger.info(
|
| 1141 |
-
f"Engine mapped to: {device}"
|
| 1142 |
-
)
|
| 1143 |
-
|
| 1144 |
-
# Build example embeddings once.
|
| 1145 |
-
build_reasoning_embeddings()
|
| 1146 |
-
|
| 1147 |
-
yield
|
| 1148 |
-
|
| 1149 |
-
engine_state.clear()
|
| 1150 |
|
| 1151 |
-
|
| 1152 |
-
"
|
|
|
|
|
|
|
| 1153 |
)
|
| 1154 |
|
|
|
|
| 1155 |
|
| 1156 |
-
# ============================================================
|
| 1157 |
-
# FASTAPI
|
| 1158 |
-
# ============================================================
|
| 1159 |
-
|
| 1160 |
-
app = FastAPI(
|
| 1161 |
-
title="Infinity GRM Engine",
|
| 1162 |
-
lifespan=lifespan
|
| 1163 |
-
)
|
| 1164 |
-
|
| 1165 |
-
|
| 1166 |
-
# ============================================================
|
| 1167 |
-
# CORS
|
| 1168 |
-
# ============================================================
|
| 1169 |
-
|
| 1170 |
-
app.add_middleware(
|
| 1171 |
-
CORSMiddleware,
|
| 1172 |
-
allow_origins=["*"],
|
| 1173 |
-
allow_credentials=True,
|
| 1174 |
-
allow_methods=["*"],
|
| 1175 |
-
allow_headers=["*"],
|
| 1176 |
-
)
|
| 1177 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1178 |
|
| 1179 |
-
|
| 1180 |
-
# REQUEST MODEL
|
| 1181 |
-
# ============================================================
|
| 1182 |
-
|
| 1183 |
-
class ChatRequest(BaseModel):
|
| 1184 |
-
message: str
|
| 1185 |
-
session_id: str
|
| 1186 |
-
|
| 1187 |
-
|
| 1188 |
-
# ============================================================
|
| 1189 |
-
# SESSION MANAGEMENT
|
| 1190 |
-
# ============================================================
|
| 1191 |
-
|
| 1192 |
-
def fetch_or_create_session(
|
| 1193 |
-
session_id: str
|
| 1194 |
-
) -> dict:
|
| 1195 |
-
|
| 1196 |
-
if session_id in session_store:
|
| 1197 |
-
return session_store[session_id]
|
| 1198 |
-
|
| 1199 |
-
default_session = {
|
| 1200 |
-
"history": [],
|
| 1201 |
-
"last_entity": "",
|
| 1202 |
-
"last_web_query": "",
|
| 1203 |
-
"last_response": "",
|
| 1204 |
-
"last_web_context": []
|
| 1205 |
-
}
|
| 1206 |
-
|
| 1207 |
-
session_store[session_id] = default_session
|
| 1208 |
-
|
| 1209 |
-
return default_session
|
| 1210 |
-
|
| 1211 |
-
|
| 1212 |
-
def save_session_to_redis(
|
| 1213 |
-
session_id: str,
|
| 1214 |
-
session: dict
|
| 1215 |
-
):
|
| 1216 |
-
"""
|
| 1217 |
-
Temporary in-memory storage.
|
| 1218 |
-
|
| 1219 |
-
Replace with Redis later.
|
| 1220 |
-
"""
|
| 1221 |
-
|
| 1222 |
-
session_store[session_id] = session
|
| 1223 |
-
|
| 1224 |
-
|
| 1225 |
-
def update_history_buffer(
|
| 1226 |
-
session: dict,
|
| 1227 |
-
role: str,
|
| 1228 |
-
content: str
|
| 1229 |
-
):
|
| 1230 |
-
|
| 1231 |
-
session["history"].append(
|
| 1232 |
-
{
|
| 1233 |
-
"role": role,
|
| 1234 |
-
"content": content
|
| 1235 |
-
}
|
| 1236 |
-
)
|
| 1237 |
-
|
| 1238 |
-
if len(session["history"]) > engine_state["max_history"]:
|
| 1239 |
-
session["history"].pop(0)
|
| 1240 |
-
|
| 1241 |
|
| 1242 |
-
|
| 1243 |
-
|
| 1244 |
-
# ============================================================
|
| 1245 |
|
| 1246 |
-
|
| 1247 |
-
a: str,
|
| 1248 |
-
b: str
|
| 1249 |
-
) -> float:
|
| 1250 |
|
| 1251 |
-
|
| 1252 |
|
| 1253 |
-
|
| 1254 |
-
[a, b],
|
| 1255 |
-
normalize_embeddings=True
|
| 1256 |
-
)
|
| 1257 |
|
| 1258 |
-
|
| 1259 |
-
|
| 1260 |
-
|
| 1261 |
-
embeddings[1]
|
| 1262 |
)
|
| 1263 |
-
)
|
| 1264 |
-
|
| 1265 |
-
|
| 1266 |
-
# ============================================================
|
| 1267 |
-
# FOLLOW-UP DETECTION
|
| 1268 |
-
# ============================================================
|
| 1269 |
-
|
| 1270 |
-
def checks_pronoun_reference(
|
| 1271 |
-
query: str
|
| 1272 |
-
) -> bool:
|
| 1273 |
-
|
| 1274 |
-
pronoun_references = {
|
| 1275 |
-
"he",
|
| 1276 |
-
"him",
|
| 1277 |
-
"his",
|
| 1278 |
-
"she",
|
| 1279 |
-
"her",
|
| 1280 |
-
"hers",
|
| 1281 |
-
"they",
|
| 1282 |
-
"them",
|
| 1283 |
-
"their",
|
| 1284 |
-
"it",
|
| 1285 |
-
"its",
|
| 1286 |
-
"that",
|
| 1287 |
-
"those",
|
| 1288 |
-
"this",
|
| 1289 |
-
"these",
|
| 1290 |
-
"then",
|
| 1291 |
-
"next",
|
| 1292 |
-
"after"
|
| 1293 |
-
}
|
| 1294 |
-
|
| 1295 |
-
return any(
|
| 1296 |
-
word in pronoun_references
|
| 1297 |
-
for word in query.lower().split()
|
| 1298 |
-
)
|
| 1299 |
-
|
| 1300 |
|
| 1301 |
-
|
| 1302 |
-
|
| 1303 |
-
session: dict
|
| 1304 |
-
) -> bool:
|
| 1305 |
-
|
| 1306 |
-
if not session["last_entity"]:
|
| 1307 |
-
return False
|
| 1308 |
-
|
| 1309 |
-
scores = []
|
| 1310 |
-
|
| 1311 |
-
try:
|
| 1312 |
-
scores.append(
|
| 1313 |
-
calculate_semantic_similarity(
|
| 1314 |
-
query,
|
| 1315 |
-
session["last_entity"]
|
| 1316 |
-
)
|
| 1317 |
)
|
| 1318 |
-
except Exception:
|
| 1319 |
-
pass
|
| 1320 |
-
|
| 1321 |
-
try:
|
| 1322 |
-
if session["last_response"]:
|
| 1323 |
-
scores.append(
|
| 1324 |
-
calculate_semantic_similarity(
|
| 1325 |
-
query,
|
| 1326 |
-
session["last_response"][:500]
|
| 1327 |
-
)
|
| 1328 |
-
)
|
| 1329 |
-
except Exception:
|
| 1330 |
-
pass
|
| 1331 |
-
|
| 1332 |
-
best_match = max(
|
| 1333 |
-
scores,
|
| 1334 |
-
default=0
|
| 1335 |
-
)
|
| 1336 |
-
|
| 1337 |
-
return (
|
| 1338 |
-
best_match > 0.45
|
| 1339 |
-
or len(query.split()) <= 3
|
| 1340 |
-
or checks_pronoun_reference(query)
|
| 1341 |
-
)
|
| 1342 |
|
|
|
|
|
|
|
| 1343 |
|
| 1344 |
-
|
| 1345 |
-
|
| 1346 |
-
# ============================================================
|
| 1347 |
-
|
| 1348 |
-
def execute_llm_generation(
|
| 1349 |
-
prompt: str,
|
| 1350 |
-
max_new_tokens: int = 2048
|
| 1351 |
-
) -> str:
|
| 1352 |
-
|
| 1353 |
-
tokenizer = engine_state["tokenizer"]
|
| 1354 |
-
model = engine_state["model"]
|
| 1355 |
-
|
| 1356 |
-
inputs = tokenizer(
|
| 1357 |
-
prompt,
|
| 1358 |
-
return_tensors="pt"
|
| 1359 |
-
)
|
| 1360 |
|
| 1361 |
-
|
| 1362 |
-
|
| 1363 |
-
|
| 1364 |
-
}
|
| 1365 |
-
|
| 1366 |
-
with torch.no_grad():
|
| 1367 |
-
|
| 1368 |
-
out = model.generate(
|
| 1369 |
-
**inputs,
|
| 1370 |
-
max_new_tokens=max_new_tokens,
|
| 1371 |
-
temperature=0.15,
|
| 1372 |
-
top_p=0.9,
|
| 1373 |
-
repetition_penalty=1.08,
|
| 1374 |
-
do_sample=True,
|
| 1375 |
-
pad_token_id=tokenizer.eos_token_id
|
| 1376 |
)
|
| 1377 |
|
| 1378 |
-
|
| 1379 |
-
0
|
| 1380 |
-
][
|
| 1381 |
-
inputs["input_ids"].shape[1]:
|
| 1382 |
-
]
|
| 1383 |
-
|
| 1384 |
-
return tokenizer.decode(
|
| 1385 |
-
generated_tokens,
|
| 1386 |
-
skip_special_tokens=True
|
| 1387 |
-
).strip()
|
| 1388 |
-
|
| 1389 |
|
| 1390 |
-
# ============================================================
|
| 1391 |
-
# TOPIC EXTRACTION
|
| 1392 |
-
# ============================================================
|
| 1393 |
|
| 1394 |
-
def
|
| 1395 |
-
|
|
|
|
| 1396 |
) -> str:
|
| 1397 |
|
| 1398 |
-
|
| 1399 |
-
Extract the primary subject of this question.
|
| 1400 |
|
| 1401 |
-
|
| 1402 |
-
|
| 1403 |
-
|
| 1404 |
-
Return only the subject.
|
| 1405 |
-
""".strip()
|
| 1406 |
|
| 1407 |
-
|
| 1408 |
-
|
| 1409 |
-
|
| 1410 |
-
).split("\n")[0].strip()
|
| 1411 |
-
|
| 1412 |
-
|
| 1413 |
-
# ============================================================
|
| 1414 |
-
# FOLLOW-UP QUERY REWRITING
|
| 1415 |
-
# ============================================================
|
| 1416 |
-
|
| 1417 |
-
def rewrite_followup_query(
|
| 1418 |
-
query: str,
|
| 1419 |
-
session: dict
|
| 1420 |
-
) -> str:
|
| 1421 |
-
|
| 1422 |
-
history = "\n".join(
|
| 1423 |
-
[
|
| 1424 |
-
f"User: {x['user']}\nSearch: {x['search']}"
|
| 1425 |
-
for x in session["last_web_context"][-5:]
|
| 1426 |
-
]
|
| 1427 |
)
|
| 1428 |
|
| 1429 |
-
|
| 1430 |
-
|
| 1431 |
-
{session["last_entity"]}
|
| 1432 |
-
|
| 1433 |
-
Recent conversation:
|
| 1434 |
-
{history}
|
| 1435 |
-
|
| 1436 |
-
User follow-up:
|
| 1437 |
-
{query}
|
| 1438 |
-
|
| 1439 |
-
Rewrite the follow-up as a standalone web search query.
|
| 1440 |
-
|
| 1441 |
-
Return only the search query.
|
| 1442 |
-
""".strip()
|
| 1443 |
-
|
| 1444 |
-
return execute_llm_generation(
|
| 1445 |
-
prompt,
|
| 1446 |
-
max_new_tokens=128
|
| 1447 |
-
).split("\n")[0].strip()
|
| 1448 |
-
|
| 1449 |
-
|
| 1450 |
-
# ============================================================
|
| 1451 |
-
# WEB SEARCH DETECTION
|
| 1452 |
-
# ============================================================
|
| 1453 |
-
|
| 1454 |
-
def should_trigger_web_search(
|
| 1455 |
-
q: str,
|
| 1456 |
-
session: dict
|
| 1457 |
-
) -> bool:
|
| 1458 |
-
|
| 1459 |
-
q = q.lower()
|
| 1460 |
-
|
| 1461 |
-
search_triggers = [
|
| 1462 |
-
"who",
|
| 1463 |
-
"what",
|
| 1464 |
-
"when",
|
| 1465 |
-
"where",
|
| 1466 |
-
"why",
|
| 1467 |
-
"how",
|
| 1468 |
-
"founder",
|
| 1469 |
-
"ceo",
|
| 1470 |
-
"company",
|
| 1471 |
-
"price",
|
| 1472 |
-
"stock",
|
| 1473 |
-
"news",
|
| 1474 |
-
"latest",
|
| 1475 |
-
"today",
|
| 1476 |
-
"current",
|
| 1477 |
-
"recent",
|
| 1478 |
-
"update",
|
| 1479 |
-
"code",
|
| 1480 |
-
"python",
|
| 1481 |
-
"html",
|
| 1482 |
-
"javascript",
|
| 1483 |
-
"documentation",
|
| 1484 |
-
"release",
|
| 1485 |
-
"version"
|
| 1486 |
-
]
|
| 1487 |
-
|
| 1488 |
-
if any(
|
| 1489 |
-
trigger in q
|
| 1490 |
-
for trigger in search_triggers
|
| 1491 |
-
):
|
| 1492 |
-
return True
|
| 1493 |
|
| 1494 |
-
return
|
| 1495 |
-
|
| 1496 |
-
|
| 1497 |
)
|
| 1498 |
|
| 1499 |
|
| 1500 |
# ============================================================
|
| 1501 |
-
#
|
| 1502 |
# ============================================================
|
| 1503 |
|
| 1504 |
def isolated_blocking_ddg(
|
| 1505 |
-
query_string: str
|
| 1506 |
) -> list:
|
| 1507 |
|
| 1508 |
try:
|
|
@@ -1511,45 +1535,44 @@ def isolated_blocking_ddg(
|
|
| 1511 |
|
| 1512 |
results = ddgs.text(
|
| 1513 |
query_string,
|
| 1514 |
-
max_results=
|
| 1515 |
)
|
| 1516 |
|
| 1517 |
if results:
|
| 1518 |
|
| 1519 |
-
|
| 1520 |
-
|
| 1521 |
-
|
| 1522 |
-
|
| 1523 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1524 |
|
| 1525 |
except Exception as e:
|
| 1526 |
|
| 1527 |
logger.error(
|
| 1528 |
-
|
|
|
|
| 1529 |
)
|
| 1530 |
|
| 1531 |
return []
|
| 1532 |
|
| 1533 |
|
| 1534 |
async def search_web_async(
|
| 1535 |
-
query: str
|
| 1536 |
) -> list:
|
| 1537 |
|
| 1538 |
urls = await asyncio.to_thread(
|
| 1539 |
isolated_blocking_ddg,
|
| 1540 |
-
query
|
| 1541 |
)
|
| 1542 |
|
| 1543 |
-
|
| 1544 |
-
|
| 1545 |
-
urls.append(
|
| 1546 |
-
"https://en.wikipedia.org/wiki/"
|
| 1547 |
-
+ query.replace(" ", "_")
|
| 1548 |
-
)
|
| 1549 |
-
|
| 1550 |
-
return list(
|
| 1551 |
-
dict.fromkeys(urls)
|
| 1552 |
-
)[:5]
|
| 1553 |
|
| 1554 |
|
| 1555 |
# ============================================================
|
|
@@ -1558,36 +1581,45 @@ async def search_web_async(
|
|
| 1558 |
|
| 1559 |
async def scrape_target_url(
|
| 1560 |
client,
|
| 1561 |
-
url: str
|
| 1562 |
):
|
| 1563 |
|
| 1564 |
try:
|
| 1565 |
|
| 1566 |
-
|
| 1567 |
url,
|
| 1568 |
-
timeout=
|
| 1569 |
)
|
| 1570 |
|
| 1571 |
-
if
|
| 1572 |
|
| 1573 |
logger.warning(
|
| 1574 |
-
|
|
|
|
|
|
|
| 1575 |
)
|
| 1576 |
|
| 1577 |
return None
|
| 1578 |
|
|
|
|
|
|
|
| 1579 |
text = None
|
| 1580 |
|
| 1581 |
try:
|
| 1582 |
|
| 1583 |
text = extract(
|
| 1584 |
-
|
|
|
|
|
|
|
|
|
|
| 1585 |
)
|
| 1586 |
|
| 1587 |
except Exception as e:
|
| 1588 |
|
| 1589 |
logger.warning(
|
| 1590 |
-
|
|
|
|
|
|
|
| 1591 |
)
|
| 1592 |
|
| 1593 |
if not text:
|
|
@@ -1595,454 +1627,31 @@ async def scrape_target_url(
|
|
| 1595 |
try:
|
| 1596 |
|
| 1597 |
soup = BeautifulSoup(
|
| 1598 |
-
|
| 1599 |
-
"html.parser"
|
| 1600 |
)
|
| 1601 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1602 |
text = soup.get_text(
|
| 1603 |
separator=" ",
|
| 1604 |
-
strip=True
|
| 1605 |
)
|
| 1606 |
|
| 1607 |
except Exception as e:
|
| 1608 |
|
| 1609 |
-
logger.
|
| 1610 |
-
|
| 1611 |
-
|
| 1612 |
-
|
| 1613 |
-
|
| 1614 |
-
|
| 1615 |
-
if not text or len(text.split()) < 20:
|
| 1616 |
-
return None
|
| 1617 |
-
|
| 1618 |
-
return {
|
| 1619 |
-
"url": url,
|
| 1620 |
-
"domain": urlparse(url).netloc,
|
| 1621 |
-
"text": text[:2500]
|
| 1622 |
-
}
|
| 1623 |
-
|
| 1624 |
-
except Exception as e:
|
| 1625 |
-
|
| 1626 |
-
logger.exception(
|
| 1627 |
-
f"Scraping error for {url}: {e}"
|
| 1628 |
-
)
|
| 1629 |
-
|
| 1630 |
-
return None
|
| 1631 |
-
|
| 1632 |
-
|
| 1633 |
-
# ============================================================
|
| 1634 |
-
# WEB CONTEXT PIPELINE
|
| 1635 |
-
# ============================================================
|
| 1636 |
-
|
| 1637 |
-
async def build_context_pipeline(
|
| 1638 |
-
query: str
|
| 1639 |
-
) -> tuple:
|
| 1640 |
-
|
| 1641 |
-
context_blocks = []
|
| 1642 |
-
extraction_sources = []
|
| 1643 |
-
|
| 1644 |
-
target_urls = await search_web_async(
|
| 1645 |
-
query
|
| 1646 |
-
)
|
| 1647 |
-
|
| 1648 |
-
headers = {
|
| 1649 |
-
"User-Agent":
|
| 1650 |
-
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) "
|
| 1651 |
-
"AppleWebKit/537.36 "
|
| 1652 |
-
"(KHTML, like Gecko) "
|
| 1653 |
-
"Chrome/122.0.0.0 Safari/537.36",
|
| 1654 |
-
|
| 1655 |
-
"Accept":
|
| 1656 |
-
"text/html,application/xhtml+xml,"
|
| 1657 |
-
"application/xml;q=0.9,image/avif,"
|
| 1658 |
-
"image/webp,*/*;q=0.8",
|
| 1659 |
-
|
| 1660 |
-
"Accept-Language":
|
| 1661 |
-
"en-US,en;q=0.9",
|
| 1662 |
-
|
| 1663 |
-
"Accept-Encoding":
|
| 1664 |
-
"gzip, deflate, br",
|
| 1665 |
-
|
| 1666 |
-
"Connection":
|
| 1667 |
-
"keep-alive",
|
| 1668 |
-
|
| 1669 |
-
"Upgrade-Insecure-Requests":
|
| 1670 |
-
"1"
|
| 1671 |
-
}
|
| 1672 |
-
|
| 1673 |
-
from curl_cffi.requests import AsyncSession
|
| 1674 |
-
|
| 1675 |
-
async with AsyncSession(
|
| 1676 |
-
impersonate="chrome",
|
| 1677 |
-
headers=headers
|
| 1678 |
-
) as client:
|
| 1679 |
-
|
| 1680 |
-
tasks = [
|
| 1681 |
-
scrape_target_url(
|
| 1682 |
-
client,
|
| 1683 |
-
u
|
| 1684 |
-
)
|
| 1685 |
-
for u in target_urls
|
| 1686 |
-
]
|
| 1687 |
-
|
| 1688 |
-
scraped_results = await asyncio.gather(
|
| 1689 |
-
*tasks
|
| 1690 |
-
)
|
| 1691 |
-
|
| 1692 |
-
for result in scraped_results:
|
| 1693 |
-
|
| 1694 |
-
if result:
|
| 1695 |
-
|
| 1696 |
-
context_blocks.append(
|
| 1697 |
-
f"[{result['domain']}] "
|
| 1698 |
-
f"{result['text']}"
|
| 1699 |
-
)
|
| 1700 |
-
|
| 1701 |
-
extraction_sources.append(
|
| 1702 |
-
result["url"]
|
| 1703 |
-
)
|
| 1704 |
-
|
| 1705 |
-
return (
|
| 1706 |
-
"\n\n".join(context_blocks),
|
| 1707 |
-
list(set(extraction_sources))
|
| 1708 |
-
)
|
| 1709 |
-
|
| 1710 |
-
|
| 1711 |
-
# ============================================================
|
| 1712 |
-
# MAIN ORCHESTRATION
|
| 1713 |
-
# ============================================================
|
| 1714 |
-
|
| 1715 |
-
async def process_ask_orchestration(
|
| 1716 |
-
q: str,
|
| 1717 |
-
session: dict
|
| 1718 |
-
) -> tuple:
|
| 1719 |
-
|
| 1720 |
-
use_web = should_trigger_web_search(
|
| 1721 |
-
q,
|
| 1722 |
-
session
|
| 1723 |
-
)
|
| 1724 |
-
|
| 1725 |
-
search_query = q
|
| 1726 |
-
|
| 1727 |
-
if (
|
| 1728 |
-
use_web
|
| 1729 |
-
and is_followup(q, session)
|
| 1730 |
-
and session["last_entity"]
|
| 1731 |
-
):
|
| 1732 |
-
|
| 1733 |
-
search_query = rewrite_followup_query(
|
| 1734 |
-
q,
|
| 1735 |
-
session
|
| 1736 |
-
)
|
| 1737 |
-
|
| 1738 |
-
context = ""
|
| 1739 |
-
sources = []
|
| 1740 |
-
|
| 1741 |
-
# --------------------------------------------------------
|
| 1742 |
-
# WEB SEARCH
|
| 1743 |
-
# --------------------------------------------------------
|
| 1744 |
-
|
| 1745 |
-
if use_web:
|
| 1746 |
-
|
| 1747 |
-
if not is_followup(q, session):
|
| 1748 |
-
|
| 1749 |
-
try:
|
| 1750 |
-
|
| 1751 |
-
session["last_entity"] = (
|
| 1752 |
-
run_topic_extraction(q)
|
| 1753 |
-
)
|
| 1754 |
-
|
| 1755 |
-
except Exception:
|
| 1756 |
-
|
| 1757 |
-
session["last_entity"] = q
|
| 1758 |
-
|
| 1759 |
-
context, sources = (
|
| 1760 |
-
await build_context_pipeline(
|
| 1761 |
-
search_query
|
| 1762 |
-
)
|
| 1763 |
-
)
|
| 1764 |
-
|
| 1765 |
-
if len(context.strip()) < 150:
|
| 1766 |
-
|
| 1767 |
-
context, sources = (
|
| 1768 |
-
await build_context_pipeline(
|
| 1769 |
-
search_query + " wikipedia"
|
| 1770 |
-
)
|
| 1771 |
-
)
|
| 1772 |
-
|
| 1773 |
-
session[
|
| 1774 |
-
"last_web_context"
|
| 1775 |
-
].append(
|
| 1776 |
-
{
|
| 1777 |
-
"user": q,
|
| 1778 |
-
"search": search_query
|
| 1779 |
-
}
|
| 1780 |
-
)
|
| 1781 |
-
|
| 1782 |
-
if len(
|
| 1783 |
-
session["last_web_context"]
|
| 1784 |
-
) > 5:
|
| 1785 |
-
|
| 1786 |
-
session[
|
| 1787 |
-
"last_web_context"
|
| 1788 |
-
].pop(0)
|
| 1789 |
-
|
| 1790 |
-
session[
|
| 1791 |
-
"last_web_query"
|
| 1792 |
-
] = search_query
|
| 1793 |
-
|
| 1794 |
-
# --------------------------------------------------------
|
| 1795 |
-
# RETRIEVE REASONING EXAMPLES
|
| 1796 |
-
# --------------------------------------------------------
|
| 1797 |
-
|
| 1798 |
-
reasoning_context = format_reasoning_examples(
|
| 1799 |
-
q,
|
| 1800 |
-
top_k=engine_state.get(
|
| 1801 |
-
"reasoning_examples",
|
| 1802 |
-
4
|
| 1803 |
-
)
|
| 1804 |
-
)
|
| 1805 |
-
|
| 1806 |
-
# --------------------------------------------------------
|
| 1807 |
-
# SYSTEM PROMPT
|
| 1808 |
-
# --------------------------------------------------------
|
| 1809 |
-
|
| 1810 |
-
system_prompt = """
|
| 1811 |
-
You are Infinity GRM, a capable reasoning and coding assistant.
|
| 1812 |
-
|
| 1813 |
-
Solve problems carefully rather than jumping to the first familiar
|
| 1814 |
-
algorithm.
|
| 1815 |
-
|
| 1816 |
-
For difficult problems:
|
| 1817 |
-
|
| 1818 |
-
1. Identify exactly what the problem requires.
|
| 1819 |
-
2. Identify important constraints and assumptions.
|
| 1820 |
-
3. Determine the key property or structure.
|
| 1821 |
-
4. Consider tempting approaches when useful.
|
| 1822 |
-
5. Reject an approach if its assumptions do not hold.
|
| 1823 |
-
6. Choose the appropriate algorithm or data structure.
|
| 1824 |
-
7. Check important edge cases.
|
| 1825 |
-
8. Verify the result.
|
| 1826 |
-
9. Give the final answer clearly.
|
| 1827 |
-
|
| 1828 |
-
For programming problems:
|
| 1829 |
-
- Prefer correct and efficient algorithms.
|
| 1830 |
-
- Respect the stated constraints.
|
| 1831 |
-
- Explain important implementation decisions.
|
| 1832 |
-
- State time and space complexity.
|
| 1833 |
-
- Do not claim an algorithm is O(n) if it contains a hidden
|
| 1834 |
-
O(n log n), O(n²), or repeated expensive operation.
|
| 1835 |
-
|
| 1836 |
-
Use the provided reasoning examples as patterns, not as facts that
|
| 1837 |
-
must be copied.
|
| 1838 |
-
|
| 1839 |
-
If web context is provided, use it when relevant.
|
| 1840 |
-
Do not invent information that contradicts the provided context.
|
| 1841 |
-
|
| 1842 |
-
Do not blindly follow an example if the current problem is different.
|
| 1843 |
-
""".strip()
|
| 1844 |
-
|
| 1845 |
-
# --------------------------------------------------------
|
| 1846 |
-
# BUILD MESSAGES
|
| 1847 |
-
# --------------------------------------------------------
|
| 1848 |
-
|
| 1849 |
-
messages = [
|
| 1850 |
-
{
|
| 1851 |
-
"role": "system",
|
| 1852 |
-
"content": system_prompt
|
| 1853 |
-
}
|
| 1854 |
-
]
|
| 1855 |
-
|
| 1856 |
-
# --------------------------------------------------------
|
| 1857 |
-
# REASONING EXAMPLES
|
| 1858 |
-
# --------------------------------------------------------
|
| 1859 |
-
|
| 1860 |
-
if reasoning_context:
|
| 1861 |
-
|
| 1862 |
-
messages.append(
|
| 1863 |
-
{
|
| 1864 |
-
"role": "system",
|
| 1865 |
-
"content":
|
| 1866 |
-
"RELEVANT REASONING EXAMPLES:\n\n"
|
| 1867 |
-
+ reasoning_context
|
| 1868 |
-
}
|
| 1869 |
-
)
|
| 1870 |
-
|
| 1871 |
-
# --------------------------------------------------------
|
| 1872 |
-
# WEB CONTEXT
|
| 1873 |
-
# --------------------------------------------------------
|
| 1874 |
-
|
| 1875 |
-
if use_web:
|
| 1876 |
-
|
| 1877 |
-
messages.extend(
|
| 1878 |
-
session["history"][-10:]
|
| 1879 |
-
)
|
| 1880 |
-
|
| 1881 |
-
messages.append(
|
| 1882 |
-
{
|
| 1883 |
-
"role": "user",
|
| 1884 |
-
"content":
|
| 1885 |
-
f"WEB CONTEXT:\n"
|
| 1886 |
-
f"{context}\n\n"
|
| 1887 |
-
f"QUESTION:\n"
|
| 1888 |
-
f"{q}"
|
| 1889 |
-
}
|
| 1890 |
-
)
|
| 1891 |
-
|
| 1892 |
-
else:
|
| 1893 |
-
|
| 1894 |
-
messages.extend(
|
| 1895 |
-
session["history"][-8:]
|
| 1896 |
-
)
|
| 1897 |
-
|
| 1898 |
-
messages.append(
|
| 1899 |
-
{
|
| 1900 |
-
"role": "user",
|
| 1901 |
-
"content": q
|
| 1902 |
-
}
|
| 1903 |
-
)
|
| 1904 |
-
|
| 1905 |
-
# --------------------------------------------------------
|
| 1906 |
-
# CHAT TEMPLATE
|
| 1907 |
-
# --------------------------------------------------------
|
| 1908 |
-
|
| 1909 |
-
compiled_prompt = (
|
| 1910 |
-
engine_state["tokenizer"]
|
| 1911 |
-
.apply_chat_template(
|
| 1912 |
-
messages,
|
| 1913 |
-
tokenize=False,
|
| 1914 |
-
add_generation_prompt=True
|
| 1915 |
-
)
|
| 1916 |
-
)
|
| 1917 |
-
|
| 1918 |
-
# --------------------------------------------------------
|
| 1919 |
-
# GENERATE
|
| 1920 |
-
# --------------------------------------------------------
|
| 1921 |
-
|
| 1922 |
-
response_string = execute_llm_generation(
|
| 1923 |
-
compiled_prompt,
|
| 1924 |
-
max_new_tokens=2048
|
| 1925 |
-
)
|
| 1926 |
-
|
| 1927 |
-
session[
|
| 1928 |
-
"last_response"
|
| 1929 |
-
] = response_string
|
| 1930 |
-
|
| 1931 |
-
update_history_buffer(
|
| 1932 |
-
session,
|
| 1933 |
-
"user",
|
| 1934 |
-
q
|
| 1935 |
-
)
|
| 1936 |
-
|
| 1937 |
-
update_history_buffer(
|
| 1938 |
-
session,
|
| 1939 |
-
"assistant",
|
| 1940 |
-
response_string
|
| 1941 |
-
)
|
| 1942 |
-
|
| 1943 |
-
return (
|
| 1944 |
-
response_string,
|
| 1945 |
-
sources
|
| 1946 |
-
)
|
| 1947 |
-
|
| 1948 |
-
|
| 1949 |
-
# ============================================================
|
| 1950 |
-
# API ENDPOINTS
|
| 1951 |
-
# ============================================================
|
| 1952 |
-
|
| 1953 |
-
@app.post("/query")
|
| 1954 |
-
async def chat_endpoint(
|
| 1955 |
-
request: ChatRequest
|
| 1956 |
-
):
|
| 1957 |
-
|
| 1958 |
-
try:
|
| 1959 |
-
|
| 1960 |
-
session = fetch_or_create_session(
|
| 1961 |
-
request.session_id
|
| 1962 |
-
)
|
| 1963 |
-
|
| 1964 |
-
answer, sources = (
|
| 1965 |
-
await process_ask_orchestration(
|
| 1966 |
-
request.message,
|
| 1967 |
-
session
|
| 1968 |
-
)
|
| 1969 |
-
)
|
| 1970 |
-
|
| 1971 |
-
save_session_to_redis(
|
| 1972 |
-
request.session_id,
|
| 1973 |
-
session
|
| 1974 |
-
)
|
| 1975 |
-
|
| 1976 |
-
return {
|
| 1977 |
-
"response": answer,
|
| 1978 |
-
"sources": sources
|
| 1979 |
-
}
|
| 1980 |
-
|
| 1981 |
-
except Exception as e:
|
| 1982 |
-
|
| 1983 |
-
logger.exception(
|
| 1984 |
-
"Critical exception inside chat runtime pipeline"
|
| 1985 |
-
)
|
| 1986 |
-
|
| 1987 |
-
raise HTTPException(
|
| 1988 |
-
status_code=500,
|
| 1989 |
-
detail=str(e)
|
| 1990 |
-
)
|
| 1991 |
-
|
| 1992 |
-
|
| 1993 |
-
@app.get("/health")
|
| 1994 |
-
async def validation_heartbeat():
|
| 1995 |
-
|
| 1996 |
-
return {
|
| 1997 |
-
"status": "healthy",
|
| 1998 |
-
"gpu_acceleration_active":
|
| 1999 |
-
torch.cuda.is_available(),
|
| 2000 |
-
"reasoning_examples":
|
| 2001 |
-
len(REASONING_EXAMPLES)
|
| 2002 |
-
}
|
| 2003 |
-
|
| 2004 |
-
|
| 2005 |
-
# ============================================================
|
| 2006 |
-
# STATIC FILES
|
| 2007 |
-
# ============================================================
|
| 2008 |
-
|
| 2009 |
-
if not os.path.exists("static"):
|
| 2010 |
-
os.makedirs("static")
|
| 2011 |
-
|
| 2012 |
-
|
| 2013 |
-
app.mount(
|
| 2014 |
-
"/static",
|
| 2015 |
-
StaticFiles(directory="static"),
|
| 2016 |
-
name="static"
|
| 2017 |
-
)
|
| 2018 |
-
|
| 2019 |
-
|
| 2020 |
-
@app.get("/")
|
| 2021 |
-
async def home_route_processor():
|
| 2022 |
-
|
| 2023 |
-
return {
|
| 2024 |
-
"message":
|
| 2025 |
-
"Infinity GRM Engine Core is active.",
|
| 2026 |
-
"reasoning_examples":
|
| 2027 |
-
len(REASONING_EXAMPLES)
|
| 2028 |
-
}
|
| 2029 |
-
|
| 2030 |
-
|
| 2031 |
-
# ============================================================
|
| 2032 |
-
# HUGGING FACE PORT
|
| 2033 |
-
# ============================================================
|
| 2034 |
-
|
| 2035 |
-
if __name__ == "__main__":
|
| 2036 |
-
|
| 2037 |
-
port = int(
|
| 2038 |
-
os.getenv(
|
| 2039 |
-
"PORT",
|
| 2040 |
-
7860
|
| 2041 |
-
)
|
| 2042 |
-
)
|
| 2043 |
-
|
| 2044 |
-
uvicorn.run(
|
| 2045 |
-
"main:app",
|
| 2046 |
-
host="0.0.0.0",
|
| 2047 |
-
port=port
|
| 2048 |
-
)
|
|
|
|
| 1 |
import os
|
| 2 |
+
import re
|
| 3 |
import asyncio
|
| 4 |
import logging
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 5 |
from contextlib import asynccontextmanager
|
| 6 |
from urllib.parse import urlparse
|
| 7 |
|
| 8 |
+
import numpy as np
|
| 9 |
+
import torch
|
| 10 |
+
import uvicorn
|
| 11 |
+
|
| 12 |
from bs4 import BeautifulSoup
|
| 13 |
from trafilatura import extract
|
| 14 |
from ddgs import DDGS
|
|
|
|
| 18 |
from fastapi.staticfiles import StaticFiles
|
| 19 |
|
| 20 |
from pydantic import BaseModel
|
| 21 |
+
from sentence_transformers import SentenceTransformer, CrossEncoder
|
| 22 |
from transformers import (
|
| 23 |
AutoTokenizer,
|
| 24 |
AutoModelForCausalLM,
|
| 25 |
+
AutoConfig,
|
| 26 |
+
)
|
| 27 |
+
|
| 28 |
+
|
| 29 |
+
# ============================================================
|
| 30 |
+
# CONFIGURATION
|
| 31 |
+
# ============================================================
|
| 32 |
+
|
| 33 |
+
MODEL_NAME = os.getenv(
|
| 34 |
+
"MODEL_NAME",
|
| 35 |
+
"Qwen/Qwen2.5-7B-Instruct"
|
| 36 |
+
)
|
| 37 |
+
|
| 38 |
+
EMBED_MODEL_NAME = os.getenv(
|
| 39 |
+
"EMBED_MODEL_NAME",
|
| 40 |
+
"sentence-transformers/all-MiniLM-L6-v2"
|
| 41 |
+
)
|
| 42 |
+
|
| 43 |
+
RERANKER_MODEL_NAME = os.getenv(
|
| 44 |
+
"RERANKER_MODEL_NAME",
|
| 45 |
+
"BAAI/bge-reranker-base"
|
| 46 |
+
)
|
| 47 |
+
|
| 48 |
+
MAX_HISTORY_MESSAGES = int(
|
| 49 |
+
os.getenv("MAX_HISTORY_MESSAGES", "8")
|
| 50 |
+
)
|
| 51 |
+
|
| 52 |
+
MAX_REASONING_EXAMPLES = int(
|
| 53 |
+
os.getenv("MAX_REASONING_EXAMPLES", "4")
|
| 54 |
+
)
|
| 55 |
+
|
| 56 |
+
MAX_WEB_RESULTS = int(
|
| 57 |
+
os.getenv("MAX_WEB_RESULTS", "5")
|
| 58 |
+
)
|
| 59 |
+
|
| 60 |
+
MAX_WEB_CHUNKS = int(
|
| 61 |
+
os.getenv("MAX_WEB_CHUNKS", "6")
|
| 62 |
+
)
|
| 63 |
+
|
| 64 |
+
WEB_CHUNK_WORDS = int(
|
| 65 |
+
os.getenv("WEB_CHUNK_WORDS", "500")
|
| 66 |
+
)
|
| 67 |
+
|
| 68 |
+
WEB_CHUNK_OVERLAP = int(
|
| 69 |
+
os.getenv("WEB_CHUNK_OVERLAP", "75")
|
| 70 |
+
)
|
| 71 |
+
|
| 72 |
+
MAX_NEW_TOKENS = int(
|
| 73 |
+
os.getenv("MAX_NEW_TOKENS", "2048")
|
| 74 |
+
)
|
| 75 |
+
|
| 76 |
+
MAX_INPUT_TOKENS = int(
|
| 77 |
+
os.getenv("MAX_INPUT_TOKENS", "12000")
|
| 78 |
+
)
|
| 79 |
+
|
| 80 |
+
RAG_MIN_SIMILARITY = float(
|
| 81 |
+
os.getenv("RAG_MIN_SIMILARITY", "0.28")
|
| 82 |
+
)
|
| 83 |
+
|
| 84 |
+
RERANK_MIN_SCORE = float(
|
| 85 |
+
os.getenv("RERANK_MIN_SCORE", "-5.0")
|
| 86 |
)
|
| 87 |
|
| 88 |
|
|
|
|
| 92 |
|
| 93 |
logging.basicConfig(
|
| 94 |
level=logging.INFO,
|
| 95 |
+
format="%(asctime)s - %(levelname)s - %(message)s",
|
| 96 |
)
|
| 97 |
|
| 98 |
logger = logging.getLogger("infinitygrm")
|
|
|
|
| 104 |
|
| 105 |
engine_state = {}
|
| 106 |
|
|
|
|
|
|
|
| 107 |
session_store = {}
|
| 108 |
|
| 109 |
+
reasoning_embeddings = None
|
| 110 |
+
|
| 111 |
|
| 112 |
# ============================================================
|
| 113 |
# REASONING EXAMPLES
|
| 114 |
#
|
| 115 |
+
# These examples teach STRUCTURES and PATTERNS.
|
| 116 |
#
|
| 117 |
+
# They are NOT required for the model to answer.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 118 |
#
|
| 119 |
+
# The model is explicitly instructed to solve novel problems
|
| 120 |
+
# from first principles when no relevant example exists.
|
| 121 |
# ============================================================
|
| 122 |
|
| 123 |
REASONING_EXAMPLES = [
|
| 124 |
|
| 125 |
{
|
| 126 |
+
"category": "arrays",
|
| 127 |
+
"subcategories": [
|
| 128 |
+
"prefix_sum",
|
| 129 |
+
"hashmap",
|
| 130 |
+
"subarray",
|
| 131 |
+
],
|
| 132 |
"problem": """
|
| 133 |
Given an array of integers that may contain negative numbers,
|
| 134 |
find the longest contiguous subarray whose sum equals k.
|
| 135 |
""",
|
| 136 |
"reasoning": """
|
| 137 |
+
Key observation: negative numbers make a normal sliding window
|
| 138 |
+
unreliable because the sum is not monotonic.
|
|
|
|
|
|
|
| 139 |
|
| 140 |
+
Use prefix sums. If the current prefix sum is S, an earlier prefix
|
| 141 |
+
sum of S-k means the elements between those positions sum to k.
|
| 142 |
|
| 143 |
+
Store the earliest index at which each prefix sum appears because
|
| 144 |
+
the earliest occurrence produces the longest possible subarray.
|
|
|
|
| 145 |
""",
|
| 146 |
"answer": """
|
| 147 |
Use a hashmap of prefix sums.
|
|
|
|
| 150 |
running_sum = 0
|
| 151 |
best = 0
|
| 152 |
|
| 153 |
+
For each index:
|
| 154 |
running_sum += nums[i]
|
| 155 |
|
| 156 |
if running_sum - k exists:
|
| 157 |
+
update best
|
| 158 |
|
| 159 |
+
store running_sum only if it has not appeared before.
|
| 160 |
|
| 161 |
Time: O(n) average.
|
| 162 |
Space: O(n).
|
| 163 |
+
""",
|
| 164 |
},
|
| 165 |
|
| 166 |
{
|
| 167 |
"category": "graphs",
|
| 168 |
+
"subcategories": [
|
| 169 |
+
"shortest_path",
|
| 170 |
+
"state_expansion",
|
| 171 |
+
"dijkstra",
|
| 172 |
+
],
|
| 173 |
"problem": """
|
| 174 |
A directed graph has positive edge costs. You may use a discount
|
| 175 |
exactly once, causing one chosen edge to cost floor(cost / 2).
|
| 176 |
Find the minimum cost from s to t.
|
| 177 |
""",
|
| 178 |
"reasoning": """
|
| 179 |
+
The important property is that whether the discount has already been
|
| 180 |
+
used changes the future choices.
|
| 181 |
|
| 182 |
+
Therefore node alone is not a sufficient state.
|
| 183 |
|
| 184 |
+
Represent the state as:
|
| 185 |
+
(node, discount_unused)
|
| 186 |
+
(node, discount_used)
|
| 187 |
|
| 188 |
+
From the unused state, an edge can be traversed normally or with the
|
| 189 |
+
discount. From the used state, it must be traversed normally.
|
| 190 |
|
| 191 |
+
Because all resulting costs are non-negative, Dijkstra can be run on
|
| 192 |
+
this expanded state graph.
|
| 193 |
""",
|
| 194 |
"answer": """
|
| 195 |
+
Run Dijkstra over the expanded state space.
|
|
|
|
|
|
|
|
|
|
|
|
|
| 196 |
|
| 197 |
+
Time: O((V + E) log V).
|
| 198 |
+
Space: O(V + E).
|
| 199 |
+
""",
|
|
|
|
|
|
|
| 200 |
},
|
| 201 |
|
| 202 |
{
|
| 203 |
+
"category": "graphs",
|
| 204 |
+
"subcategories": [
|
| 205 |
+
"cycle_detection",
|
| 206 |
+
"dfs",
|
| 207 |
+
"directed_graph",
|
| 208 |
+
],
|
| 209 |
"problem": """
|
| 210 |
Determine whether a directed graph contains a cycle in O(V+E).
|
| 211 |
""",
|
| 212 |
"reasoning": """
|
| 213 |
+
A simple visited set is insufficient for directed-cycle detection.
|
|
|
|
|
|
|
|
|
|
| 214 |
|
| 215 |
Use three states:
|
| 216 |
0 = unvisited
|
| 217 |
1 = currently exploring
|
| 218 |
2 = completely processed
|
| 219 |
|
| 220 |
+
During DFS, an edge to a state-1 vertex is a back edge and proves the
|
| 221 |
+
existence of a cycle.
|
| 222 |
+
|
| 223 |
+
A state-2 vertex does not indicate a cycle in the current DFS path.
|
| 224 |
""",
|
| 225 |
"answer": """
|
| 226 |
Use DFS with three colors/states.
|
| 227 |
|
| 228 |
+
Time: O(V + E).
|
| 229 |
Space: O(V).
|
| 230 |
+
""",
|
| 231 |
},
|
| 232 |
|
| 233 |
{
|
| 234 |
"category": "data_structures",
|
| 235 |
+
"subcategories": [
|
| 236 |
+
"hashmap",
|
| 237 |
+
"frequency",
|
| 238 |
+
"strings",
|
| 239 |
+
],
|
| 240 |
"problem": """
|
| 241 |
Find the first non-repeating character in a string.
|
| 242 |
""",
|
| 243 |
"reasoning": """
|
| 244 |
The answer depends on both frequency and original order.
|
| 245 |
|
| 246 |
+
First count every character. Then scan the string again and return
|
| 247 |
+
the first character whose frequency is one.
|
|
|
|
| 248 |
""",
|
| 249 |
"answer": """
|
| 250 |
Use a frequency hashmap followed by a second scan.
|
| 251 |
|
| 252 |
Time: O(n).
|
| 253 |
Space: O(k), where k is the number of distinct characters.
|
| 254 |
+
""",
|
| 255 |
},
|
| 256 |
|
| 257 |
{
|
| 258 |
+
"category": "arrays",
|
| 259 |
+
"subcategories": [
|
| 260 |
+
"two_pointers",
|
| 261 |
+
"sorted_array",
|
| 262 |
+
],
|
| 263 |
"problem": """
|
| 264 |
Given a sorted array, determine whether two numbers sum to target.
|
| 265 |
""",
|
| 266 |
"reasoning": """
|
| 267 |
+
The sorted order lets us eliminate entire ranges of candidates.
|
|
|
|
| 268 |
|
| 269 |
+
If the sum is too small, move the left pointer right.
|
| 270 |
+
If the sum is too large, move the right pointer left.
|
| 271 |
+
If it equals target, the pair exists.
|
| 272 |
""",
|
| 273 |
"answer": """
|
| 274 |
Use two pointers.
|
| 275 |
|
| 276 |
Time: O(n).
|
| 277 |
Space: O(1).
|
| 278 |
+
""",
|
| 279 |
},
|
| 280 |
|
| 281 |
{
|
| 282 |
+
"category": "graphs",
|
| 283 |
+
"subcategories": [
|
| 284 |
+
"bfs",
|
| 285 |
+
"unweighted_shortest_path",
|
| 286 |
+
],
|
| 287 |
"problem": """
|
| 288 |
Find the shortest path in an unweighted graph.
|
| 289 |
""",
|
| 290 |
"reasoning": """
|
| 291 |
Every edge has equal cost.
|
| 292 |
|
| 293 |
+
BFS explores vertices by increasing number of edges from the source.
|
| 294 |
+
Therefore the first time a vertex is reached, its distance is
|
| 295 |
+
minimal.
|
| 296 |
""",
|
| 297 |
"answer": """
|
| 298 |
Use BFS with a queue.
|
| 299 |
|
| 300 |
+
Time: O(V + E).
|
| 301 |
Space: O(V).
|
| 302 |
+
""",
|
| 303 |
},
|
| 304 |
|
| 305 |
{
|
| 306 |
+
"category": "graphs",
|
| 307 |
+
"subcategories": [
|
| 308 |
+
"dijkstra",
|
| 309 |
+
"weighted_shortest_path",
|
| 310 |
+
],
|
| 311 |
"problem": """
|
| 312 |
Find the shortest path in a graph with positive weighted edges.
|
| 313 |
""",
|
| 314 |
"reasoning": """
|
| 315 |
BFS is not sufficient because edges have different costs.
|
| 316 |
|
| 317 |
+
With positive weights, Dijkstra's greedy choice is valid: the
|
| 318 |
+
currently smallest unsettled distance cannot later be improved by a
|
| 319 |
+
path containing additional non-negative edges.
|
| 320 |
""",
|
| 321 |
"answer": """
|
| 322 |
Use Dijkstra with a min-heap.
|
| 323 |
|
| 324 |
Typical complexity:
|
| 325 |
+
O((V + E) log V).
|
| 326 |
+
""",
|
| 327 |
},
|
| 328 |
|
| 329 |
{
|
| 330 |
+
"category": "graphs",
|
| 331 |
+
"subcategories": [
|
| 332 |
+
"0_1_bfs",
|
| 333 |
+
"shortest_path",
|
| 334 |
+
],
|
| 335 |
"problem": """
|
| 336 |
A graph has only edge weights 0 and 1. Find shortest paths.
|
| 337 |
""",
|
| 338 |
"reasoning": """
|
| 339 |
+
Ordinary BFS treats all edges equally, which is incorrect.
|
| 340 |
|
| 341 |
+
Dijkstra works, but the special weights allow a deque:
|
| 342 |
+
zero-cost edges go to the front and one-cost edges go to the back.
|
| 343 |
|
| 344 |
+
This preserves increasing distance order without a heap.
|
|
|
|
| 345 |
""",
|
| 346 |
"answer": """
|
| 347 |
Use 0-1 BFS.
|
| 348 |
|
| 349 |
+
Time: O(V + E).
|
| 350 |
Space: O(V).
|
| 351 |
+
""",
|
| 352 |
},
|
| 353 |
|
| 354 |
{
|
| 355 |
"category": "dynamic_programming",
|
| 356 |
+
"subcategories": [
|
| 357 |
+
"linear_dp",
|
| 358 |
+
"state_transition",
|
| 359 |
+
],
|
| 360 |
"problem": """
|
| 361 |
Given positive integers, maximize the sum without selecting adjacent
|
| 362 |
elements.
|
| 363 |
""",
|
| 364 |
"reasoning": """
|
| 365 |
+
At each element there are two choices.
|
| 366 |
|
| 367 |
+
Skip it:
|
| 368 |
+
dp[i] = dp[i-1]
|
|
|
|
| 369 |
|
| 370 |
+
Take it:
|
| 371 |
+
dp[i] = dp[i-2] + nums[i]
|
| 372 |
+
|
| 373 |
+
Therefore:
|
| 374 |
|
| 375 |
dp[i] = max(dp[i-1], dp[i-2] + nums[i])
|
| 376 |
|
| 377 |
+
Only the previous two states are required.
|
| 378 |
""",
|
| 379 |
"answer": """
|
| 380 |
+
Use dynamic programming with two rolling values.
|
| 381 |
|
| 382 |
Time: O(n).
|
| 383 |
Space: O(1).
|
| 384 |
+
""",
|
| 385 |
},
|
| 386 |
|
| 387 |
{
|
| 388 |
"category": "binary_search",
|
| 389 |
+
"subcategories": [
|
| 390 |
+
"search_on_answer",
|
| 391 |
+
"monotonicity",
|
| 392 |
+
],
|
| 393 |
"problem": """
|
| 394 |
Find the minimum integer x for which a feasibility condition becomes
|
| 395 |
true.
|
| 396 |
""",
|
| 397 |
"reasoning": """
|
| 398 |
+
Binary search on the answer is valid only when feasibility is
|
| 399 |
+
monotonic.
|
| 400 |
|
| 401 |
+
If x is feasible, every larger value must also be feasible, creating
|
| 402 |
+
a false-to-true boundary.
|
| 403 |
|
| 404 |
+
Search for the first feasible value.
|
| 405 |
""",
|
| 406 |
"answer": """
|
| 407 |
Use binary search on the answer.
|
| 408 |
|
| 409 |
+
Perform one feasibility check per midpoint.
|
| 410 |
|
| 411 |
+
Complexity:
|
| 412 |
+
O(log C) feasibility checks, where C is the search range.
|
| 413 |
+
""",
|
| 414 |
},
|
| 415 |
|
| 416 |
{
|
| 417 |
"category": "algorithm_selection",
|
| 418 |
+
"subcategories": [
|
| 419 |
+
"binary_search",
|
| 420 |
+
"monotonicity",
|
| 421 |
+
],
|
| 422 |
"problem": """
|
| 423 |
Should binary search automatically be used whenever an answer is a
|
| 424 |
number?
|
|
|
|
| 426 |
"reasoning": """
|
| 427 |
No.
|
| 428 |
|
| 429 |
+
A numeric answer is not sufficient.
|
|
|
|
| 430 |
|
| 431 |
+
Binary search requires an ordered search space or a monotonic
|
| 432 |
+
predicate that creates a usable boundary.
|
| 433 |
""",
|
| 434 |
"answer": """
|
| 435 |
Do not automatically use binary search.
|
| 436 |
|
| 437 |
+
Prove monotonicity first.
|
| 438 |
+
""",
|
| 439 |
},
|
| 440 |
|
| 441 |
{
|
| 442 |
"category": "debugging",
|
| 443 |
+
"subcategories": [
|
| 444 |
+
"control_flow",
|
| 445 |
+
"python",
|
| 446 |
+
],
|
| 447 |
"problem": """
|
| 448 |
A function returns the first unique character but returns None too
|
| 449 |
early.
|
|
|
|
| 458 |
"reasoning": """
|
| 459 |
The return None statement is inside the loop.
|
| 460 |
|
| 461 |
+
Therefore the function terminates after checking only the first
|
| 462 |
+
non-unique character.
|
| 463 |
|
| 464 |
+
The counting logic is not necessarily wrong; the control flow is.
|
| 465 |
""",
|
| 466 |
"answer": """
|
| 467 |
Move return None outside the loop.
|
| 468 |
+
""",
|
| 469 |
},
|
| 470 |
|
| 471 |
{
|
| 472 |
"category": "complexity",
|
| 473 |
+
"subcategories": [
|
| 474 |
+
"nested_work",
|
| 475 |
+
"list_membership",
|
| 476 |
+
],
|
| 477 |
"problem": """
|
| 478 |
What is the complexity of:
|
| 479 |
|
|
|
|
| 483 |
result.append(x)
|
| 484 |
""",
|
| 485 |
"reasoning": """
|
| 486 |
+
Membership testing in a Python list is O(n) in the worst case.
|
| 487 |
|
| 488 |
+
That operation can occur once for every input element.
|
| 489 |
|
| 490 |
+
Therefore the overall worst-case complexity is O(n²).
|
| 491 |
""",
|
| 492 |
"answer": """
|
| 493 |
Worst case:
|
| 494 |
+
|
| 495 |
Time: O(n²)
|
| 496 |
Space: O(n)
|
| 497 |
|
| 498 |
+
A set can provide average O(1) membership testing.
|
| 499 |
+
""",
|
| 500 |
},
|
| 501 |
|
| 502 |
{
|
| 503 |
"category": "optimization",
|
| 504 |
+
"subcategories": [
|
| 505 |
+
"hash_set",
|
| 506 |
+
"deduplication",
|
| 507 |
+
],
|
| 508 |
"problem": """
|
| 509 |
Remove duplicates from a list while preserving original order.
|
| 510 |
""",
|
| 511 |
"reasoning": """
|
| 512 |
+
The result must preserve the first occurrence while membership
|
| 513 |
+
testing needs to be fast.
|
| 514 |
|
| 515 |
+
Use a set for membership and a list for output ordering.
|
|
|
|
| 516 |
""",
|
| 517 |
"answer": """
|
| 518 |
seen = set()
|
|
|
|
| 525 |
|
| 526 |
Average time: O(n).
|
| 527 |
Space: O(n).
|
| 528 |
+
""",
|
| 529 |
},
|
| 530 |
|
| 531 |
{
|
| 532 |
"category": "stacks",
|
| 533 |
+
"subcategories": [
|
| 534 |
+
"parentheses",
|
| 535 |
+
"matching",
|
| 536 |
+
],
|
| 537 |
"problem": """
|
| 538 |
Determine whether parentheses, brackets, and braces are correctly
|
| 539 |
balanced and nested.
|
|
|
|
| 543 |
|
| 544 |
That is last-in-first-out behavior, so a stack is appropriate.
|
| 545 |
|
| 546 |
+
Opening brackets are pushed. Closing brackets must match the stack
|
| 547 |
+
top.
|
| 548 |
""",
|
| 549 |
"answer": """
|
| 550 |
Use a stack.
|
| 551 |
|
| 552 |
Time: O(n).
|
| 553 |
Space: O(n).
|
| 554 |
+
""",
|
| 555 |
},
|
| 556 |
|
| 557 |
{
|
| 558 |
"category": "sliding_window",
|
| 559 |
+
"subcategories": [
|
| 560 |
+
"strings",
|
| 561 |
+
"two_pointers",
|
| 562 |
+
],
|
| 563 |
"problem": """
|
| 564 |
Find the longest substring without repeating characters.
|
| 565 |
""",
|
| 566 |
"reasoning": """
|
| 567 |
Maintain a window containing unique characters.
|
| 568 |
|
| 569 |
+
When a duplicate appears, move the left boundary beyond the previous
|
| 570 |
+
occurrence of that character.
|
| 571 |
|
| 572 |
+
Both boundaries only move forward.
|
|
|
|
| 573 |
""",
|
| 574 |
"answer": """
|
| 575 |
+
Use a sliding window with a hashmap.
|
| 576 |
|
| 577 |
Time: O(n).
|
| 578 |
Space: O(k).
|
| 579 |
+
""",
|
| 580 |
},
|
| 581 |
|
| 582 |
{
|
| 583 |
+
"category": "selection",
|
| 584 |
+
"subcategories": [
|
| 585 |
+
"quickselect",
|
| 586 |
+
"order_statistics",
|
| 587 |
+
],
|
| 588 |
"problem": """
|
| 589 |
Find the kth largest element without fully sorting the array.
|
| 590 |
""",
|
| 591 |
"reasoning": """
|
| 592 |
+
Full sorting performs more work than necessary because only one order
|
| 593 |
+
statistic is needed.
|
| 594 |
|
| 595 |
+
Quickselect partitions around a pivot and continues only on the side
|
| 596 |
+
containing the desired position.
|
| 597 |
""",
|
| 598 |
"answer": """
|
| 599 |
+
Use Quickselect.
|
|
|
|
|
|
|
|
|
|
| 600 |
|
| 601 |
+
Average time: O(n).
|
| 602 |
+
Worst-case time: O(n²) without appropriate pivot guarantees.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 603 |
""",
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 604 |
},
|
| 605 |
|
| 606 |
{
|
| 607 |
"category": "trees",
|
| 608 |
+
"subcategories": [
|
| 609 |
+
"dfs",
|
| 610 |
+
"recursion",
|
| 611 |
+
],
|
| 612 |
"problem": """
|
| 613 |
Find the maximum depth of a binary tree.
|
| 614 |
""",
|
| 615 |
"reasoning": """
|
| 616 |
The depth of a node is one plus the maximum depth of its children.
|
| 617 |
|
| 618 |
+
This gives the recurrence:
|
| 619 |
+
|
| 620 |
+
depth(node) =
|
| 621 |
+
1 + max(depth(left), depth(right))
|
| 622 |
""",
|
| 623 |
"answer": """
|
| 624 |
+
Use recursive DFS.
|
| 625 |
|
| 626 |
Time: O(n).
|
| 627 |
+
Space: O(h), where h is the tree height.
|
| 628 |
+
""",
|
| 629 |
},
|
| 630 |
|
| 631 |
{
|
| 632 |
"category": "trees",
|
| 633 |
+
"subcategories": [
|
| 634 |
+
"postorder",
|
| 635 |
+
"height",
|
| 636 |
+
"balance",
|
| 637 |
+
],
|
| 638 |
"problem": """
|
| 639 |
Determine whether a binary tree is height-balanced.
|
| 640 |
""",
|
| 641 |
"reasoning": """
|
| 642 |
+
Computing the height independently for every node can repeatedly
|
| 643 |
traverse the same subtrees.
|
| 644 |
|
| 645 |
+
Calculate heights bottom-up instead.
|
| 646 |
|
| 647 |
+
Return failure immediately if a subtree is unbalanced.
|
| 648 |
""",
|
| 649 |
"answer": """
|
| 650 |
Use postorder DFS.
|
| 651 |
|
| 652 |
+
Return the subtree height or a sentinel indicating imbalance.
|
| 653 |
|
| 654 |
Time: O(n).
|
| 655 |
Space: O(h).
|
| 656 |
+
""",
|
| 657 |
},
|
| 658 |
|
| 659 |
{
|
| 660 |
"category": "greedy",
|
| 661 |
+
"subcategories": [
|
| 662 |
+
"proof",
|
| 663 |
+
"exchange_argument",
|
| 664 |
+
],
|
| 665 |
"problem": """
|
| 666 |
Can you always solve an optimization problem greedily by taking the
|
| 667 |
locally cheapest option?
|
|
|
|
| 669 |
"reasoning": """
|
| 670 |
No.
|
| 671 |
|
| 672 |
+
A local choice can prevent a better global solution.
|
| 673 |
|
| 674 |
A greedy algorithm needs a proof that the local choice is safe,
|
| 675 |
+
often through an exchange argument or another structural property.
|
| 676 |
""",
|
| 677 |
"answer": """
|
| 678 |
Do not assume greedy works.
|
| 679 |
+
|
| 680 |
+
Prove the greedy choice is safe or construct a counterexample.
|
| 681 |
+
""",
|
| 682 |
},
|
| 683 |
|
| 684 |
{
|
| 685 |
"category": "algorithm_selection",
|
| 686 |
+
"subcategories": [
|
| 687 |
+
"state_design",
|
| 688 |
+
"dynamic_programming",
|
| 689 |
+
],
|
| 690 |
"problem": """
|
| 691 |
+
A state can be reached multiple ways, and future options depend on
|
| 692 |
+
how the state was reached. Should you store only one distance per
|
| 693 |
+
node?
|
| 694 |
""",
|
| 695 |
"reasoning": """
|
| 696 |
Not necessarily.
|
| 697 |
|
| 698 |
+
If future behavior depends on additional information, that
|
| 699 |
information is part of the state.
|
| 700 |
|
| 701 |
+
For example, whether a discount has been used can distinguish two
|
| 702 |
+
otherwise identical graph nodes.
|
| 703 |
""",
|
| 704 |
"answer": """
|
| 705 |
Expand the state representation.
|
| 706 |
|
| 707 |
+
Use something like:
|
| 708 |
+
(node, extra_state)
|
| 709 |
+
instead of only:
|
| 710 |
+
node
|
| 711 |
+
""",
|
| 712 |
},
|
| 713 |
|
| 714 |
{
|
| 715 |
"category": "verification",
|
| 716 |
+
"subcategories": [
|
| 717 |
+
"testing",
|
| 718 |
+
"edge_cases",
|
| 719 |
+
],
|
| 720 |
"problem": """
|
| 721 |
How should a newly written algorithm be tested?
|
| 722 |
""",
|
| 723 |
"reasoning": """
|
| 724 |
+
Normal examples are insufficient.
|
| 725 |
|
| 726 |
+
Test:
|
| 727 |
+
- smallest valid inputs
|
| 728 |
- empty input when allowed
|
| 729 |
+
- duplicates
|
| 730 |
- negative values
|
| 731 |
- boundary values
|
| 732 |
+
- sorted and reverse-sorted data
|
| 733 |
+
- impossible cases
|
| 734 |
+
- multiple optimal answers
|
| 735 |
+
- very large inputs
|
| 736 |
+
- adversarial structures
|
| 737 |
""",
|
| 738 |
"answer": """
|
| 739 |
+
Use normal, edge-case, adversarial, and large-input tests.
|
| 740 |
+
""",
|
| 741 |
},
|
| 742 |
|
| 743 |
{
|
| 744 |
"category": "complexity",
|
| 745 |
+
"subcategories": [
|
| 746 |
+
"nested_algorithms",
|
| 747 |
+
],
|
| 748 |
"problem": """
|
| 749 |
+
An algorithm performs binary search and runs Dijkstra for every
|
| 750 |
binary-search step. What happens to the complexity?
|
| 751 |
""",
|
| 752 |
"reasoning": """
|
| 753 |
+
Nested algorithm costs multiply.
|
| 754 |
|
| 755 |
If Dijkstra costs O(E log V) and binary search performs O(log C)
|
| 756 |
iterations, the total is:
|
| 757 |
|
| 758 |
O(E log V log C)
|
|
|
|
|
|
|
| 759 |
""",
|
| 760 |
"answer": """
|
| 761 |
+
Multiply the costs of nested operations.
|
| 762 |
|
| 763 |
Total:
|
| 764 |
O(E log V log C).
|
| 765 |
+
""",
|
| 766 |
},
|
| 767 |
|
| 768 |
{
|
| 769 |
+
"category": "intervals",
|
| 770 |
+
"subcategories": [
|
| 771 |
+
"sorting",
|
| 772 |
+
"merging",
|
| 773 |
+
],
|
| 774 |
"problem": """
|
| 775 |
Write a Python function that merges overlapping intervals.
|
| 776 |
""",
|
|
|
|
| 780 |
After sorting, the next interval either overlaps the current interval
|
| 781 |
or starts after it ends.
|
| 782 |
|
| 783 |
+
When they overlap, extend the current endpoint. Otherwise finalize
|
| 784 |
+
the current interval and start a new one.
|
| 785 |
""",
|
| 786 |
"answer": """
|
| 787 |
+
Sort by interval start, then scan.
|
| 788 |
|
| 789 |
Time: O(n log n).
|
| 790 |
Space: O(n) for the output.
|
| 791 |
+
""",
|
| 792 |
},
|
| 793 |
|
| 794 |
{
|
| 795 |
+
"category": "strings",
|
| 796 |
+
"subcategories": [
|
| 797 |
+
"two_pointers",
|
| 798 |
+
"normalization",
|
| 799 |
+
],
|
| 800 |
"problem": """
|
| 801 |
+
Determine whether a string is a palindrome after removing
|
| 802 |
non-alphanumeric characters and ignoring case.
|
| 803 |
""",
|
| 804 |
"reasoning": """
|
| 805 |
+
Compare characters from both ends while skipping non-alphanumeric
|
| 806 |
+
characters.
|
| 807 |
+
|
| 808 |
+
Normalize characters before comparison.
|
| 809 |
|
| 810 |
+
This avoids constructing a second normalized string.
|
|
|
|
| 811 |
""",
|
| 812 |
"answer": """
|
| 813 |
+
Use two pointers.
|
| 814 |
|
| 815 |
Time: O(n).
|
| 816 |
+
Auxiliary space: O(1).
|
| 817 |
+
""",
|
| 818 |
},
|
| 819 |
|
| 820 |
{
|
| 821 |
"category": "debugging",
|
| 822 |
+
"subcategories": [
|
| 823 |
+
"recursion",
|
| 824 |
+
"stack",
|
| 825 |
+
],
|
| 826 |
"problem": """
|
| 827 |
A recursive function works on small inputs but crashes on very large
|
| 828 |
inputs.
|
| 829 |
""",
|
| 830 |
"reasoning": """
|
| 831 |
+
The algorithm may be logically correct but exceed Python's recursion
|
| 832 |
+
depth.
|
| 833 |
+
|
| 834 |
+
Determine whether recursion depth grows with input size.
|
| 835 |
|
| 836 |
+
If so, an iterative formulation using an explicit stack may avoid the
|
| 837 |
+
language recursion limit.
|
| 838 |
""",
|
| 839 |
"answer": """
|
| 840 |
+
Check recursion depth and convert to iteration when appropriate.
|
| 841 |
+
""",
|
|
|
|
| 842 |
},
|
| 843 |
|
| 844 |
{
|
| 845 |
"category": "graphs",
|
| 846 |
+
"subcategories": [
|
| 847 |
+
"negative_weights",
|
| 848 |
+
"shortest_path",
|
| 849 |
+
],
|
| 850 |
"problem": """
|
| 851 |
Why can't ordinary Dijkstra handle negative edge weights?
|
| 852 |
""",
|
| 853 |
"reasoning": """
|
| 854 |
+
Dijkstra assumes that once the smallest tentative distance is
|
| 855 |
+
selected, no future path can improve it.
|
| 856 |
|
| 857 |
+
A negative edge can create a cheaper path later, violating that
|
| 858 |
+
greedy assumption.
|
| 859 |
""",
|
| 860 |
"answer": """
|
| 861 |
+
Negative weights invalidate Dijkstra's correctness condition.
|
| 862 |
+
|
| 863 |
+
Consider Bellman-Ford or another algorithm suitable for the input.
|
| 864 |
+
""",
|
| 865 |
},
|
| 866 |
|
| 867 |
{
|
| 868 |
"category": "math",
|
| 869 |
+
"subcategories": [
|
| 870 |
+
"bitwise",
|
| 871 |
+
"powers_of_two",
|
| 872 |
+
],
|
| 873 |
"problem": """
|
| 874 |
How can you determine whether an integer is a power of two?
|
| 875 |
""",
|
| 876 |
"reasoning": """
|
| 877 |
+
A positive power of two has exactly one bit set.
|
| 878 |
|
| 879 |
For positive n:
|
| 880 |
+
|
| 881 |
n & (n - 1) == 0
|
| 882 |
+
|
| 883 |
+
is true exactly when n has one set bit.
|
| 884 |
""",
|
| 885 |
"answer": """
|
| 886 |
Check:
|
| 887 |
|
| 888 |
n > 0 and (n & (n - 1)) == 0
|
| 889 |
+
""",
|
| 890 |
},
|
| 891 |
|
| 892 |
{
|
| 893 |
"category": "algorithm_selection",
|
| 894 |
+
"subcategories": [
|
| 895 |
+
"complexity",
|
| 896 |
+
"optimization",
|
| 897 |
+
],
|
| 898 |
"problem": """
|
| 899 |
+
You are given a problem where n can be 200,000. A proposed solution
|
| 900 |
uses O(n²). What should you investigate?
|
| 901 |
""",
|
| 902 |
"reasoning": """
|
| 903 |
+
Quadratic work is generally too large at this input scale.
|
| 904 |
|
| 905 |
+
Look for structural improvements such as hashing, sorting, two
|
| 906 |
+
pointers, prefix sums, binary search, heaps, graph algorithms, or
|
| 907 |
+
dynamic programming.
|
| 908 |
""",
|
| 909 |
"answer": """
|
| 910 |
+
Identify the O(n²) bottleneck and search for an O(n log n) or O(n)
|
| 911 |
formulation.
|
| 912 |
+
""",
|
| 913 |
},
|
| 914 |
|
| 915 |
{
|
| 916 |
"category": "problem_solving",
|
| 917 |
+
"subcategories": [
|
| 918 |
+
"debugging",
|
| 919 |
+
"edge_cases",
|
| 920 |
+
],
|
| 921 |
"problem": """
|
| 922 |
+
A solution works on examples but fails hidden tests. What should be
|
| 923 |
+
investigated?
|
| 924 |
""",
|
| 925 |
"reasoning": """
|
| 926 |
+
Check unstated assumptions.
|
| 927 |
|
| 928 |
+
Typical causes include:
|
| 929 |
+
incorrect boundaries,
|
| 930 |
empty inputs,
|
| 931 |
+
duplicates,
|
| 932 |
+
overflow,
|
| 933 |
incorrect state transitions,
|
| 934 |
wrong complexity,
|
| 935 |
+
or an algorithm that only works on the sample structure.
|
| 936 |
""",
|
| 937 |
"answer": """
|
| 938 |
Use adversarial and edge-case testing and verify the algorithm's
|
| 939 |
invariants.
|
| 940 |
+
""",
|
| 941 |
},
|
| 942 |
|
| 943 |
{
|
| 944 |
"category": "invariants",
|
| 945 |
+
"subcategories": [
|
| 946 |
+
"correctness",
|
| 947 |
+
"proof",
|
| 948 |
+
],
|
| 949 |
"problem": """
|
| 950 |
What is an invariant in an algorithm?
|
| 951 |
""",
|
|
|
|
| 953 |
An invariant is a property that remains true throughout the relevant
|
| 954 |
part of the algorithm.
|
| 955 |
|
| 956 |
+
It can be used both to prove correctness and to detect implementation
|
| 957 |
errors.
|
| 958 |
""",
|
| 959 |
"answer": """
|
| 960 |
+
State the property that must remain true after every relevant
|
| 961 |
+
iteration or state transition.
|
| 962 |
+
""",
|
| 963 |
},
|
| 964 |
|
| 965 |
{
|
| 966 |
+
"category": "dynamic_programming",
|
| 967 |
+
"subcategories": [
|
| 968 |
+
"state_design",
|
| 969 |
+
"transitions",
|
| 970 |
+
],
|
| 971 |
"problem": """
|
| 972 |
How do you design a dynamic programming solution to an unfamiliar
|
| 973 |
problem?
|
| 974 |
""",
|
| 975 |
"reasoning": """
|
| 976 |
+
Start by identifying the smallest information required to describe a
|
| 977 |
subproblem.
|
| 978 |
|
| 979 |
Then define:
|
| 980 |
1. state
|
| 981 |
2. transition
|
| 982 |
3. base cases
|
| 983 |
+
4. computation order
|
| 984 |
5. final state
|
| 985 |
|
| 986 |
+
Avoid putting unnecessary information into the state.
|
| 987 |
""",
|
| 988 |
"answer": """
|
| 989 |
+
Define the state and transition before implementing the DP.
|
| 990 |
+
""",
|
| 991 |
},
|
| 992 |
|
| 993 |
{
|
| 994 |
+
"category": "optimization",
|
| 995 |
+
"subcategories": [
|
| 996 |
+
"profiling",
|
| 997 |
+
"complexity",
|
| 998 |
+
],
|
| 999 |
"problem": """
|
| 1000 |
A program produces the right answer but is too slow. What should
|
| 1001 |
you do?
|
|
|
|
| 1006 |
First identify the dominant operation using complexity analysis or
|
| 1007 |
profiling.
|
| 1008 |
|
| 1009 |
+
Then improve the actual bottleneck instead of randomly optimizing
|
| 1010 |
+
unrelated code.
|
| 1011 |
""",
|
| 1012 |
"answer": """
|
| 1013 |
Find the dominant bottleneck, determine its complexity, and improve
|
| 1014 |
+
the responsible algorithm or data structure.
|
| 1015 |
+
""",
|
| 1016 |
},
|
| 1017 |
|
| 1018 |
{
|
| 1019 |
+
"category": "arrays",
|
| 1020 |
+
"subcategories": [
|
| 1021 |
+
"prefix_sum",
|
| 1022 |
+
"hashmap",
|
| 1023 |
+
],
|
| 1024 |
"problem": """
|
| 1025 |
Why should a dictionary store the earliest index of a prefix sum when
|
| 1026 |
finding the longest subarray with a target sum?
|
| 1027 |
""",
|
| 1028 |
"reasoning": """
|
| 1029 |
+
For a fixed ending index, a smaller earlier index creates a longer
|
| 1030 |
subarray.
|
| 1031 |
|
| 1032 |
+
Replacing an existing prefix-sum index with a later index can only
|
| 1033 |
+
make future answers shorter.
|
| 1034 |
""",
|
| 1035 |
"answer": """
|
| 1036 |
+
Keep the earliest occurrence of every prefix sum.
|
| 1037 |
+
""",
|
| 1038 |
},
|
| 1039 |
|
| 1040 |
{
|
| 1041 |
"category": "logic",
|
| 1042 |
+
"subcategories": [
|
| 1043 |
+
"implication",
|
| 1044 |
+
"affirming_consequent",
|
| 1045 |
+
],
|
| 1046 |
"problem": """
|
| 1047 |
A statement says: If A is true, then B is true. Does B being true
|
| 1048 |
prove A is true?
|
|
|
|
| 1050 |
"reasoning": """
|
| 1051 |
No.
|
| 1052 |
|
| 1053 |
+
The implication establishes A -> B, not B -> A.
|
| 1054 |
|
| 1055 |
+
B may be true for another reason.
|
|
|
|
| 1056 |
""",
|
| 1057 |
"answer": """
|
| 1058 |
No. A -> B does not imply B -> A.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1059 |
""",
|
|
|
|
|
|
|
|
|
|
| 1060 |
},
|
| 1061 |
|
| 1062 |
{
|
| 1063 |
"category": "systems",
|
| 1064 |
+
"subcategories": [
|
| 1065 |
+
"gpu_inference",
|
| 1066 |
+
"concurrency",
|
| 1067 |
+
"batching",
|
| 1068 |
+
],
|
| 1069 |
"problem": """
|
| 1070 |
Twenty users may request inference from the same GPU-hosted language
|
| 1071 |
model simultaneously. How can the server avoid exhausting GPU
|
| 1072 |
memory?
|
| 1073 |
""",
|
| 1074 |
"reasoning": """
|
| 1075 |
+
Model weights can be shared, but generation memory grows with active
|
| 1076 |
+
requests and sequence lengths.
|
| 1077 |
|
| 1078 |
+
Unlimited concurrency can therefore exhaust memory.
|
|
|
|
| 1079 |
|
| 1080 |
+
Use request queuing, bounded concurrency, and controlled batching.
|
|
|
|
| 1081 |
""",
|
| 1082 |
"answer": """
|
| 1083 |
Use bounded concurrency, request queuing, and controlled batching.
|
| 1084 |
+
""",
|
| 1085 |
},
|
| 1086 |
|
| 1087 |
{
|
| 1088 |
+
"category": "correctness",
|
| 1089 |
+
"subcategories": [
|
| 1090 |
+
"assumptions",
|
| 1091 |
+
"proof",
|
| 1092 |
+
],
|
| 1093 |
"problem": """
|
| 1094 |
An algorithm's proof assumes all edge weights are positive, but the
|
| 1095 |
implementation accepts negative weights. Is the proof still valid?
|
|
|
|
| 1097 |
"reasoning": """
|
| 1098 |
No.
|
| 1099 |
|
| 1100 |
+
The implementation accepts inputs outside the assumptions used by
|
| 1101 |
+
the proof.
|
| 1102 |
|
| 1103 |
+
Correctness guarantees only apply to inputs satisfying those
|
| 1104 |
+
assumptions.
|
| 1105 |
""",
|
| 1106 |
"answer": """
|
| 1107 |
+
Either enforce the positive-weight constraint or use an algorithm
|
| 1108 |
whose correctness supports negative weights.
|
| 1109 |
+
""",
|
| 1110 |
+
},
|
|
|
|
| 1111 |
]
|
| 1112 |
|
| 1113 |
|
| 1114 |
# ============================================================
|
| 1115 |
+
# CATEGORY ROUTING
|
| 1116 |
# ============================================================
|
| 1117 |
|
| 1118 |
+
CATEGORY_HINTS = {
|
| 1119 |
+
"graphs": [
|
| 1120 |
+
"graph",
|
| 1121 |
+
"graphs",
|
| 1122 |
+
"vertex",
|
| 1123 |
+
"vertices",
|
| 1124 |
+
"edge",
|
| 1125 |
+
"edges",
|
| 1126 |
+
"shortest path",
|
| 1127 |
+
"dijkstra",
|
| 1128 |
+
"bfs",
|
| 1129 |
+
"dfs",
|
| 1130 |
+
"topological",
|
| 1131 |
+
"cycle",
|
| 1132 |
+
"connected",
|
| 1133 |
+
],
|
| 1134 |
+
|
| 1135 |
+
"dynamic_programming": [
|
| 1136 |
+
"dynamic programming",
|
| 1137 |
+
"dp",
|
| 1138 |
+
"subproblem",
|
| 1139 |
+
"transition",
|
| 1140 |
+
"memoization",
|
| 1141 |
+
"tabulation",
|
| 1142 |
+
],
|
| 1143 |
+
|
| 1144 |
+
"binary_search": [
|
| 1145 |
+
"binary search",
|
| 1146 |
+
"monotonic",
|
| 1147 |
+
"first true",
|
| 1148 |
+
"last true",
|
| 1149 |
+
"search on answer",
|
| 1150 |
+
],
|
| 1151 |
+
|
| 1152 |
+
"sliding_window": [
|
| 1153 |
+
"sliding window",
|
| 1154 |
+
"substring",
|
| 1155 |
+
"subarray",
|
| 1156 |
+
"window",
|
| 1157 |
+
],
|
| 1158 |
+
|
| 1159 |
+
"trees": [
|
| 1160 |
+
"binary tree",
|
| 1161 |
+
"tree",
|
| 1162 |
+
"bst",
|
| 1163 |
+
"subtree",
|
| 1164 |
+
"node",
|
| 1165 |
+
"leaf",
|
| 1166 |
+
],
|
| 1167 |
+
|
| 1168 |
+
"debugging": [
|
| 1169 |
+
"bug",
|
| 1170 |
+
"debug",
|
| 1171 |
+
"error",
|
| 1172 |
+
"crash",
|
| 1173 |
+
"wrong answer",
|
| 1174 |
+
"fails",
|
| 1175 |
+
"broken",
|
| 1176 |
+
],
|
| 1177 |
+
|
| 1178 |
+
"complexity": [
|
| 1179 |
+
"complexity",
|
| 1180 |
+
"big o",
|
| 1181 |
+
"time complexity",
|
| 1182 |
+
"space complexity",
|
| 1183 |
+
"too slow",
|
| 1184 |
+
"performance",
|
| 1185 |
+
],
|
| 1186 |
+
|
| 1187 |
+
"arrays": [
|
| 1188 |
+
"array",
|
| 1189 |
+
"arrays",
|
| 1190 |
+
"subarray",
|
| 1191 |
+
"prefix sum",
|
| 1192 |
+
"two pointers",
|
| 1193 |
+
],
|
| 1194 |
+
|
| 1195 |
+
"strings": [
|
| 1196 |
+
"string",
|
| 1197 |
+
"substring",
|
| 1198 |
+
"character",
|
| 1199 |
+
"palindrome",
|
| 1200 |
+
],
|
| 1201 |
+
|
| 1202 |
+
"systems": [
|
| 1203 |
+
"gpu",
|
| 1204 |
+
"cuda",
|
| 1205 |
+
"server",
|
| 1206 |
+
"concurrency",
|
| 1207 |
+
"batching",
|
| 1208 |
+
"memory",
|
| 1209 |
+
"inference",
|
| 1210 |
+
"api",
|
| 1211 |
+
],
|
| 1212 |
+
}
|
| 1213 |
+
|
| 1214 |
+
|
| 1215 |
+
def detect_categories(query: str) -> set:
|
| 1216 |
+
q = query.lower()
|
| 1217 |
+
|
| 1218 |
+
categories = set()
|
| 1219 |
+
|
| 1220 |
+
for category, keywords in CATEGORY_HINTS.items():
|
| 1221 |
+
for keyword in keywords:
|
| 1222 |
+
|
| 1223 |
+
if " " in keyword:
|
| 1224 |
+
if keyword in q:
|
| 1225 |
+
categories.add(category)
|
| 1226 |
+
break
|
| 1227 |
+
else:
|
| 1228 |
+
if re.search(
|
| 1229 |
+
rf"\b{re.escape(keyword)}\b",
|
| 1230 |
+
q,
|
| 1231 |
+
):
|
| 1232 |
+
categories.add(category)
|
| 1233 |
+
break
|
| 1234 |
+
|
| 1235 |
+
return categories
|
| 1236 |
+
|
| 1237 |
+
|
| 1238 |
+
# ============================================================
|
| 1239 |
+
# EMBEDDINGS
|
| 1240 |
+
# ============================================================
|
| 1241 |
+
|
| 1242 |
+
def example_to_embedding_text(example: dict) -> str:
|
| 1243 |
+
|
| 1244 |
+
return (
|
| 1245 |
+
f"CATEGORY: {example['category']}\n"
|
| 1246 |
+
f"SUBCATEGORIES: "
|
| 1247 |
+
f"{', '.join(example.get('subcategories', []))}\n"
|
| 1248 |
+
f"PROBLEM: {example['problem']}\n"
|
| 1249 |
+
f"REASONING: {example['reasoning']}\n"
|
| 1250 |
+
f"ANSWER: {example['answer']}"
|
| 1251 |
+
)
|
| 1252 |
|
| 1253 |
|
| 1254 |
def build_reasoning_embeddings():
|
| 1255 |
+
|
|
|
|
|
|
|
|
|
|
| 1256 |
global reasoning_embeddings
|
| 1257 |
|
| 1258 |
if not REASONING_EXAMPLES:
|
|
|
|
| 1264 |
if embed_model is None:
|
| 1265 |
return
|
| 1266 |
|
| 1267 |
+
texts = [
|
| 1268 |
+
example_to_embedding_text(example)
|
| 1269 |
+
for example in REASONING_EXAMPLES
|
| 1270 |
+
]
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1271 |
|
| 1272 |
reasoning_embeddings = embed_model.encode(
|
| 1273 |
texts,
|
| 1274 |
normalize_embeddings=True,
|
| 1275 |
+
convert_to_numpy=True,
|
| 1276 |
+
show_progress_bar=False,
|
| 1277 |
)
|
| 1278 |
|
| 1279 |
logger.info(
|
| 1280 |
+
"Built embeddings for %d reasoning examples.",
|
| 1281 |
+
len(REASONING_EXAMPLES),
|
| 1282 |
)
|
| 1283 |
|
| 1284 |
|
| 1285 |
def retrieve_reasoning_examples(
|
| 1286 |
query: str,
|
| 1287 |
+
top_k: int = 4,
|
| 1288 |
) -> list:
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1289 |
|
| 1290 |
global reasoning_embeddings
|
| 1291 |
|
|
|
|
| 1298 |
return REASONING_EXAMPLES[:top_k]
|
| 1299 |
|
| 1300 |
try:
|
| 1301 |
+
|
| 1302 |
+
if reasoning_embeddings is None:
|
| 1303 |
+
build_reasoning_embeddings()
|
| 1304 |
+
|
| 1305 |
query_embedding = embed_model.encode(
|
| 1306 |
[query],
|
| 1307 |
normalize_embeddings=True,
|
| 1308 |
+
convert_to_numpy=True,
|
| 1309 |
+
show_progress_bar=False,
|
| 1310 |
)[0]
|
| 1311 |
|
|
|
|
|
|
|
|
|
|
| 1312 |
scores = np.dot(
|
| 1313 |
reasoning_embeddings,
|
| 1314 |
+
query_embedding,
|
| 1315 |
)
|
| 1316 |
|
| 1317 |
+
ranked_indices = np.argsort(scores)[::-1]
|
| 1318 |
+
|
| 1319 |
+
query_categories = detect_categories(query)
|
| 1320 |
|
| 1321 |
selected = []
|
| 1322 |
+
selected_indices = set()
|
| 1323 |
+
|
| 1324 |
+
# ----------------------------------------------------
|
| 1325 |
+
# First pass: category-compatible examples
|
| 1326 |
+
# ----------------------------------------------------
|
| 1327 |
+
|
| 1328 |
+
for index in ranked_indices:
|
| 1329 |
+
|
| 1330 |
+
index = int(index)
|
| 1331 |
+
|
| 1332 |
+
example = REASONING_EXAMPLES[index]
|
| 1333 |
+
|
| 1334 |
+
score = float(scores[index])
|
| 1335 |
+
|
| 1336 |
+
if score < RAG_MIN_SIMILARITY:
|
| 1337 |
+
continue
|
| 1338 |
|
| 1339 |
+
if (
|
| 1340 |
+
query_categories
|
| 1341 |
+
and example["category"] not in query_categories
|
| 1342 |
+
):
|
| 1343 |
+
continue
|
| 1344 |
+
|
| 1345 |
+
selected.append(
|
| 1346 |
+
{
|
| 1347 |
+
**example,
|
| 1348 |
+
"_similarity": score,
|
| 1349 |
+
}
|
| 1350 |
+
)
|
| 1351 |
+
|
| 1352 |
+
selected_indices.add(index)
|
| 1353 |
+
|
| 1354 |
+
if len(selected) >= top_k:
|
| 1355 |
+
break
|
| 1356 |
+
|
| 1357 |
+
# ----------------------------------------------------
|
| 1358 |
+
# Second pass: semantic fallback
|
| 1359 |
+
#
|
| 1360 |
+
# This is crucial.
|
| 1361 |
+
#
|
| 1362 |
+
# If no category-specific example exists, semantic
|
| 1363 |
+
# similarity can still find a structurally useful
|
| 1364 |
+
# example.
|
| 1365 |
+
# ----------------------------------------------------
|
| 1366 |
+
|
| 1367 |
+
if len(selected) < top_k:
|
| 1368 |
+
|
| 1369 |
+
for index in ranked_indices:
|
| 1370 |
+
|
| 1371 |
+
index = int(index)
|
| 1372 |
+
|
| 1373 |
+
if index in selected_indices:
|
| 1374 |
+
continue
|
| 1375 |
+
|
| 1376 |
+
score = float(scores[index])
|
| 1377 |
+
|
| 1378 |
+
if score < RAG_MIN_SIMILARITY:
|
| 1379 |
+
continue
|
| 1380 |
+
|
| 1381 |
+
example = REASONING_EXAMPLES[index]
|
| 1382 |
+
|
| 1383 |
+
selected.append(
|
| 1384 |
+
{
|
| 1385 |
+
**example,
|
| 1386 |
+
"_similarity": score,
|
| 1387 |
+
}
|
| 1388 |
+
)
|
| 1389 |
+
|
| 1390 |
+
selected_indices.add(index)
|
| 1391 |
+
|
| 1392 |
+
if len(selected) >= top_k:
|
| 1393 |
+
break
|
| 1394 |
|
| 1395 |
return selected
|
| 1396 |
|
| 1397 |
except Exception as e:
|
| 1398 |
+
|
| 1399 |
logger.warning(
|
| 1400 |
+
"Reasoning retrieval failed: %s",
|
| 1401 |
+
e,
|
| 1402 |
)
|
| 1403 |
|
| 1404 |
+
return []
|
| 1405 |
|
| 1406 |
|
| 1407 |
def format_reasoning_examples(
|
| 1408 |
query: str,
|
| 1409 |
+
top_k: int = 4,
|
| 1410 |
) -> str:
|
|
|
|
|
|
|
|
|
|
| 1411 |
|
| 1412 |
examples = retrieve_reasoning_examples(
|
| 1413 |
query,
|
| 1414 |
+
top_k=top_k,
|
| 1415 |
)
|
| 1416 |
|
| 1417 |
if not examples:
|
|
|
|
| 1425 |
f"""
|
| 1426 |
EXAMPLE {i}
|
| 1427 |
CATEGORY: {example["category"]}
|
| 1428 |
+
SUBCATEGORIES: {", ".join(example.get("subcategories", []))}
|
| 1429 |
+
RELEVANCE: {example["_similarity"]:.3f}
|
| 1430 |
|
| 1431 |
PROBLEM:
|
| 1432 |
{example["problem"].strip()}
|
| 1433 |
|
| 1434 |
+
APPROACH SUMMARY:
|
| 1435 |
{example["reasoning"].strip()}
|
| 1436 |
|
| 1437 |
+
ANSWER:
|
| 1438 |
{example["answer"].strip()}
|
| 1439 |
""".strip()
|
| 1440 |
)
|
|
|
|
| 1443 |
|
| 1444 |
|
| 1445 |
# ============================================================
|
| 1446 |
+
# TEXT UTILITIES
|
| 1447 |
# ============================================================
|
| 1448 |
|
| 1449 |
+
def normalize_text(text: str) -> str:
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1450 |
|
| 1451 |
+
text = re.sub(
|
| 1452 |
+
r"\s+",
|
| 1453 |
+
" ",
|
| 1454 |
+
text or "",
|
| 1455 |
)
|
| 1456 |
|
| 1457 |
+
return text.strip()
|
| 1458 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1459 |
|
| 1460 |
+
def chunk_text(
|
| 1461 |
+
text: str,
|
| 1462 |
+
chunk_size: int = WEB_CHUNK_WORDS,
|
| 1463 |
+
overlap: int = WEB_CHUNK_OVERLAP,
|
| 1464 |
+
) -> list:
|
| 1465 |
|
| 1466 |
+
words = normalize_text(text).split()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1467 |
|
| 1468 |
+
if not words:
|
| 1469 |
+
return []
|
|
|
|
| 1470 |
|
| 1471 |
+
chunks = []
|
|
|
|
|
|
|
|
|
|
| 1472 |
|
| 1473 |
+
start = 0
|
| 1474 |
|
| 1475 |
+
while start < len(words):
|
|
|
|
|
|
|
|
|
|
| 1476 |
|
| 1477 |
+
end = min(
|
| 1478 |
+
start + chunk_size,
|
| 1479 |
+
len(words),
|
|
|
|
| 1480 |
)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1481 |
|
| 1482 |
+
chunk = " ".join(
|
| 1483 |
+
words[start:end]
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1484 |
)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1485 |
|
| 1486 |
+
if chunk:
|
| 1487 |
+
chunks.append(chunk)
|
| 1488 |
|
| 1489 |
+
if end >= len(words):
|
| 1490 |
+
break
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1491 |
|
| 1492 |
+
start = max(
|
| 1493 |
+
start + 1,
|
| 1494 |
+
end - overlap,
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1495 |
)
|
| 1496 |
|
| 1497 |
+
return chunks
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1498 |
|
|
|
|
|
|
|
|
|
|
| 1499 |
|
| 1500 |
+
def trim_text_by_tokens(
|
| 1501 |
+
text: str,
|
| 1502 |
+
max_tokens: int,
|
| 1503 |
) -> str:
|
| 1504 |
|
| 1505 |
+
tokenizer = engine_state.get("tokenizer")
|
|
|
|
| 1506 |
|
| 1507 |
+
if tokenizer is None:
|
| 1508 |
+
return text
|
|
|
|
|
|
|
|
|
|
| 1509 |
|
| 1510 |
+
tokens = tokenizer.encode(
|
| 1511 |
+
text,
|
| 1512 |
+
add_special_tokens=False,
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1513 |
)
|
| 1514 |
|
| 1515 |
+
if len(tokens) <= max_tokens:
|
| 1516 |
+
return text
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1517 |
|
| 1518 |
+
return tokenizer.decode(
|
| 1519 |
+
tokens[:max_tokens],
|
| 1520 |
+
skip_special_tokens=True,
|
| 1521 |
)
|
| 1522 |
|
| 1523 |
|
| 1524 |
# ============================================================
|
| 1525 |
+
# WEB SEARCH
|
| 1526 |
# ============================================================
|
| 1527 |
|
| 1528 |
def isolated_blocking_ddg(
|
| 1529 |
+
query_string: str,
|
| 1530 |
) -> list:
|
| 1531 |
|
| 1532 |
try:
|
|
|
|
| 1535 |
|
| 1536 |
results = ddgs.text(
|
| 1537 |
query_string,
|
| 1538 |
+
max_results=MAX_WEB_RESULTS,
|
| 1539 |
)
|
| 1540 |
|
| 1541 |
if results:
|
| 1542 |
|
| 1543 |
+
urls = []
|
| 1544 |
+
|
| 1545 |
+
for result in results:
|
| 1546 |
+
|
| 1547 |
+
href = result.get("href")
|
| 1548 |
+
|
| 1549 |
+
if href:
|
| 1550 |
+
urls.append(href)
|
| 1551 |
+
|
| 1552 |
+
return list(
|
| 1553 |
+
dict.fromkeys(urls)
|
| 1554 |
+
)
|
| 1555 |
|
| 1556 |
except Exception as e:
|
| 1557 |
|
| 1558 |
logger.error(
|
| 1559 |
+
"DuckDuckGo search failed: %s",
|
| 1560 |
+
e,
|
| 1561 |
)
|
| 1562 |
|
| 1563 |
return []
|
| 1564 |
|
| 1565 |
|
| 1566 |
async def search_web_async(
|
| 1567 |
+
query: str,
|
| 1568 |
) -> list:
|
| 1569 |
|
| 1570 |
urls = await asyncio.to_thread(
|
| 1571 |
isolated_blocking_ddg,
|
| 1572 |
+
query,
|
| 1573 |
)
|
| 1574 |
|
| 1575 |
+
return urls[:MAX_WEB_RESULTS]
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1576 |
|
| 1577 |
|
| 1578 |
# ============================================================
|
|
|
|
| 1581 |
|
| 1582 |
async def scrape_target_url(
|
| 1583 |
client,
|
| 1584 |
+
url: str,
|
| 1585 |
):
|
| 1586 |
|
| 1587 |
try:
|
| 1588 |
|
| 1589 |
+
response = await client.get(
|
| 1590 |
url,
|
| 1591 |
+
timeout=12.0,
|
| 1592 |
)
|
| 1593 |
|
| 1594 |
+
if response.status_code != 200:
|
| 1595 |
|
| 1596 |
logger.warning(
|
| 1597 |
+
"HTTP %s: %s",
|
| 1598 |
+
response.status_code,
|
| 1599 |
+
url,
|
| 1600 |
)
|
| 1601 |
|
| 1602 |
return None
|
| 1603 |
|
| 1604 |
+
html = response.text
|
| 1605 |
+
|
| 1606 |
text = None
|
| 1607 |
|
| 1608 |
try:
|
| 1609 |
|
| 1610 |
text = extract(
|
| 1611 |
+
html,
|
| 1612 |
+
include_comments=False,
|
| 1613 |
+
include_tables=True,
|
| 1614 |
+
favor_precision=True,
|
| 1615 |
)
|
| 1616 |
|
| 1617 |
except Exception as e:
|
| 1618 |
|
| 1619 |
logger.warning(
|
| 1620 |
+
"Trafilatura failed for %s: %s",
|
| 1621 |
+
url,
|
| 1622 |
+
e,
|
| 1623 |
)
|
| 1624 |
|
| 1625 |
if not text:
|
|
|
|
| 1627 |
try:
|
| 1628 |
|
| 1629 |
soup = BeautifulSoup(
|
| 1630 |
+
html,
|
| 1631 |
+
"html.parser",
|
| 1632 |
)
|
| 1633 |
|
| 1634 |
+
for tag in soup(
|
| 1635 |
+
[
|
| 1636 |
+
"script",
|
| 1637 |
+
"style",
|
| 1638 |
+
"noscript",
|
| 1639 |
+
"nav",
|
| 1640 |
+
"footer",
|
| 1641 |
+
"header",
|
| 1642 |
+
]
|
| 1643 |
+
):
|
| 1644 |
+
tag.decompose()
|
| 1645 |
+
|
| 1646 |
text = soup.get_text(
|
| 1647 |
separator=" ",
|
| 1648 |
+
strip=True,
|
| 1649 |
)
|
| 1650 |
|
| 1651 |
except Exception as e:
|
| 1652 |
|
| 1653 |
+
logger.warning(
|
| 1654 |
+
"BeautifulSoup failed for %s: %s",
|
| 1655 |
+
url,
|
| 1656 |
+
e,
|
| 1657 |
+
)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|