Files
Azornes-Comfyui-Model-Resolver/core/services/search_cache.py
T
Dariusz L b4b03bb335 refactor(search): extract orchestrator, providers, and cache from SearchService
Replace the 1095-line monolithic SearchService with a thin facade that
delegates to SearchOrchestrator. Split the implementation into three
focused modules:

- search_orchestrator.py: request parsing, provider coordination, and
  response construction
- search_providers.py: per-source execution, progress callbacks, and
  async task creation
- search_cache.py: timestamping and stable cache stamping for results

Add unit tests for the new components in
tests/test_search_service_components.py. Preserve the existing HTTP
route behavior by keeping SearchService.search_sources as the stable
route-facing API.
2026-08-02 00:56:18 +02:00

105 lines
3.0 KiB
Python

"""Timestamping and cache helpers for source search results."""
from datetime import datetime, timezone
from typing import Any
SEARCH_RESULT_KEYS = (
"popular",
"model_list",
"huggingface",
"civitai",
"civarchive",
"lora_manager_archive",
)
class SearchResultCache:
"""Keep stable timestamps for equivalent search results."""
def __init__(self, timestamps: dict[str, str]):
self._timestamps = timestamps
@staticmethod
def _current_timestamp() -> str:
return (
datetime.now(timezone.utc)
.replace(microsecond=0)
.isoformat()
)
@staticmethod
def _result_signature(source_key: str, result: Any) -> str:
if isinstance(result, list):
return "|".join(
SearchResultCache._result_signature(source_key, item)
for item in result
)
if not isinstance(result, dict):
return ""
parts = [
source_key,
result.get("download_url")
or result.get("url")
or result.get("model_url")
or "",
result.get("filename") or result.get("path") or "",
result.get("repo_id") or result.get("repo") or "",
result.get("model_id") or "",
result.get("version_id") or "",
result.get("name") or "",
]
return "::".join(str(part).strip() for part in parts)
def stamp_result(
self,
source_key: str,
result: Any,
*,
force_search: bool,
) -> Any:
if isinstance(result, list):
return [
self.stamp_result(
source_key,
item,
force_search=force_search,
)
for item in result
]
if not isinstance(result, dict):
return result
signature = self._result_signature(source_key, result)
if not signature:
return result
timestamp = (
result.get("searched_at")
or result.get("searchedAt")
or (
None
if force_search
else self._timestamps.get(signature)
)
)
if not timestamp:
timestamp = self._current_timestamp()
if force_search:
self._timestamps[signature] = timestamp
else:
self._timestamps.setdefault(signature, timestamp)
result["searched_at"] = timestamp
return result
def stamp_results(self, payload: dict[str, Any], *, force_search: bool):
"""Stamp all provider result collections in a search response."""
for source_key in SEARCH_RESULT_KEYS:
if payload.get(source_key):
payload[source_key] = self.stamp_result(
source_key,
payload[source_key],
force_search=force_search,
)
return payload