HTML 77.2%
TypeScript 10.5%
Python 9.6%
JavaScript 2.5%
1"""Search: Postgres FTS + trigram (+ pgvector when embeddings exist) and a deterministic natural-language → filter compiler (v2).23 "open models over 100B released this year" → type model · openness open · params ≥ 1e11 · year_from = year_to = <this year>4 "cheapest models with 1M context" → type model · context ≥ 1 000 000 · sort cheapest5 "reasoning models under $1/M tokens" → type model · reasoning · max_output_price 1.06 "models that fit in 64GB" → type model · memory_gb 64 (→ ESTIMATED params bound at 4-bit)7 "Anthropic models released since 2025" → type model · organization Anthropic (verified by the API against org aliases) · year_from 20258 "open vision models with Apache license" → type model · openness open · modality image · license Apache-2.09 "papers introducing MoE models" → type paper · residual "introducing MoE"1011Every recognised fragment is listed in `compiled` (filter, label, value, source_span) so the UI can show "Compiled as …"; the rest of the12text is `residual` (used for full-text search) and its words are echoed in `unrecognised`."""13from __future__ import annotations1415import re16from dataclasses import dataclass, field17from datetime import UTC, datetime18from typing import Any1920from sqlalchemy.ext.asyncio import AsyncConnection2122from aiatlas.db import fetch_all23from aiatlas.ontology.licenses import normalize_license24from aiatlas.sdk.extract.numbers import parse_context_length, parse_param_count25from aiatlas.services import hardware_fit as hf2627TYPE_WORDS = {28 "model": ("model", "models", "llm", "llms", "language model", "language models"),29 "company": ("company", "companies", "startup", "startups", "lab", "labs", "organization", "organizations", "organisation"),30 "paper": ("paper", "papers", "research", "publication", "publications", "preprint", "preprints", "arxiv"),31 "provider": ("provider", "providers", "inference provider", "api provider", "inference providers"),32 "benchmark": ("benchmark", "benchmarks", "leaderboard", "leaderboards", "eval", "evals"),33 "hardware": ("gpu", "gpus", "chip", "chips", "hardware", "accelerator", "accelerators", "npu", "tpu"),34 "framework": ("framework", "frameworks", "library", "libraries", "runtime", "runtimes"),35 "dataset": ("dataset", "datasets"),36 "tool": ("tool", "tools", "agent", "agents", "app", "application"),37 "repository": ("repo", "repos", "repository", "repositories"),38}39MODALITY_WORDS = {"vision": "image", "multimodal": "image", "image": "image", "images": "image", "audio": "audio", "speech": "audio", "voice": "audio",40 "video": "video", "embedding": "embedding", "embeddings": "embedding"}41LICENSE_WORDS = {"apache": "Apache-2.0", "apache 2.0": "Apache-2.0", "apache-2.0": "Apache-2.0", "mit": "MIT", "mit license": "MIT", "bsd": "BSD-3-Clause",42 "cc-by": "CC-BY-4.0", "cc by": "CC-BY-4.0", "gpl": "GPL-3.0", "llama license": "Llama-3.1-Community", "gemma license": "Gemma-Terms"}43SORT_WORDS = {"cheapest": "cheapest", "cheap": "cheapest", "lowest price": "cheapest", "newest": "newest", "latest": "newest", "most recent": "newest",44 "recent": "newest", "largest": "largest", "biggest": "largest", "smallest": "smallest", "best": "best"}45STOP = {"the", "a", "an", "with", "and", "or", "of", "for", "that", "which", "in", "on", "to", "by", "from", "at", "my", "me", "i", "are", "is", "show", "find", "list", "all", "any"}46_SIZE = r"(\d+(?:\.\d+)?)\s*([bBmMtT])\b(?:\s*param(?:eter)?s?)?"474849@dataclass50class Query:51 text: str = ""52 entity_type: str | None = None53 openness: str | None = None54 year_from: int | None = None55 year_to: int | None = None56 params_min: int | None = None57 params_max: int | None = None58 context_min: int | None = None59 modalities: list[str] = field(default_factory=list)60 organization: str | None = None61 filters: dict[str, Any] = field(default_factory=dict)62 # v263 reasoning: bool | None = None64 max_input_price: float | None = None65 max_output_price: float | None = None66 memory_gb: float | None = None67 license_key: str | None = None68 commercial_use: bool | None = None69 days_back: int | None = None70 benchmark: str | None = None71 provider: str | None = None72 sort: str | None = None73 compiled: list[dict[str, Any]] = field(default_factory=list)74 residual: str = ""75 unrecognised: list[str] = field(default_factory=list)7677 def as_dict(self) -> dict[str, Any]:78 out = {k: v for k, v in self.__dict__.items() if v not in (None, "", [], {}) and k not in ("compiled", "residual", "unrecognised")}79 out["compiled"] = self.compiled80 out["residual"] = self.residual81 out["unrecognised"] = self.unrecognised82 out["filters"] = {**self.filters, "residual": self.residual}83 return out8485 @property86 def has_structure(self) -> bool:87 return any([self.entity_type, self.openness, self.year_from, self.year_to, self.params_min, self.params_max, self.context_min, self.modalities,88 self.organization, self.reasoning, self.max_input_price, self.max_output_price, self.memory_gb, self.license_key, self.commercial_use,89 self.days_back, self.benchmark, self.provider])909192class _Compiler:93 def __init__(self, text: str):94 self.text = text95 self.low = text.lower()96 self.consumed: list[tuple[int, int]] = []97 self.q = Query(text=text)9899 def take(self, m: re.Match[str], filter_: str, label: str, value: Any) -> None:100 self.consumed.append((m.start(), m.end()))101 self.q.compiled.append({"filter": filter_, "label": label, "value": value, "source_span": self.text[m.start():m.end()]})102103 def search(self, pattern: str) -> re.Match[str] | None:104 for m in re.finditer(pattern, self.low, flags=re.IGNORECASE):105 if not any(s <= m.start() < e or s < m.end() <= e for s, e in self.consumed):106 return m107 return None108109 def residual(self) -> str:110 chars = list(self.text)111 for s, e in self.consumed:112 for i in range(s, e):113 chars[i] = " "114 return " ".join("".join(chars).split())115116117def compile_query(q: str) -> Query:118 c = _Compiler(q.strip())119 out = c.q120 year_now = datetime.now(UTC).year121122 # --- explicit "benchmark <name>" / "provider <name>"123 m = c.search(r"\bbenchmark\s+([a-z0-9][\w.\- ]{1,40}?)(?=\s+(?:leaderboard|results|scores|models)\b|$)")124 if m:125 out.benchmark = m.group(1).strip()126 out.entity_type = "benchmark"127 c.take(m, "benchmark", "Benchmark", out.benchmark)128 m = c.search(r"\bprovider\s+([a-z0-9][\w.\- ]{1,40}?)(?=\s+(?:prices?|models)\b|$)")129 if m:130 out.provider = m.group(1).strip()131 out.entity_type = "provider"132 c.take(m, "provider", "Provider", out.provider)133134 # --- price bounds135 m = c.search(r"(?:under|below|cheaper than|less than|at most|<|≤)\s*\$?\s*(\d+(?:\.\d+)?)\s*(?:usd)?\s*(?:/|per)\s*(?:1\s*)?m(?:illion)?(?:\s*(?:tok(?:ens?)?|output(?:\s*tokens?)?|input(?:\s*tokens?)?))?")136 if m:137 which = "input" if "input" in m.group(0) else "output"138 val = float(m.group(1))139 if which == "input":140 out.max_input_price = val141 else:142 out.max_output_price = val143 c.take(m, f"max_{which}_price", f"Cheapest {which} price ≤ ${val:g} / 1M tokens", val)144 m = c.search(r"\$\s*(\d+(?:\.\d+)?)\s*(?:/|per)\s*(?:1\s*)?m(?:illion)?\b(?:\s*tok(?:ens?)?)?")145 if m and out.max_output_price is None and out.max_input_price is None:146 out.max_output_price = float(m.group(1))147 c.take(m, "max_output_price", f"Cheapest output price ≤ ${out.max_output_price:g} / 1M tokens", out.max_output_price)148149 # --- memory / hardware fit150 m = c.search(r"(?:fits?\s+(?:in|on)|runs?\s+on|run\s+locally\s+on|on\s+my)\s*(?:a\s+|an\s+)?(?:mac(?:book)?(?:\s+\w+)?\s+with\s+)?(\d+(?:\.\d+)?)\s*gb\b(?:\s*(?:of\s+)?(?:ram|memory|vram|unified memory))?")151 if not m:152 m = c.search(r"\b(\d+(?:\.\d+)?)\s*gb\s*(?:of\s+)?(?:ram|memory|vram)\b")153 if m:154 out.memory_gb = float(m.group(1))155 c.take(m, "memory_gb", f"Fits in {out.memory_gb:g} GB (estimated at 4-bit, 8K context)", out.memory_gb)156 elif c.search(r"\bon my mac\b"):157 m2 = c.search(r"\bon my mac\b")158 assert m2 is not None159 out.openness = "open"160 c.take(m2, "openness", "Runs locally → open weights", "open")161162 # --- context window163 m = c.search(r"(?:context(?:\s+window)?\s*(?:of\s+)?(?:>=|≥|>|over|above|at least|min(?:imum)?)?\s*(\d+(?:\.\d+)?\s*[kKmM])\b(?:\s*tokens?)?)")164 if not m:165 m = c.search(r"(?:with\s+)?(\d+(?:\.\d+)?\s*[kKmM])\b\s*(?:\+\s*)?(?:tokens?\s+)?(?:of\s+)?context(?:\s+window)?")166 if not m:167 m = c.search(r"\blong[\s-]context\b")168 if m:169 out.context_min = 128_000170 c.take(m, "context_min", "Context ≥ 128K tokens", 128_000)171 m = None172 if m:173 out.context_min = parse_context_length(m.group(1))174 c.take(m, "context_min", f"Context ≥ {out.context_min:,} tokens", out.context_min)175176 # --- parameter counts177 m = c.search(r"\bbetween\s+" + _SIZE + r"\s+and\s+" + _SIZE)178 if m:179 out.params_min = parse_param_count(m.group(1) + m.group(2) + " params")180 out.params_max = parse_param_count(m.group(3) + m.group(4) + " params")181 c.take(m, "params_range", f"Parameters between {m.group(1)}{m.group(2).upper()} and {m.group(3)}{m.group(4).upper()}", [out.params_min, out.params_max])182 m = c.search(r"(?:more than|over|above|at least|>=|≥|>|larger than|bigger than)\s*" + _SIZE)183 if m:184 out.params_min = parse_param_count(m.group(1) + m.group(2) + " params")185 c.take(m, "params_min", f"Parameters ≥ {m.group(1)}{m.group(2).upper()}", out.params_min)186 m = c.search(r"(?:less than|under|below|at most|<=|≤|<|smaller than)\s*" + _SIZE)187 if m:188 out.params_max = parse_param_count(m.group(1) + m.group(2) + " params")189 c.take(m, "params_max", f"Parameters ≤ {m.group(1)}{m.group(2).upper()}", out.params_max)190 m = c.search(r"\b" + _SIZE + r"\s*(?:models?|parameters?)\b")191 if m and out.params_min is None and out.params_max is None:192 n = parse_param_count(m.group(1) + m.group(2) + " params")193 if n:194 out.params_min, out.params_max = int(n * 0.85), int(n * 1.15)195 c.take(m, "params_about", f"About {m.group(1)}{m.group(2).upper()} parameters (±15%)", n)196197 # --- dates198 m = c.search(r"\b(?:(?:released|launched|published|announced)\s+)?(?:in\s+the\s+)?(?:last|past)\s+(\d+)\s+(day|week|month|year)s?\b")199 if m:200 n, unit = int(m.group(1)), m.group(2)201 out.days_back = n * {"day": 1, "week": 7, "month": 30, "year": 365}[unit]202 c.take(m, "days_back", f"Released in the last {n} {unit}{'s' if n > 1 else ''}", out.days_back)203 m = c.search(r"\b(?:released|launched|published|announced)?\s*(?:this|the current)\s+year\b")204 if m:205 out.year_from = out.year_to = year_now206 c.take(m, "year", f"Released in {year_now}", year_now)207 m = c.search(r"\b(?:released|launched|published|announced)?\s*(?:last|previous)\s+year\b")208 if m:209 out.year_from = out.year_to = year_now - 1210 c.take(m, "year", f"Released in {year_now - 1}", year_now - 1)211 m = c.search(r"\b(?:(?:released|launched|published|announced)\s+)?(?:since|after|from)\s+(20\d\d)\b")212 if m:213 out.year_from = int(m.group(1))214 c.take(m, "year_from", f"Released since {out.year_from}", out.year_from)215 m = c.search(r"\b(?:(?:released|launched|published|announced)\s+)?(?:before|until|up to)\s+(20\d\d)\b")216 if m:217 out.year_to = int(m.group(1))218 c.take(m, "year_to", f"Released before {out.year_to}", out.year_to)219 m = c.search(r"\b(?:(?:released|launched|published|announced)\s+)?in\s+(20\d\d)\b")220 if m:221 out.year_from = out.year_to = int(m.group(1))222 c.take(m, "year", f"Released in {m.group(1)}", int(m.group(1)))223 m = c.search(r"\b(20\d\d)\b")224 if m and out.year_from is None and out.year_to is None:225 out.year_from = out.year_to = int(m.group(1))226 c.take(m, "year", f"Released in {m.group(1)}", int(m.group(1)))227228 # --- licence229 m = c.search(r"\b(apache(?:[\s-]*2(?:\.0)?)?|mit|bsd|cc[\s-]by|gpl|llama|gemma)\s*(?:licen[cs]e[d]?)\b")230 if m:231 raw = m.group(1).strip()232 key = normalize_license(raw) or LICENSE_WORDS.get(raw.lower())233 if key:234 out.license_key = key235 c.take(m, "license", f"Licence {key}", key)236 m = c.search(r"\b(?:for\s+)?commercial(?:\s+use|ly usable|-use)?\b(?:\s+allowed|\s+ok)?")237 if m:238 out.commercial_use = True239 c.take(m, "commercial_use", "Licence allows commercial use", True)240241 # --- openness242 m = c.search(r"\b(open[- ]?(?:weights?|source|sourced)|open)\b(?=\s+(?:models?|llms?|vision|reasoning|coding|multimodal|\w+\s+models?)\b)") or c.search(r"\bopen[- ]?(?:weights?|source|sourced)\b")243 if m:244 out.openness = "open"245 c.take(m, "openness", "Open weights / open source", "open")246 else:247 m = c.search(r"\b(proprietary|closed(?:[- ]source)?|api[- ]only)\b")248 if m:249 out.openness = "proprietary"250 c.take(m, "openness", "Proprietary (API only)", "proprietary")251252 # --- reasoning253 m = c.search(r"\b(reasoning|thinking)\b(?=\s+(?:models?|llms?)\b)") or c.search(r"\bwith\s+(reasoning|thinking)\b")254 if m:255 out.reasoning = True256 c.take(m, "reasoning", "Reasoning / thinking models", True)257258 # --- modalities259 for w, mod in MODALITY_WORDS.items():260 m = c.search(rf"\b{w}\b")261 if m and mod not in out.modalities:262 out.modalities.append(mod)263 c.take(m, "modality", f"Modality: {mod}", mod)264265 # --- entity type: the earliest surviving type word wins ("papers introducing MoE models" → paper)266 if out.entity_type is None:267 best: tuple[int, str, re.Match[str]] | None = None268 for etype, words in TYPE_WORDS.items():269 for w in sorted(words, key=len, reverse=True):270 m = c.search(rf"\b{re.escape(w)}\b")271 if m and (best is None or m.start() < best[0]):272 best = (m.start(), etype, m)273 break274 if best:275 out.entity_type = best[1]276 c.take(best[2], "entity_type", f"Type: {best[1]}", best[1])277 if out.entity_type is None and (out.openness or out.params_min or out.params_max or out.context_min or out.reasoning or out.memory_gb or out.max_output_price or out.max_input_price):278 out.entity_type = "model"279 out.compiled.append({"filter": "entity_type", "label": "Type: model (implied)", "value": "model", "source_span": None})280281 # --- sort hints282 for w, s in sorted(SORT_WORDS.items(), key=lambda kv: -len(kv[0])):283 m = c.search(rf"\b{re.escape(w)}\b")284 if m:285 out.sort = s286 c.take(m, "sort", f"Sort: {s}", s)287 break288289 # --- organization: "by <Org>" / "from <Org>" / "<Org> models" (verified against the organization alias table by the API layer)290 m = c.search(r"\b(?:by|from)\s+([A-Za-z][\w.&-]+(?:\s+[A-Z][\w.&-]+)?)")291 if m and m.group(1).lower() not in STOP and m.group(1).lower() not in {w for ws in TYPE_WORDS.values() for w in ws}:292 out.organization = q[m.start(1):m.end(1)]293 c.take(m, "organization", f"Organization: {out.organization}", out.organization)294 else:295 # "<Org> models …" only at the very start of the query — a capitalised word in the middle ("introducing MoE models") is free text296 m = re.match(r"\s*(?:(?:all|the|show|list|find)\s+)?([A-Z][\w.&-]*(?:\s+[A-Z][\w.&-]*)?)\s+(?:models?|llms?|papers?|releases?)\b", q)297 if m and not any(s <= m.start(1) < e for s, e in c.consumed) and m.group(1).lower() not in STOP and m.group(1).lower() not in {w for ws in TYPE_WORDS.values() for w in ws} \298 and m.group(1).lower() not in ("open", "reasoning", "vision", "proprietary", "multimodal", "cheapest", "newest", "largest", "best", "new", "moe", "small", "large"):299 out.organization = m.group(1)300 c.consumed.append((m.start(1), m.end(1)))301 out.compiled.append({"filter": "organization", "label": f"Organization: {out.organization}", "value": out.organization, "source_span": m.group(1)})302303 residual = c.residual()304 residual = re.sub(r"\b(" + "|".join(sorted(STOP, key=len, reverse=True)) + r")\b", " ", residual, flags=re.IGNORECASE)305 residual = re.sub(r"[<>≤≥$]", " ", residual)306 out.residual = " ".join(residual.split())307 out.filters["residual"] = out.residual308 out.unrecognised = [w for w in re.findall(r"[\w.\-]+", out.residual) if len(w) > 1] if out.has_structure else []309 return out310311312def params_bound_from_memory(memory_gb: float, quant: str = "4bit") -> int:313 """ESTIMATED largest parameter count that fits `memory_gb` at 8K context (inverse of services.hardware_fit)."""314 kv = hf.KV_GB_PER_8K315 avail = max(0.0, memory_gb - hf.RESERVED_GB - kv)316 return int(avail * 1e9 / (hf.BYTES_PER_PARAM.get(quant, 0.5) * hf.OVERHEAD))317318319# ------------------------------------------------------------------------------------------------------------------ SQL320321322def _num(path: str) -> str:323 return f"(case when {path} ~ '^-?[0-9]+(\\.[0-9]+)?$' then ({path})::double precision end)"324325326def where_for(q: Query) -> tuple[list[str], dict[str, Any]]:327 """Filters shared by `search_entities` and the count query. Every numeric cast is regex-guarded."""328 where = ["e.merged_into is null"]329 params: dict[str, Any] = {}330 if q.entity_type:331 where.append("e.entity_type = :etype" if q.entity_type != "model" else "e.entity_type = 'model'")332 params["etype"] = q.entity_type333 if q.openness == "open":334 where.append("(e.attributes->>'openness' in ('open-weights','open-source','open') or e.attributes->>'weights_availability' = 'open')")335 elif q.openness == "proprietary":336 where.append("e.attributes->>'openness' in ('proprietary','closed')")337 date_col = "coalesce(e.attributes->>'release_date', e.attributes->>'published_at')"338 if q.year_from:339 where.append(f"left({date_col}, 4) >= :yf")340 params["yf"] = str(q.year_from)341 if q.year_to:342 where.append(f"left({date_col}, 4) <= :yt")343 params["yt"] = str(q.year_to)344 if q.days_back:345 where.append(f"({date_col} >= to_char((now() at time zone 'UTC') - make_interval(days => :days), 'YYYY-MM-DD') or e.first_seen_at >= now() - make_interval(days => :days))")346 params["days"] = int(q.days_back)347 pmax = q.params_max348 if q.memory_gb is not None:349 bound = params_bound_from_memory(q.memory_gb)350 pmax = min(pmax, bound) if pmax else bound351 where.append("e.attributes ? 'parameter_count'")352 if q.params_min:353 where.append(f"{_num('e.attributes->>' + chr(39) + 'parameter_count' + chr(39))} >= :pmin")354 params["pmin"] = float(q.params_min)355 if pmax:356 where.append(f"{_num('e.attributes->>' + chr(39) + 'parameter_count' + chr(39))} <= :pmax")357 params["pmax"] = float(pmax)358 if q.context_min:359 where.append(f"{_num('e.attributes->>' + chr(39) + 'context_length' + chr(39))} >= :cmin")360 params["cmin"] = float(q.context_min)361 for i, mod in enumerate(q.modalities):362 where.append(f"(e.attributes->'modalities' ? :mod{i} or e.attributes->'modalities_input' ? :mod{i} or e.attributes->'modalities_output' ? :mod{i}"363 + (" or e.attributes->>'vision' = 'true'" if mod == "image" else "") + ")")364 params[f"mod{i}"] = mod365 if q.reasoning:366 where.append("e.attributes->>'reasoning' = 'true'")367 if q.max_output_price is not None:368 where.append("exists (select 1 from prices p where p.model_id = e.id and p.valid_to is null and p.output_per_mtok > 0 and p.output_per_mtok <= :maxout)")369 params["maxout"] = float(q.max_output_price)370 if q.max_input_price is not None:371 where.append("exists (select 1 from prices p where p.model_id = e.id and p.valid_to is null and p.input_per_mtok > 0 and p.input_per_mtok <= :maxin)")372 params["maxin"] = float(q.max_input_price)373 if q.license_key:374 where.append("(e.attributes->>'license_key' = :lic or lower(e.attributes->>'license') = any(cast(:lic_raw as text[])))")375 params["lic"] = q.license_key376 params["lic_raw"] = _raw_license_labels(q.license_key)377 if q.commercial_use:378 keys = _commercial_keys()379 where.append("(e.attributes->>'license_key' = any(cast(:ckeys as text[])) or lower(e.attributes->>'license') = any(cast(:craw as text[])))")380 params["ckeys"] = keys381 params["craw"] = [lbl for k in keys for lbl in _raw_license_labels(k)]382 if q.organization:383 where.append("exists (select 1 from entities o where o.id = e.organization_id and (o.canonical_name ilike :org or o.slug = :orgslug "384 "or exists (select 1 from entity_aliases a where a.entity_id = o.id and a.alias ilike :org)))")385 params["org"] = q.organization386 params["orgslug"] = q.organization.lower().replace(" ", "-")387 return where, params388389390def _raw_license_labels(key: str) -> list[str]:391 from aiatlas.ontology.licenses import LICENSES392393 info = LICENSES.get(key)394 if not info:395 return [key.lower()]396 return sorted({key.lower(), *(a.lower() for a in info.aliases), *( [info.spdx.lower()] if info.spdx else [])})397398399def _commercial_keys() -> list[str]:400 from aiatlas.ontology.licenses import LICENSES401402 return sorted(k for k, v in LICENSES.items() if v.commercial_use is True and v.weights_downloadable)403404405SORT_SQL = {406 "cheapest": "(select min(p.output_per_mtok) from prices p where p.model_id = e.id and p.valid_to is null and p.output_per_mtok > 0) asc nulls last",407 "newest": "coalesce(e.attributes->>'release_date', e.attributes->>'published_at', '') desc",408 "largest": _num("e.attributes->>'parameter_count'") + " desc nulls last",409 "smallest": _num("e.attributes->>'parameter_count'") + " asc nulls last",410}411412413async def search_entities(conn: AsyncConnection, q: Query, *, limit: int = 30, offset: int = 0, embedding: list[float] | None = None) -> list[dict[str, Any]]:414 where, params = where_for(q)415 params.update({"limit": limit, "offset": offset})416 text = (q.residual or q.filters.get("residual") or "").strip() or ("" if q.has_structure else q.text)417 if q.benchmark and q.entity_type == "benchmark":418 text = q.benchmark419 if q.provider and q.entity_type == "provider":420 text = q.provider421 rank = "coalesce((e.quality->>'score')::float, 0) / 100.0"422 if text:423 params["q"] = text424 params["qlike"] = f"%{text}%"425 params["qprefix"] = " & ".join(f"{w}:*" for w in re.findall(r"\w+", text)[:8]) or text426 where.append("(e.search @@ to_tsquery('simple', :qprefix) or e.canonical_name ilike :qlike or e.canonical_name % :q "427 "or exists (select 1 from entity_aliases a where a.entity_id = e.id and a.alias ilike :qlike))")428 rank = ("(ts_rank_cd(e.search, to_tsquery('simple', :qprefix)) * 2 + similarity(e.canonical_name, :q) * 3 "429 "+ case when e.canonical_name ilike :qlike then 1 else 0 end + coalesce((e.quality->>'score')::float, 0) / 200.0 "430 "+ case e.entity_type when 'model' then 0.3 when 'company' then 0.3 when 'provider' then 0.2 else 0 end)")431 if embedding is not None:432 params["vec"] = "[" + ",".join(f"{x:.6f}" for x in embedding) + "]"433 rank = f"({rank}) + coalesce(1 - (x.embedding <=> cast(:vec as vector)), 0) * 2"434 join = "left join entity_embeddings x on x.entity_id = e.id"435 else:436 join = ""437 order = SORT_SQL.get(q.sort or "", None)438 order_sql = f"{order}, rank desc" if order and q.entity_type in (None, "model") else "rank desc, e.updated_at desc"439 sql = f"""select e.id, e.entity_type, e.canonical_name, e.slug, left(e.description, 240) as description, e.status, e.attributes, e.quality,440 o.canonical_name as organization_name, o.slug as organization_slug, {rank} as rank441 from entities e left join entities o on o.id = e.organization_id {join}442 where {' and '.join(where)} order by {order_sql} limit :limit offset :offset"""443 return await fetch_all(conn, sql, **params)444445446async def verify_organization(conn: AsyncConnection, name: str | None) -> dict[str, Any] | None:447 """The compiler only *proposes* an organization; it counts only when it matches an organization-like entity or one of its aliases."""448 if not name:449 return None450 rows = await fetch_all(conn, """select e.id, e.slug, e.canonical_name from entities e where e.entity_type in ('company','organization','lab','university') and e.merged_into is null451 and (e.canonical_name ilike :n or e.slug = :s or exists (select 1 from entity_aliases a where a.entity_id = e.id and a.alias ilike :n))452 order by length(e.canonical_name) limit 1""", n=name, s=name.lower().replace(" ", "-"))453 return rows[0] if rows else None454455456async def suggest(conn: AsyncConnection, prefix: str, *, limit: int = 8) -> list[dict[str, Any]]:457 return await fetch_all(conn, """select e.id, e.entity_type, e.canonical_name, e.slug, o.canonical_name as organization_name458 from entities e left join entities o on o.id = e.organization_id459 where e.merged_into is null and (e.canonical_name ilike :p or exists (select 1 from entity_aliases a where a.entity_id = e.id and a.alias ilike :p))460 order by case e.entity_type when 'model' then 0 when 'company' then 1 when 'provider' then 2 else 3 end,461 coalesce((e.quality->>'score')::float, 0) desc, length(e.canonical_name) limit :n""", p=f"{prefix}%", n=limit)462463464__all__ = ["Query", "compile_query", "params_bound_from_memory", "search_entities", "suggest", "verify_organization", "where_for"]465