tejasashinde's picture
Corrected logic and UI
6f97a9b
Raw
History Blame Contribute Delete
37.3 kB
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)