platform_access.py 4.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124
  1. from __future__ import annotations
  2. from typing import Any
  3. from content_agent.errors import ContentAgentError, ErrorCode, sanitize_error_detail
  4. from content_agent.interfaces import PlatformSearchClient
  5. _MAX_EXCEPTION_MESSAGE_LENGTH = 1000
  6. def run(
  7. search_queries: list[dict[str, Any]], platform_client: PlatformSearchClient
  8. ) -> dict[str, list[dict[str, Any]]]:
  9. results: list[dict[str, Any]] = []
  10. by_platform_content_id: dict[str, dict[str, Any]] = {}
  11. query_failures: list[dict[str, Any]] = []
  12. for search_query in search_queries:
  13. try:
  14. query_results = platform_client.search(search_query)
  15. except Exception as exc:
  16. query_failures.append(_query_failure(search_query, exc))
  17. continue
  18. for result in query_results:
  19. platform_content_id = result.get("platform_content_id")
  20. enriched = _with_query_source(result, search_query)
  21. if not platform_content_id:
  22. results.append(enriched)
  23. continue
  24. existing = by_platform_content_id.get(platform_content_id)
  25. if existing:
  26. _append_query_source(existing, search_query)
  27. continue
  28. by_platform_content_id[platform_content_id] = enriched
  29. results.append(enriched)
  30. if search_queries and query_failures and len(query_failures) == len(search_queries) and not results:
  31. raise ContentAgentError(
  32. ErrorCode.PLATFORM_REQUEST_FAILED,
  33. "all platform queries failed",
  34. {"query_failures": query_failures},
  35. )
  36. return {"platform_results": results, "query_failures": query_failures}
  37. def _with_query_source(
  38. result: dict[str, Any], search_query: dict[str, Any]
  39. ) -> dict[str, Any]:
  40. enriched = {
  41. **result,
  42. "search_query": search_query["search_query"],
  43. "search_query_generation_method": search_query["search_query_generation_method"],
  44. }
  45. _append_query_source(enriched, search_query)
  46. return enriched
  47. def _append_query_source(result: dict[str, Any], search_query: dict[str, Any]) -> None:
  48. source = {
  49. "search_query_id": search_query["search_query_id"],
  50. "search_query": search_query["search_query"],
  51. "search_query_generation_method": search_query["search_query_generation_method"],
  52. }
  53. if search_query.get("llm_variant_of"):
  54. source["llm_variant_of"] = search_query["llm_variant_of"]
  55. query_sources = result.setdefault("query_sources", [])
  56. if any(
  57. item.get("search_query_id") == source["search_query_id"]
  58. for item in query_sources
  59. ):
  60. return
  61. query_sources.append(source)
  62. result["matched_search_query_ids"] = [
  63. item["search_query_id"] for item in query_sources
  64. ]
  65. result["matched_search_queries"] = [item["search_query"] for item in query_sources]
  66. result["matched_search_query_generation_methods"] = [
  67. item["search_query_generation_method"] for item in query_sources
  68. ]
  69. def _query_failure(search_query: dict[str, Any], exc: Exception) -> dict[str, Any]:
  70. if isinstance(exc, ContentAgentError):
  71. error_code = exc.error_code.value
  72. message = exc.message
  73. detail = exc.detail
  74. else:
  75. error_code = ErrorCode.PLATFORM_REQUEST_FAILED.value
  76. message = "platform query failed"
  77. detail = _exception_detail(exc)
  78. return {
  79. "search_query_id": search_query["search_query_id"],
  80. "search_query": search_query["search_query"],
  81. "search_query_generation_method": search_query.get(
  82. "search_query_generation_method"
  83. ),
  84. "status": "failed",
  85. "error_code": error_code,
  86. "message": message,
  87. "error_detail": detail,
  88. }
  89. def _exception_detail(exc: Exception) -> dict[str, Any]:
  90. detail: dict[str, Any] = {"exception_type": type(exc).__name__}
  91. message = _truncate_exception_message(str(exc))
  92. if message:
  93. detail["exception_message"] = message
  94. structured_detail = getattr(exc, "detail", None)
  95. if isinstance(structured_detail, dict):
  96. detail.update(sanitize_error_detail(structured_detail))
  97. cause = exc.__cause__ or exc.__context__
  98. if cause is not None:
  99. detail["cause_exception_type"] = type(cause).__name__
  100. cause_message = _truncate_exception_message(str(cause))
  101. if cause_message:
  102. detail["cause_message"] = cause_message
  103. return detail
  104. def _truncate_exception_message(message: str) -> str:
  105. if len(message) <= _MAX_EXCEPTION_MESSAGE_LENGTH:
  106. return message
  107. return f"{message[:_MAX_EXCEPTION_MESSAGE_LENGTH]}..."