| from __future__ import annotations |
|
|
| import hashlib |
| import json |
| import logging |
| import os |
| import re |
|
|
| from backend.env import load_project_env |
| from backend.models import SpaceItem |
| from backend.storage import cache_get, cache_set |
| from backend.tracks import TRACK_NAMES |
|
|
| logger = logging.getLogger(__name__) |
|
|
| load_project_env() |
|
|
| QUERY_PROFILE_CACHE_VERSION = "v6" |
| REASON_CACHE_VERSION = "v22" |
| BLURB_CACHE_VERSION = "v10" |
| RERANK_CACHE_VERSION = "v11" |
| NEBIUS_BASE_URL = "https://api.tokenfactory.nebius.com/v1/" |
| NEBIUS_MODEL = os.getenv("NEBIUS_MODEL", "nvidia/Nemotron-3-Nano-Omni") |
|
|
|
|
| class LLMService: |
| def __init__(self) -> None: |
| self._client = None |
|
|
| def available(self) -> bool: |
| return self._load_client() is not None |
|
|
| @staticmethod |
| def _coerce_space(space: SpaceItem | dict) -> SpaceItem: |
| if isinstance(space, SpaceItem): |
| return space |
| if isinstance(space, dict): |
| readme = str(space.get("readme", space.get("readme_text", ""))) |
| summary = str(space.get("summary", space.get("desc", ""))) |
| zone = str(space.get("zone", space.get("category", "Other"))) |
| return SpaceItem.from_dict( |
| { |
| "repo_id": space.get("repo_id", space.get("id", "")), |
| "title": space.get("title", space.get("name", "")), |
| "summary": summary, |
| "url": space.get("url", ""), |
| "zone": zone, |
| "track": space.get("track", ""), |
| "tags": space.get("tags", []), |
| "difficulty": space.get("difficulty", "casual"), |
| "likes": space.get("likes", 0), |
| "sdk": space.get("sdk", "unknown"), |
| "status": space.get("status", "unknown"), |
| "last_modified": space.get("last_modified", ""), |
| "emoji": space.get("emoji", "🚀"), |
| "readme_text": readme, |
| } |
| ) |
| raise TypeError(f"Unsupported space type: {type(space)!r}") |
|
|
| @staticmethod |
| def _coerce_spaces(spaces: list[SpaceItem | dict]) -> list[SpaceItem]: |
| return [LLMService._coerce_space(space) for space in spaces] |
|
|
| @staticmethod |
| def _coerce_semantic_profile(profile) -> dict: |
| if isinstance(profile, dict): |
| return dict(profile) |
| if isinstance(profile, str): |
| try: |
| parsed = json.loads(profile) |
| except Exception: |
| return {} |
| if isinstance(parsed, dict): |
| return parsed |
| return {} |
|
|
| @staticmethod |
| def _normalize_text(text: str) -> str: |
| return " ".join((text or "").split()) |
|
|
| @staticmethod |
| def _strip_clause(text: str) -> str: |
| clause = LLMService._normalize_text(text).strip(" .:-") |
| clause = re.sub(r"^(?:the|this|that)\s+(?:app|space|project)\s+", "", clause, flags=re.I) |
| clause = re.sub(r"^(?:it|you|users?|people|we)\s+", "", clause, flags=re.I) |
| clause = re.sub(r"^(?:can|could|will|would|should|lets?|helps?|allows?|enables?|generates?|creates?|shows?|returns?|tracks?|flags?|surfaces?|guides?|provides?|keeps?|turns?|makes?)\s+", "", clause, flags=re.I) |
| return clause.strip(" .:-") |
|
|
| @staticmethod |
| def _query_fit_phrase(query: str, liked_text: str = "", semantic_profile: dict | None = None) -> str: |
| normalized = (query or "").lower() |
| combined = f"{query or ''} {liked_text or ''}".lower() |
| if any(phrase in combined for phrase in ("playing cards", "card game", "card-based", "card matching", "deckbuilder", "poker")): |
| return "users looking for casual card-based games" |
| if any(phrase in combined for phrase in ("tutor", "teaching", "lesson", "study", "education")): |
| return "users looking for tutor or learning apps" |
| if any(phrase in combined for phrase in ("writing", "journal", "story", "creative")): |
| return "people looking for writing or storytelling apps" |
| if any(phrase in combined for phrase in ("news", "headline", "article", "report")): |
| return "people looking for simple news explainers" |
| if any(phrase in combined for phrase in ("security", "privacy", "cyber", "phishing", "scam", "fraud", "malware")): |
| return "people looking for security and privacy tools" |
| profile = LLMService._coerce_semantic_profile(semantic_profile) |
| audience = str(profile.get("audience", "")).strip() |
| if audience: |
| return audience |
| if normalized.strip(): |
| return f"people exploring {normalized.strip()}" |
| return "people browsing this category" |
|
|
| @staticmethod |
| def _activity_phrase(summary: str, semantic_profile: dict | None = None, evidence: str = "") -> str: |
| profile = LLMService._coerce_semantic_profile(semantic_profile) |
| for key in ("primary_activity", "activity", "one_liner"): |
| value = str(profile.get(key, "")).strip() |
| if value: |
| return value |
| for source in (evidence, summary): |
| text = str(source or "").strip() |
| if text: |
| clause = LLMService._strip_clause(text) |
| if clause: |
| return clause |
| return "a focused app" |
|
|
| @staticmethod |
| def _first_profile_clause(profile: dict | None, keys: tuple[str, ...]) -> str: |
| data = LLMService._coerce_semantic_profile(profile) |
| for key in keys: |
| value = data.get(key) |
| if isinstance(value, list): |
| for item in value: |
| clause = LLMService._strip_clause(str(item)) |
| if clause: |
| return clause |
| elif value: |
| clause = LLMService._strip_clause(str(value)) |
| if clause: |
| return clause |
| return "" |
|
|
| @staticmethod |
| def _sentence_from_clause(prefix: str, clause: str) -> str: |
| clause = LLMService._strip_clause(clause) |
| if not clause: |
| return "" |
| first_word = clause.split(" ", 1)[0].lower() |
| if prefix.lower() == "it" and first_word in {"helps", "lets", "allows", "enables", "generates", "creates", "shows", "returns", "tracks", "flags", "surfaces", "guides", "provides", "keeps", "turns", "makes"}: |
| return f"It {clause}" |
| return f"{prefix} {clause}" |
|
|
| @staticmethod |
| def _build_local_recommendation_reason( |
| *, |
| query: str, |
| title: str, |
| summary: str, |
| evidence: str, |
| semantic_profile: dict | None = None, |
| liked_text: str = "", |
| ) -> str: |
| profile = LLMService._coerce_semantic_profile(semantic_profile) |
| activity = LLMService._activity_phrase(summary, profile, evidence) |
| action_clause = LLMService._first_profile_clause(profile, ("core_actions", "key_snippets")) |
| value_clause = LLMService._first_profile_clause(profile, ("value_points", "outcomes", "key_snippets")) |
| fit_phrase = LLMService._query_fit_phrase(query, liked_text=liked_text, semantic_profile=profile) |
|
|
| first_sentence = f"{title or 'This app'} offers {activity}" |
| if action_clause: |
| first_sentence = f"{first_sentence} where you {action_clause}" |
| first_sentence = f"{first_sentence}, making it a great fit for {fit_phrase}" |
|
|
| second_sentence = "" |
| if value_clause: |
| second_sentence = LLMService._sentence_from_clause("Its", value_clause) |
| if not second_sentence: |
| second_sentence = f"That makes it a strong fit for {fit_phrase}" |
|
|
| reason = f"{first_sentence}. {second_sentence}." |
| reason = LLMService._normalize_output(reason) |
| if len(reason.split(". ")) > 2: |
| reason = " ".join(reason.split(". ")[:2]).strip() |
| return reason |
|
|
| @staticmethod |
| def _candidate_reason(candidate: dict, query: str, profile: dict | None = None) -> str: |
| title = str(candidate.get("title") or candidate.get("name") or "").strip() |
| summary = str(candidate.get("summary") or candidate.get("description") or candidate.get("desc") or "").strip() |
| semantic_profile = candidate.get("semantic_profile") if isinstance(candidate.get("semantic_profile"), dict) else {} |
| if not semantic_profile and isinstance(profile, dict): |
| semantic_profile = profile |
| evidence = str(candidate.get("readme_excerpt") or candidate.get("readme_summary") or summary or "").strip() |
| reason = LLMService._build_local_recommendation_reason( |
| query=query, |
| title=title, |
| summary=summary, |
| evidence=evidence, |
| semantic_profile=semantic_profile, |
| ) |
| return reason |
|
|
| @staticmethod |
| def _tokenize_terms(text: str) -> list[str]: |
| terms: list[str] = [] |
| for token in re.findall(r"[a-z0-9]+", (text or "").lower()): |
| if len(token) > 2 and token not in terms: |
| terms.append(token) |
| return terms |
|
|
| @staticmethod |
| def _candidate_text(candidate: dict) -> str: |
| parts = [ |
| str(candidate.get("title") or candidate.get("name") or ""), |
| str(candidate.get("summary") or candidate.get("description") or candidate.get("desc") or ""), |
| str(candidate.get("track") or ""), |
| str(candidate.get("category") or candidate.get("zone") or ""), |
| " ".join(str(tag) for tag in (candidate.get("tags", []) or [])[:8]), |
| str(candidate.get("readme_excerpt") or candidate.get("readme_summary") or candidate.get("readme") or ""), |
| ] |
| profile = candidate.get("semantic_profile") if isinstance(candidate.get("semantic_profile"), dict) else {} |
| if profile: |
| for key in ("primary_activity", "audience", "value_proposition", "value_points", "core_actions", "key_snippets"): |
| value = profile.get(key) |
| if isinstance(value, list): |
| parts.extend(str(item) for item in value if str(item).strip()) |
| elif value: |
| parts.append(str(value)) |
| return LLMService._normalize_text(" ".join(parts)) |
|
|
| @staticmethod |
| def _score_candidate_text(query_terms: list[str], candidate_text: str) -> tuple[float, list[str]]: |
| source = candidate_text.lower() |
| hits = [term for term in query_terms if term and term in source] |
| return float(len(hits)), hits |
|
|
| def _load_client(self): |
| if self._client is False: |
| return None |
| if self._client is not None: |
| return self._client |
|
|
| api_key = os.environ.get("NEBIUS_API_KEY", "").strip() |
| if not api_key: |
| logger.warning("NEBIUS_API_KEY is not configured; falling back to local review copy.") |
| self._client = False |
| return None |
|
|
| try: |
| from openai import OpenAI |
| except Exception as exc: |
| logger.warning("openai package is unavailable; falling back to local review copy: %s", exc) |
| self._client = False |
| return None |
|
|
| try: |
| self._client = OpenAI(base_url=NEBIUS_BASE_URL, api_key=api_key) |
| return self._client |
| except Exception as exc: |
| logger.warning("Nebius client initialization failed; falling back to local review copy: %s", exc) |
| self._client = False |
| return None |
|
|
| def _chat_via_http( |
| self, |
| *, |
| messages: list[dict], |
| max_tokens: int, |
| temperature: float, |
| response_format: dict | None = None, |
| reasoning_effort: str | None = None, |
| ) -> str: |
| return "" |
|
|
| @staticmethod |
| def _normalize_output(text: str) -> str: |
| text = (text or "").strip() |
| text = text.replace("\r", " ").replace("\n", " ") |
| text = re.sub(r"<think>.*?</think>", " ", text, flags=re.S | re.I) |
| if text.lower().startswith("<think>") and "</think>" in text.lower(): |
| text = text.split("</think>", 1)[1] |
| text = text.replace("`", "") |
| text = text.replace("*", "") |
| text = text.replace("|", " ") |
| text = " ".join(text.split()) |
| text = text.removeprefix("Why it fits:") |
| text = text.removeprefix("Reason:") |
| return text.strip() |
|
|
| @staticmethod |
| def _unwrap_reason_text(text: str) -> str: |
| reason = LLMService._normalize_output(text) |
| if reason.startswith("{") and '"reason"' in reason: |
| try: |
| payload = json.loads(reason) |
| if isinstance(payload, dict): |
| reason = LLMService._normalize_output(str(payload.get("reason", ""))) |
| except Exception: |
| pass |
| return reason |
|
|
| @staticmethod |
| def _finalize_reason_text(text: str) -> str: |
| reason = LLMService._unwrap_reason_text(text) |
| if not reason: |
| return "" |
|
|
| reason = re.sub(r"\s+", " ", reason).strip(" .:-") |
| if not reason: |
| return "" |
|
|
| sentence_parts = re.split(r"(?<=[.!?])\s+", reason) |
| if len(sentence_parts) > 2: |
| reason = " ".join(sentence_parts[:2]).strip() |
| return reason.strip() |
|
|
| @staticmethod |
| def _parse_json_text(text: str, expected: str = "dict"): |
| raw = (text or "").strip() |
| if not raw: |
| return None |
|
|
| candidates = [raw] |
| if raw.startswith("```"): |
| fenced = re.sub(r"^```(?:json)?\s*|\s*```$", "", raw, flags=re.S | re.I).strip() |
| if fenced: |
| candidates.append(fenced) |
|
|
| for opener, closer in (("{", "}"), ("[", "]")): |
| start = raw.find(opener) |
| end = raw.rfind(closer) |
| if start != -1 and end != -1 and end > start: |
| sliced = raw[start : end + 1].strip() |
| if sliced: |
| candidates.append(sliced) |
|
|
| for candidate in candidates: |
| candidate = re.sub(r"<think>.*?</think>", " ", candidate, flags=re.S | re.I).strip() |
| try: |
| parsed = json.loads(candidate) |
| except Exception: |
| continue |
| if expected == "dict" and isinstance(parsed, dict): |
| return parsed |
| if expected == "list" and isinstance(parsed, list): |
| return parsed |
| if expected == "any": |
| return parsed |
| if isinstance(parsed, str): |
| try: |
| nested = json.loads(parsed) |
| except Exception: |
| continue |
| if expected == "dict" and isinstance(nested, dict): |
| return nested |
| if expected == "list" and isinstance(nested, list): |
| return nested |
| return None |
|
|
| @staticmethod |
| def _looks_generic_reason(text: str) -> bool: |
| lowered = (text or "").lower() |
| if not lowered.strip(): |
| return True |
| generic_phrases = [ |
| "this space appears related", |
| "this one stands out because", |
| "the readme points to", |
| "it fits the query", |
| "core interaction", |
| "broad category label", |
| "<think>", |
| "this feels relevant to", |
| "which makes the connection feel concrete rather than generic", |
| "the main interaction already lines up", |
| "it seems to focus on", |
| ] |
| if any(phrase in lowered for phrase in generic_phrases): |
| return True |
| code_markers = ["def ", "import ", "from ", "=>", "::", "()", "filename=", "duration=", "type="] |
| if sum(1 for marker in code_markers if marker in lowered) >= 2: |
| return True |
| return False |
|
|
| @staticmethod |
| def _extract_message_text(message_content) -> str: |
| if isinstance(message_content, str): |
| return message_content.strip() |
| if isinstance(message_content, list): |
| parts: list[str] = [] |
| for item in message_content: |
| if isinstance(item, dict): |
| item_type = str(item.get("type", "")).strip().lower() |
| if item_type == "text": |
| value = str(item.get("text", "")).strip() |
| if value: |
| parts.append(value) |
| elif item: |
| parts.append(str(item).strip()) |
| return "\n".join(part for part in parts if part).strip() |
| if message_content is None: |
| return "" |
| return str(message_content).strip() |
|
|
| @staticmethod |
| def _preview_object(value, limit: int = 2000) -> str: |
| try: |
| if hasattr(value, "model_dump"): |
| value = value.model_dump() |
| elif hasattr(value, "to_dict"): |
| value = value.to_dict() |
| elif hasattr(value, "__dict__") and not isinstance(value, (str, bytes, dict, list, tuple)): |
| value = dict(value.__dict__) |
| text = json.dumps(value, ensure_ascii=False, default=str) |
| except Exception: |
| text = str(value) |
| text = text.replace("\n", " ") |
| return text[:limit] |
|
|
| @staticmethod |
| def _extract_choice_text(choice) -> str: |
| if choice is None: |
| return "" |
|
|
| message = getattr(choice, "message", None) |
| if message is None and isinstance(choice, dict): |
| message = choice.get("message") |
| if message is None: |
| return "" |
|
|
| content = getattr(message, "content", None) |
| if content is None and isinstance(message, dict): |
| content = message.get("content") |
| text = LLMService._extract_message_text(content) |
| if text: |
| return text |
|
|
| for attr in ("parsed",): |
| value = getattr(message, attr, None) |
| if value is None and isinstance(message, dict): |
| value = message.get(attr) |
| if value: |
| text = LLMService._extract_message_text(value) |
| if text: |
| return text |
| return "" |
|
|
| @staticmethod |
| def _prepare_messages(messages: list[dict]) -> list[dict]: |
| prepared: list[dict] = [] |
| for message in messages: |
| if not isinstance(message, dict): |
| continue |
| role = str(message.get("role", "user")).strip() or "user" |
| content = message.get("content", "") |
| if role == "system" or isinstance(content, list): |
| prepared.append({"role": role, "content": content}) |
| continue |
| prepared.append( |
| { |
| "role": role, |
| "content": [ |
| { |
| "type": "text", |
| "text": str(content), |
| } |
| ], |
| } |
| ) |
| return prepared |
|
|
| def chat( |
| self, |
| messages: list[dict], |
| cache_key: str | None = None, |
| max_tokens: int = 180, |
| temperature: float = 0.4, |
| response_format: dict | None = None, |
| reasoning_effort: str | None = None, |
| ) -> str: |
| if cache_key: |
| cached = cache_get(cache_key) |
| if cached: |
| return cached |
|
|
| client = self._load_client() |
| if client is None: |
| return "" |
|
|
| try: |
| response = client.chat.completions.create( |
| model=NEBIUS_MODEL, |
| messages=self._prepare_messages(messages), |
| max_tokens=max_tokens, |
| temperature=temperature, |
| ) |
| choice = response.choices[0] if getattr(response, "choices", None) else None |
| text = self._extract_choice_text(choice) |
| if cache_key and text: |
| cache_set(cache_key, text) |
| return text |
| except Exception as exc: |
| logger.warning("Nebius review generation failed; falling back to local review copy: %s", exc) |
| return "" |
|
|
| def _chat( |
| self, |
| system_prompt: str, |
| user_prompt: str, |
| cache_key: str | None = None, |
| max_tokens: int = 180, |
| temperature: float = 0.4, |
| response_format: dict | None = None, |
| reasoning_effort: str | None = None, |
| ) -> str: |
| return self.chat( |
| [ |
| {"role": "system", "content": system_prompt}, |
| {"role": "user", "content": [{"type": "text", "text": user_prompt}]}, |
| ], |
| cache_key=cache_key, |
| max_tokens=max_tokens, |
| temperature=temperature, |
| response_format=response_format, |
| reasoning_effort=reasoning_effort, |
| ) |
|
|
| def rewrite_recommendation_reason( |
| self, |
| *, |
| query: str, |
| repo_id: str = "", |
| title: str, |
| summary: str, |
| track: str, |
| zone: str, |
| tags: list[str], |
| evidence: str, |
| matched_signals: list[str] | None = None, |
| liked_text: str = "", |
| semantic_profile: dict | None = None, |
| readme_text: str = "", |
| ) -> str: |
| matched_signals = matched_signals or [] |
| cache_payload = { |
| "version": REASON_CACHE_VERSION, |
| "query": query, |
| "repo_id": repo_id, |
| "title": title, |
| "summary": summary, |
| "track": track, |
| "zone": zone, |
| "tags": tags[:8], |
| "evidence": evidence[:900], |
| "matched_signals": matched_signals[:6], |
| "liked_text": liked_text, |
| "semantic_profile": self._coerce_semantic_profile(semantic_profile), |
| "readme_text": readme_text[:4000], |
| } |
| cache_key = "reason:" + hashlib.sha1( |
| json.dumps(cache_payload, sort_keys=True, ensure_ascii=False).encode("utf-8") |
| ).hexdigest() |
| cached = cache_get(cache_key) |
| if cached: |
| reason = self._finalize_reason_text(cached) |
| if reason: |
| return reason |
|
|
| profile = self._coerce_semantic_profile(semantic_profile) |
| readme_excerpt = self._normalize_text(readme_text)[:12000] |
| system_prompt = ( |
| "You write the visible Why it fits text for an app recommendation card. " |
| "Return only the final user-facing blurb, never reasoning or analysis. " |
| "Output exactly 2 short sentences. " |
| "Use a warm, natural, editorial product-review tone. " |
| "Sentence 1 must say what the app experience actually is. " |
| "Sentence 2 must explain why it matches the user's query. " |
| "Do not mention the README, instructions, metadata, evidence, or evaluation process." |
| ) |
| user_prompt = "\n".join( |
| [ |
| f"User query: {query or 'none'}", |
| f"App name: {title or 'This app'}", |
| f"Short summary: {summary or 'not provided'}", |
| f"Track: {track or 'unknown'}", |
| f"Category: {zone or 'Other'}", |
| f"Tags: {', '.join(tags[:8]) or 'none'}", |
| f"Helpful matching signals: {', '.join(matched_signals[:6]) or 'none'}", |
| f"Semantic profile: {json.dumps(profile, ensure_ascii=False) if profile else '{}'}", |
| "Write in this tone:", |
| "MatchWise offers a fun card-playing experience where you flip and match cards, making it a great fit for users looking for casual card-based games. Its endless AI-generated challenges keep the gameplay fresh and engaging.", |
| "README content:", |
| readme_excerpt or evidence or summary or "not available", |
| "Return only the final 2 sentences.", |
| ] |
| ) |
| text = self._chat( |
| system_prompt, |
| user_prompt, |
| cache_key=cache_key, |
| max_tokens=80, |
| temperature=0.1, |
| ) |
| reason = self._finalize_reason_text(text) if text else "" |
| if not reason: |
| retry_prompt = "\n".join( |
| [ |
| f"User query: {query or 'none'}", |
| f"App name: {title or 'This app'}", |
| f"Summary: {summary or 'not provided'}", |
| f"Evidence: {evidence or 'not available'}", |
| "Return only the final answer. No thinking. No analysis. No draft. Exactly 2 sentences.", |
| ] |
| ) |
| text = self._chat( |
| system_prompt, |
| retry_prompt, |
| max_tokens=80, |
| temperature=0.0, |
| ) |
| reason = self._finalize_reason_text(text) if text else "" |
| if not reason or self._looks_generic_reason(reason): |
| reason = self._build_local_recommendation_reason( |
| query=query, |
| title=title, |
| summary=summary, |
| evidence=evidence, |
| semantic_profile=semantic_profile, |
| liked_text=liked_text, |
| ) |
| if reason: |
| cache_set(cache_key, reason) |
| return reason |
| return "" |
|
|
| def generate_reason(self, query: str, space: SpaceItem) -> str: |
| return self.rewrite_recommendation_reason( |
| query=query, |
| title=space.title, |
| summary=space.summary, |
| track=space.track, |
| zone=space.zone, |
| tags=space.tags, |
| evidence=space.readme_text[:900].strip() or space.summary, |
| matched_signals=[space.track] if space.track else [], |
| readme_text=space.readme_text, |
| ) |
|
|
| def generate_recommendation_blurb(self, query: str, space: SpaceItem) -> dict: |
| cache_key = f"{BLURB_CACHE_VERSION}:blurb:{space.repo_id}:{query.lower().strip()}" |
| cached = cache_get(cache_key) |
| if cached: |
| try: |
| payload = json.loads(cached) |
| if isinstance(payload, dict): |
| payload.setdefault("summary", "") |
| payload.setdefault("reason", "") |
| payload["reason"] = self._finalize_reason_text(payload.get("reason", "")) |
| return payload |
| except Exception: |
| pass |
|
|
| readme_excerpt = space.readme_text[:900].strip() |
| reason = self.rewrite_recommendation_reason( |
| query=query, |
| title=space.title, |
| summary=space.summary, |
| track=space.track, |
| zone=space.zone, |
| tags=space.tags, |
| evidence=readme_excerpt or space.summary, |
| matched_signals=[space.track] if space.track else [], |
| readme_text=space.readme_text, |
| ) |
| payload = {"summary": space.summary or "", "reason": reason} |
| if reason: |
| cache_set(cache_key, json.dumps(payload, ensure_ascii=False)) |
| return payload |
|
|
| def generate_query_profile(self, query: str, spaces: list[SpaceItem | dict]) -> dict: |
| cache_key = f"{QUERY_PROFILE_CACHE_VERSION}:profile:{query.lower().strip()}" |
| cached = cache_get(cache_key) |
| if cached: |
| try: |
| payload = json.loads(cached) |
| if isinstance(payload, dict): |
| return payload |
| except Exception: |
| pass |
|
|
| coerced_spaces = self._coerce_spaces(spaces) |
| sample_spaces = sorted(coerced_spaces, key=lambda item: (-item.likes, item.title.lower()))[:12] |
| query_terms = self._tokenize_terms(query) |
| fallback = self._fallback_query_profile(query, coerced_spaces) |
| track_scores = {track: 0.0 for track in TRACK_NAMES} |
| for space in sample_spaces: |
| candidate_text = self._candidate_text( |
| { |
| "title": space.title, |
| "summary": space.summary, |
| "track": space.track, |
| "zone": space.zone, |
| "tags": space.tags, |
| "readme_excerpt": space.readme_text[:300], |
| "semantic_profile": {}, |
| } |
| ) |
| score, _ = self._score_candidate_text(query_terms, candidate_text) |
| if space.track in track_scores: |
| track_scores[space.track] += score + (space.likes / 1000.0) |
| primary_track = max(track_scores.items(), key=lambda item: item[1])[0] if any(track_scores.values()) else fallback.get("primary_track") |
| keywords = query_terms[:8] |
| if not keywords and query.strip(): |
| keywords = [query.strip()] |
| must_have = keywords[:4] |
| avoid = [term for term in fallback.get("avoid", []) if term] |
| confidence = 0.25 |
| if query_terms: |
| confidence += min(0.5, len(query_terms) * 0.08) |
| if primary_track: |
| confidence += 0.1 |
| payload = { |
| "primary_track": primary_track, |
| "keywords": keywords, |
| "must_have": must_have, |
| "avoid": avoid, |
| "summary": query.strip() or fallback.get("summary", ""), |
| "confidence": round(min(confidence, 0.95), 2) if query else 0.0, |
| } |
| cache_set(cache_key, json.dumps(payload, ensure_ascii=False)) |
| return payload |
|
|
| def generate_example_queries(self, spaces: list[SpaceItem | dict], track_names: list[str]) -> list[str]: |
| cache_key = "example_queries:v2" |
| cached = cache_get(cache_key) |
| if cached: |
| try: |
| payload = json.loads(cached) |
| if isinstance(payload, list) and payload: |
| return [str(item) for item in payload][:8] |
| except Exception: |
| pass |
|
|
| coerced_spaces = self._coerce_spaces(spaces) |
| fallback = self._fallback_example_queries(coerced_spaces) |
| cache_set(cache_key, json.dumps(fallback, ensure_ascii=False)) |
| return fallback |
|
|
| @staticmethod |
| def _candidate_prompt_payload(candidate: dict) -> dict: |
| return { |
| "repo_id": str(candidate.get("repo_id") or candidate.get("id") or "").strip(), |
| "title": str(candidate.get("title") or candidate.get("name") or "").strip(), |
| "summary": str(candidate.get("summary") or candidate.get("description") or candidate.get("desc") or "").strip(), |
| "track": str(candidate.get("track") or "").strip(), |
| "category": str(candidate.get("category") or candidate.get("zone") or "").strip(), |
| "tags": list(candidate.get("tags", []) or [])[:8], |
| "likes": int(candidate.get("likes", 0) or 0), |
| "readme_excerpt": str( |
| candidate.get("readme_excerpt") |
| or candidate.get("readme_summary") |
| or candidate.get("readme", "") |
| or "" |
| )[:900], |
| "signals": list(candidate.get("matched_signals", []) or [])[:6], |
| } |
|
|
| def rerank_candidates(self, query: str, candidates: list[dict], profile: dict | None = None, limit: int = 12) -> list[dict]: |
| if not candidates: |
| return [] |
|
|
| profile = profile or {} |
| prompt_candidates = [self._candidate_prompt_payload(item) for item in candidates] |
| candidate_ids = ",".join(item.get("repo_id", "") for item in prompt_candidates) |
| cache_key = f"{RERANK_CACHE_VERSION}:rerank:" + hashlib.sha1( |
| f"{query.lower().strip()}|{candidate_ids}|{json.dumps(profile, sort_keys=True, ensure_ascii=False)}".encode( |
| "utf-8" |
| ) |
| ).hexdigest() |
| cached = cache_get(cache_key) |
| if cached: |
| try: |
| payload = json.loads(cached) |
| if isinstance(payload, list): |
| return [item for item in payload if isinstance(item, dict)][:limit] |
| except Exception: |
| pass |
|
|
| query_terms = self._tokenize_terms(query) |
| profile_terms = [] |
| for key in ("keywords", "must_have", "avoid", "summary"): |
| value = profile.get(key, []) |
| if isinstance(value, list): |
| profile_terms.extend(str(item) for item in value if str(item).strip()) |
| elif value: |
| profile_terms.append(str(value)) |
| profile_terms = self._tokenize_terms(" ".join(profile_terms)) |
|
|
| scored_candidates: list[dict] = [] |
| for item in candidates: |
| repo_id = str(item.get("repo_id", "")).strip() |
| if not repo_id: |
| continue |
| candidate_text = self._candidate_text(item) |
| query_score, query_hits = self._score_candidate_text(query_terms, candidate_text) |
| profile_score, profile_hits = self._score_candidate_text(profile_terms, candidate_text) if profile_terms else (0.0, []) |
| signal_text = " ".join(str(signal) for signal in (item.get("signals", []) or []) if str(signal).strip()) |
| signal_score, signal_hits = self._score_candidate_text(query_terms, signal_text) if signal_text else (0.0, []) |
| likes = float(item.get("likes", 0) or 0) |
| likes_bonus = min(likes / 5000.0, 0.12) |
| score = query_score * 5.0 + profile_score * 2.0 + signal_score * 1.5 + likes_bonus |
| if not query_terms and not profile_terms: |
| score += float(item.get("rank_score", 0) or 0) / 100.0 |
| if query_terms and not query_hits and not profile_hits and not signal_hits: |
| score *= 0.15 |
| reason = self._candidate_reason(item, query, profile) |
| scored_candidates.append( |
| { |
| "repo_id": repo_id, |
| "reason": reason, |
| "rank_score": round(min(max(score * 10.0, 0.0), 100.0), 2), |
| } |
| ) |
|
|
| scored_candidates.sort(key=lambda item: (-float(item.get("rank_score", 0) or 0), str(item.get("repo_id", "")))) |
| fallback = scored_candidates[:limit] |
| cache_set(cache_key, json.dumps(fallback, ensure_ascii=False)) |
| return fallback |
|
|
| def _fallback_example_queries(self, spaces: list[SpaceItem]) -> list[str]: |
| ordered = sorted(spaces, key=lambda s: (-s.likes, s.title.lower())) |
| buckets: dict[str, list[SpaceItem]] = {} |
| for space in ordered: |
| buckets.setdefault(space.track or "Other", []).append(space) |
|
|
| queries: list[str] = [] |
| for track, track_spaces in buckets.items(): |
| if track_spaces: |
| space = track_spaces[0] |
| queries.append(f"find apps like {space.title.lower()} for {track.lower()}") |
|
|
| while len(queries) < 8: |
| queries.extend( |
| [ |
| "fun learning app for beginners", |
| "creative story or writing space", |
| "useful AI tools for builders", |
| "game or puzzle space to try", |
| ] |
| ) |
|
|
| deduped: list[str] = [] |
| for query in queries: |
| if query not in deduped: |
| deduped.append(query) |
| return deduped[:8] |
|
|
| def _fallback_query_profile(self, query: str, spaces: list[SpaceItem]) -> dict: |
| tokens = [token for token in query.lower().split() if len(token) > 2] |
| track_scores = {track: 0 for track in TRACK_NAMES} |
| for token in tokens: |
| if token in {"agent", "llm", "llama", "gguf", "api", "builder", "trace", "dataset", "career", "job", "security", "cybersecurity", "privacy", "audit", "phishing", "scam", "fraud", "vulnerability", "threat"}: |
| track_scores["Backyard AI"] += 1 |
| if token in {"game", "quiz", "puzzle", "learning", "study", "story", "creative", "language", "translate", "demo"}: |
| track_scores["An Adventure in Thousand Token Wood"] += 1 |
|
|
| primary_track = max(track_scores.items(), key=lambda item: item[1])[0] if any(track_scores.values()) else None |
| return { |
| "primary_track": primary_track, |
| "keywords": tokens[:8], |
| "must_have": tokens[:4], |
| "avoid": [], |
| "summary": query, |
| "confidence": 0.35 if query else 0.0, |
| } |
|
|
|
|
| _SERVICE: LLMService | None = None |
|
|
|
|
| def get_llm_service() -> LLMService: |
| global _SERVICE |
| if _SERVICE is None: |
| _SERVICE = LLMService() |
| return _SERVICE |
|
|
|
|
| def generate_recommendation_reason(query: str, space: SpaceItem) -> str: |
| return get_llm_service().generate_reason(query, space) |
|
|
|
|
| def generate_recommendation_blurb(query: str, space: SpaceItem) -> dict: |
| return get_llm_service().generate_recommendation_blurb(query, space) |
|
|
|
|
| def generate_example_queries(spaces: list[SpaceItem], track_names: list[str]) -> list[str]: |
| return get_llm_service().generate_example_queries(spaces, track_names) |
|
|
|
|
| def generate_query_profile(query: str, spaces: list[SpaceItem]) -> dict: |
| return get_llm_service().generate_query_profile(query, spaces) |
|
|
|
|
| def rerank_candidates(query: str, candidates: list[dict], profile: dict | None = None, limit: int = 12) -> list[dict]: |
| return get_llm_service().rerank_candidates(query, candidates, profile=profile, limit=limit) |
|
|