File size: 7,062 Bytes
a9e46a4
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
from __future__ import annotations

from pathlib import Path
from typing import Any
import json

from .schemas import ExperimentReport, Hypothesis, Insight, PipelineConfiguration, ValidationReport


class SharedKnowledgeSpace:
    """File-based bridge between V1 and T1."""

    def __init__(self, root: str | Path):
        self.root = Path(root)
        self.reports_dir = self.root / "experiment_reports"
        self.insights_dir = self.root / "insights"
        self.configs_dir = self.root / "pipeline_configs"
        self.hypotheses_dir = self.root / "hypotheses"
        self.validation_reports_dir = self.root / "validation_reports"
        self.summary_stats_file = self.root / "summary_statistics.json"
        for d in (
            self.reports_dir,
            self.insights_dir,
            self.configs_dir,
            self.hypotheses_dir,
            self.validation_reports_dir,
        ):
            d.mkdir(parents=True, exist_ok=True)

    @staticmethod
    def _write_json(path: Path, payload: dict[str, Any]) -> None:
        path.write_text(json.dumps(payload, indent=2, ensure_ascii=True), encoding="utf-8")

    @staticmethod
    def _read_json(path: Path) -> dict[str, Any]:
        return json.loads(path.read_text(encoding="utf-8"))

    def save_report(self, report: ExperimentReport) -> Path:
        fp = self.reports_dir / f"{report.run_id}.json"
        self._write_json(fp, report.to_dict())
        return fp

    def save_insight(self, insight: Insight) -> Path:
        fp = self.insights_dir / f"{insight.insight_id}.json"
        self._write_json(fp, insight.to_dict())
        return fp

    def save_pipeline_config(self, config: PipelineConfiguration) -> Path:
        fp = self.configs_dir / f"{config.config_id}.json"
        self._write_json(fp, config.to_dict())
        return fp

    def save_hypothesis(self, hypothesis: Hypothesis) -> Path:
        fp = self.hypotheses_dir / f"{hypothesis.hypothesis_id}.json"
        self._write_json(fp, hypothesis.to_dict())
        return fp

    def save_validation_report(self, report: ValidationReport) -> Path:
        fp = self.validation_reports_dir / f"{report.validation_id}.json"
        self._write_json(fp, report.to_dict())
        return fp

    def list_reports(self) -> list[dict[str, Any]]:
        items: list[dict[str, Any]] = []
        for fp in sorted(self.reports_dir.glob("*.json")):
            items.append(self._read_json(fp))
        return items

    def list_insights(self) -> list[dict[str, Any]]:
        items: list[dict[str, Any]] = []
        for fp in sorted(self.insights_dir.glob("*.json")):
            items.append(self._read_json(fp))
        return items

    def list_configs(self) -> list[dict[str, Any]]:
        items: list[dict[str, Any]] = []
        for fp in sorted(self.configs_dir.glob("*.json")):
            items.append(self._read_json(fp))
        return items

    def list_hypotheses(self) -> list[dict[str, Any]]:
        items: list[dict[str, Any]] = []
        for fp in sorted(self.hypotheses_dir.glob("*.json")):
            items.append(self._read_json(fp))
        return items

    def list_validation_reports(self) -> list[dict[str, Any]]:
        items: list[dict[str, Any]] = []
        for fp in sorted(self.validation_reports_dir.glob("*.json")):
            items.append(self._read_json(fp))
        return items

    def latest_config_for_task(self, task_scope: str) -> dict[str, Any] | None:
        matched = [cfg for cfg in self.list_configs() if cfg.get("task_scope") == task_scope]
        return matched[-1] if matched else None

    def get_hypothesis_context(self, domain: str, top_k: int = 5) -> list[dict[str, Any]]:
        domain_l = domain.strip().lower()
        hypotheses = self.list_hypotheses()
        validations = self.list_validation_reports()
        by_hid = {}
        for vr in validations:
            hid = vr.get("hypothesis_id", "")
            if not hid:
                continue
            by_hid.setdefault(hid, []).append(vr)
        scored: list[dict[str, Any]] = []
        for h in hypotheses:
            h_domain = str(h.get("domain", "")).lower()
            if domain_l and domain_l not in h_domain and domain_l not in " ".join(h.get("tags", [])).lower():
                continue
            reports = by_hid.get(h.get("hypothesis_id", ""), [])
            if reports:
                avg_score = sum(float(r.get("score", 0.0)) for r in reports) / len(reports)
                avg_conf = sum(float(r.get("confidence", 0.0)) for r in reports) / len(reports)
            else:
                avg_score = 0.0
                avg_conf = 0.0
            scored.append(
                {
                    "hypothesis": h,
                    "validation_reports": reports,
                    "avg_score": avg_score,
                    "avg_confidence": avg_conf,
                }
            )
        scored.sort(key=lambda x: (x["avg_score"], x["avg_confidence"]), reverse=True)
        return scored[: max(0, top_k)]

    def update_summary_statistics(self) -> dict[str, Any]:
        validations = self.list_validation_reports()
        tag_stats: dict[str, dict[str, float]] = {}
        failure_reason_counter: dict[str, int] = {}
        success_scores: list[float] = []

        hypotheses = {h.get("hypothesis_id", ""): h for h in self.list_hypotheses()}

        for r in validations:
            status = r.get("status", "inconclusive")
            score = float(r.get("score", 0.0))
            if status == "success":
                success_scores.append(score)
            reason = str(r.get("failure_reason", "")).strip()
            if status == "failed" and reason:
                failure_reason_counter[reason] = failure_reason_counter.get(reason, 0) + 1

            hid = r.get("hypothesis_id", "")
            h = hypotheses.get(hid, {})
            for tag in h.get("tags", []):
                s = tag_stats.setdefault(tag, {"count": 0.0, "success": 0.0, "score_sum": 0.0})
                s["count"] += 1
                if status == "success":
                    s["success"] += 1
                s["score_sum"] += score

        tag_summary = {}
        for tag, s in tag_stats.items():
            count = max(1.0, s["count"])
            tag_summary[tag] = {
                "count": int(s["count"]),
                "success_rate": s["success"] / count,
                "avg_score": s["score_sum"] / count,
            }

        payload = {
            "total_validation_reports": len(validations),
            "overall_avg_success_score": (sum(success_scores) / len(success_scores)) if success_scores else 0.0,
            "tag_summary": tag_summary,
            "failure_reason_counter": failure_reason_counter,
        }
        self._write_json(self.summary_stats_file, payload)
        return payload

    def get_summary_statistics(self) -> dict[str, Any]:
        if not self.summary_stats_file.exists():
            return self.update_summary_statistics()
        return self._read_json(self.summary_stats_file)