Some checks failed
Code Review / ai-code-review (push) Successful in 13s
CD Pipeline / tests (push) Successful in 1m39s
CD Pipeline / build-and-deploy (push) Successful in 4m35s
CD Pipeline / post-deploy-checks (push) Successful in 1m51s
Ansible / Reboot Recovery Contract / validate (push) Has been cancelled
184 lines
7.4 KiB
Python
184 lines
7.4 KiB
Python
"""
|
|
AI technology watch service.
|
|
|
|
Builds a read-only cross-domain AI technology radar from primary sources. This
|
|
wraps the existing market-watch fetcher so the broader radar uses the same
|
|
no-SDK, no-paid-API, no-production-change boundary as the Agent market watch.
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
import importlib.util
|
|
import sys
|
|
from collections import Counter
|
|
from collections.abc import Callable
|
|
from datetime import datetime, timezone
|
|
from pathlib import Path
|
|
from typing import Any
|
|
|
|
FetchSource = Callable[[str, int], Any]
|
|
|
|
_AGENT_MARKET_WATCH_MODULE = None
|
|
|
|
|
|
def run_ai_technology_watch(
|
|
registry: dict[str, Any],
|
|
*,
|
|
registry_path: str,
|
|
mode: str = "live",
|
|
previous_report: dict[str, Any] | None = None,
|
|
timeout_seconds: int = 12,
|
|
fetcher: FetchSource | None = None,
|
|
generated_at: str | None = None,
|
|
) -> dict[str, Any]:
|
|
"""Build a broad read-only AI technology watch report."""
|
|
run_agent_market_watch = _load_agent_market_watch_runner()
|
|
agent_previous = _to_agent_previous_report(previous_report or {})
|
|
raw_report = run_agent_market_watch(
|
|
registry,
|
|
registry_path=registry_path,
|
|
mode=mode,
|
|
previous_report=agent_previous,
|
|
timeout_seconds=timeout_seconds,
|
|
fetcher=fetcher,
|
|
generated_at=generated_at,
|
|
)
|
|
registry_by_id = {
|
|
str(item.get("candidate_id", "")).strip(): item
|
|
for item in registry.get("candidates") or []
|
|
if str(item.get("candidate_id", "")).strip()
|
|
}
|
|
technologies = [
|
|
_technology_row(raw_candidate, registry_by_id.get(raw_candidate["candidate_id"], {}))
|
|
for raw_candidate in raw_report.get("candidates") or []
|
|
]
|
|
area_counts = Counter(row["technology_area"] for row in technologies)
|
|
priority_counts = Counter(row["evaluation_priority"] for row in technologies)
|
|
changed = [row for row in technologies if row["changed"]]
|
|
|
|
return {
|
|
"schema_version": "ai_technology_watch_report_v1",
|
|
"generated_at": generated_at or datetime.now(timezone.utc).isoformat(),
|
|
"mode": mode,
|
|
"registry": {
|
|
"path": registry_path,
|
|
"schema_version": str(registry.get("schema_version", "")),
|
|
"updated_at": str(registry.get("updated_at", "")),
|
|
},
|
|
"cadence": dict(registry.get("cadence") or {}),
|
|
"policy": _policy(registry),
|
|
"summary": {
|
|
"technology_count": len(technologies),
|
|
"technology_area_count": len(area_counts),
|
|
"source_count": raw_report["summary"]["source_count"],
|
|
"changed_technologies": len(changed),
|
|
"watch_only_technologies": len(technologies) - len(changed),
|
|
"review_queue_count": len(changed),
|
|
"source_failure_count": raw_report["summary"]["failure_count"],
|
|
"high_priority_count": priority_counts.get("p0", 0)
|
|
+ priority_counts.get("p1", 0),
|
|
},
|
|
"technology_area_counts": dict(sorted(area_counts.items())),
|
|
"technologies": technologies,
|
|
"review_queue": [_review_queue_item(row) for row in changed],
|
|
"new_technology_discovery": raw_report.get("new_candidate_discovery") or [],
|
|
"failures": raw_report.get("failures") or [],
|
|
}
|
|
|
|
|
|
def _load_agent_market_watch_runner() -> Any:
|
|
global _AGENT_MARKET_WATCH_MODULE
|
|
if _AGENT_MARKET_WATCH_MODULE is not None:
|
|
return _AGENT_MARKET_WATCH_MODULE.run_agent_market_watch
|
|
|
|
module_name = "awoooi_agent_market_watch_service_for_ai_technology"
|
|
service_path = Path(__file__).with_name("agent_market_watch.py")
|
|
spec = importlib.util.spec_from_file_location(module_name, service_path)
|
|
if spec is None or spec.loader is None:
|
|
raise RuntimeError(f"cannot load agent market watch service from {service_path}")
|
|
module = importlib.util.module_from_spec(spec)
|
|
sys.modules[module_name] = module
|
|
spec.loader.exec_module(module)
|
|
_AGENT_MARKET_WATCH_MODULE = module
|
|
return module.run_agent_market_watch
|
|
|
|
|
|
def _policy(registry: dict[str, Any]) -> dict[str, Any]:
|
|
policy = dict(registry.get("policy") or {})
|
|
policy.setdefault("read_only", True)
|
|
policy.setdefault("sdk_installation_approved", False)
|
|
policy.setdefault("paid_api_calls_approved", False)
|
|
policy.setdefault("production_routing_approved", False)
|
|
policy.setdefault("workflow_modification_approved", False)
|
|
policy.setdefault("telegram_send_approved", False)
|
|
policy.setdefault("model_provider_switch_approved", False)
|
|
policy.setdefault("host_write_approved", False)
|
|
return policy
|
|
|
|
|
|
def _technology_row(raw_candidate: dict[str, Any], registry_candidate: dict[str, Any]) -> dict[str, Any]:
|
|
return {
|
|
"technology_id": raw_candidate["candidate_id"],
|
|
"display_name": raw_candidate["display_name"],
|
|
"technology_area": str(registry_candidate.get("technology_area") or "uncategorized"),
|
|
"integration_surface": str(registry_candidate.get("integration_surface") or "watch_only"),
|
|
"awoooi_role": str(registry_candidate.get("awoooi_role") or raw_candidate.get("recommended_role") or ""),
|
|
"evaluation_priority": raw_candidate.get("evaluation_priority") or "watch",
|
|
"requires_cost_approval": bool(raw_candidate.get("requires_cost_approval")),
|
|
"requires_dependency_approval": bool(raw_candidate.get("requires_dependency_approval")),
|
|
"requires_security_review": bool(registry_candidate.get("requires_security_review", True)),
|
|
"sources": raw_candidate.get("sources") or [],
|
|
"changed": bool(raw_candidate.get("changed")),
|
|
"decision": raw_candidate.get("decision"),
|
|
"recommended_actions": _recommended_actions(raw_candidate, registry_candidate),
|
|
}
|
|
|
|
|
|
def _recommended_actions(
|
|
raw_candidate: dict[str, Any],
|
|
registry_candidate: dict[str, Any],
|
|
) -> list[str]:
|
|
if raw_candidate.get("changed"):
|
|
return [
|
|
"refresh_ai_technology_scorecard",
|
|
"classify_business_applicability",
|
|
"prepare_no_install_integration_note",
|
|
"route_high_risk_items_to_human_review",
|
|
]
|
|
if any(source.get("error") for source in raw_candidate.get("sources") or []):
|
|
return ["retry_primary_source_fetch", "keep_current_runtime_status"]
|
|
actions = list(registry_candidate.get("steady_state_actions") or [])
|
|
return actions or ["keep_watch_only_status"]
|
|
|
|
|
|
def _review_queue_item(row: dict[str, Any]) -> dict[str, Any]:
|
|
return {
|
|
"technology_id": row["technology_id"],
|
|
"technology_area": row["technology_area"],
|
|
"reason": "primary_source_version_or_content_changed",
|
|
"required_next_gate": "scorecard_then_sandbox_or_replay_plan",
|
|
"requires_cost_approval": row["requires_cost_approval"],
|
|
"requires_dependency_approval": row["requires_dependency_approval"],
|
|
"requires_security_review": row["requires_security_review"],
|
|
}
|
|
|
|
|
|
def _to_agent_previous_report(report: dict[str, Any]) -> dict[str, Any] | None:
|
|
if not report:
|
|
return None
|
|
if report.get("schema_version") == "agent_market_watch_report_v1":
|
|
return report
|
|
if report.get("schema_version") != "ai_technology_watch_report_v1":
|
|
return None
|
|
return {
|
|
"schema_version": "agent_market_watch_report_v1",
|
|
"candidates": [
|
|
{
|
|
"candidate_id": row.get("technology_id"),
|
|
"sources": row.get("sources") or [],
|
|
}
|
|
for row in report.get("technologies") or []
|
|
if row.get("technology_id")
|
|
],
|
|
}
|