File size: 8,456 Bytes
f25de22
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
f988400
f25de22
 
 
5473f39
f25de22
 
 
 
 
5473f39
f25de22
 
 
 
 
 
 
 
 
 
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
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
"""Operon Adaptive Assembly Dashboard — template selection, assembly, and experience pool."""

from __future__ import annotations

from dataclasses import dataclass, field
from typing import Any

import gradio as gr


# ---------------------------------------------------------------------------
# Inline simulation types
# ---------------------------------------------------------------------------

@dataclass(frozen=True)
class TaskFingerprint:
    task_shape: str
    tool_count: int
    subtask_count: int
    required_roles: tuple[str, ...]
    tags: tuple[str, ...] = ()


@dataclass(frozen=True)
class PatternTemplate:
    template_id: str
    name: str
    topology: str
    stage_count: int
    fingerprint: TaskFingerprint
    tags: tuple[str, ...] = ()
    success_rate: float | None = None


@dataclass
class ExperienceRecord:
    stage_name: str
    signal_category: str
    intervention_kind: str
    outcome_success: bool


# ---------------------------------------------------------------------------
# Preset library
# ---------------------------------------------------------------------------

TEMPLATES = [
    PatternTemplate("t1", "Sequential Review Pipeline", "reviewer_gate", 2,
                    TaskFingerprint("sequential", 2, 2, ("writer", "reviewer")),
                    ("content", "review")),
    PatternTemplate("t2", "Enterprise Analysis Organism", "skill_organism", 3,
                    TaskFingerprint("sequential", 4, 3, ("researcher", "strategist")),
                    ("enterprise", "analysis")),
    PatternTemplate("t3", "Research Swarm", "specialist_swarm", 4,
                    TaskFingerprint("parallel", 5, 4, ("analyst", "writer")),
                    ("research",)),
    PatternTemplate("t4", "Single Worker", "single_worker", 1,
                    TaskFingerprint("sequential", 1, 1, ("worker",)),
                    ("simple",)),
]


def _jaccard(a: tuple[str, ...], b: tuple[str, ...]) -> float:
    sa, sb = set(a), set(b)
    union = sa | sb
    return len(sa & sb) / len(union) if union else 1.0


def _score(tmpl: PatternTemplate, fp: TaskFingerprint) -> float:
    shape = 1.0 if tmpl.fingerprint.task_shape == fp.task_shape else 0.0
    tool_prox = 1.0 / (1.0 + abs(tmpl.fingerprint.tool_count - fp.tool_count))
    sub_prox = 1.0 / (1.0 + abs(tmpl.fingerprint.subtask_count - fp.subtask_count))
    role_sim = _jaccard(tmpl.fingerprint.required_roles, fp.required_roles)
    tag_sim = _jaccard(tmpl.tags, fp.tags)
    sr = tmpl.success_rate if tmpl.success_rate is not None else 0.5
    return 0.30 * shape + 0.15 * tool_prox + 0.15 * sub_prox + 0.20 * role_sim + 0.10 * tag_sim + 0.10 * sr


# ---------------------------------------------------------------------------
# Presets
# ---------------------------------------------------------------------------

PRESETS = {
    "Enterprise Analysis": TaskFingerprint("sequential", 3, 3, ("researcher", "strategist"), ("enterprise",)),
    "Content Review": TaskFingerprint("sequential", 2, 2, ("writer", "reviewer"), ("content",)),
    "Parallel Research": TaskFingerprint("parallel", 5, 4, ("analyst", "writer"), ("research",)),
    "Simple Task": TaskFingerprint("sequential", 1, 1, ("worker",), ("simple",)),
}

EXPERIENCE_PRESETS = {
    "Fresh (no experience)": [],
    "Warm (escalate works for epistemic)": [
        ExperienceRecord("router", "epistemic", "escalate", True),
        ExperienceRecord("router", "epistemic", "escalate", True),
        ExperienceRecord("router", "epistemic", "retry", False),
    ],
    "Mixed (retry and escalate both tried)": [
        ExperienceRecord("planner", "epistemic", "escalate", True),
        ExperienceRecord("planner", "epistemic", "retry", True),
        ExperienceRecord("planner", "somatic", "halt", True),
        ExperienceRecord("router", "epistemic", "retry", False),
    ],
}


# ---------------------------------------------------------------------------
# Callbacks
# ---------------------------------------------------------------------------

def _run_selection(preset_name):
    if preset_name not in PRESETS:
        return "Select a preset."
    fp = PRESETS[preset_name]
    scored = [(t, round(_score(t, fp), 4)) for t in TEMPLATES]
    scored.sort(key=lambda x: x[1], reverse=True)
    rows = ""
    for i, (t, s) in enumerate(scored):
        bg = "#1a2a1a" if i == 0 else "transparent"
        badge = ' <span style="color:#6a6;font-size:12px">SELECTED</span>' if i == 0 else ""
        rows += f"""<tr style="background:{bg}">
            <td style="padding:10px;font-weight:{'600' if i==0 else '400'}">{t.name}{badge}</td>
            <td style="padding:10px">{t.topology}</td>
            <td style="padding:10px">{t.stage_count} stages</td>
            <td style="padding:10px;font-weight:600">{s:.4f}</td>
        </tr>"""
    fp_html = f"""<div style="margin-bottom:16px;padding:12px;background:#111;border-radius:6px;font-size:14px">
        <strong>Fingerprint:</strong> shape={fp.task_shape}, tools={fp.tool_count}, subtasks={fp.subtask_count}, roles={fp.required_roles}, tags={fp.tags}
    </div>"""
    table = f"""<table style="width:100%;border-collapse:collapse;font-size:14px">
        <thead><tr style="border-bottom:2px solid #333">
            <th style="text-align:left;padding:8px">Template</th>
            <th style="text-align:left;padding:8px">Topology</th>
            <th style="text-align:left;padding:8px">Stages</th>
            <th style="text-align:left;padding:8px">Score</th>
        </tr></thead>
        <tbody>{rows}</tbody>
    </table>"""
    return fp_html + table


def _run_experience(exp_preset):
    if exp_preset not in EXPERIENCE_PRESETS:
        return "Select a preset."
    records = EXPERIENCE_PRESETS[exp_preset]
    if not records:
        return '<p style="color:#888">Empty experience pool — watcher uses rule-based decisions only.</p>'
    rows = ""
    for r in records:
        color = "#4a4" if r.outcome_success else "#a44"
        rows += f"""<tr>
            <td style="padding:8px">{r.stage_name}</td>
            <td style="padding:8px">{r.signal_category}</td>
            <td style="padding:8px">{r.intervention_kind}</td>
            <td style="padding:8px;color:{color}">{'success' if r.outcome_success else 'failed'}</td>
        </tr>"""
    # Compute recommendation
    success_by_kind: dict[str, int] = {}
    for r in records:
        if r.outcome_success:
            success_by_kind[r.intervention_kind] = success_by_kind.get(r.intervention_kind, 0) + 1
    rec = max(success_by_kind, key=lambda k: success_by_kind[k]) if success_by_kind else "none"
    table = f"""<table style="width:100%;border-collapse:collapse;font-size:14px;margin-bottom:16px">
        <thead><tr style="border-bottom:2px solid #333">
            <th style="text-align:left;padding:8px">Stage</th>
            <th style="text-align:left;padding:8px">Signal</th>
            <th style="text-align:left;padding:8px">Intervention</th>
            <th style="text-align:left;padding:8px">Outcome</th>
        </tr></thead>
        <tbody>{rows}</tbody>
    </table>"""
    rec_html = f'<div style="padding:12px;background:#0a1a2a;border:1px solid #1a2a3a;border-radius:6px;font-size:14px"><strong style="color:#6af">Recommendation:</strong> {rec.upper()}</div>'
    return table + rec_html


# ---------------------------------------------------------------------------
# App
# ---------------------------------------------------------------------------

def build_app():
    with gr.Blocks(title="Operon Adaptive Assembly", theme=gr.themes.Base()) as demo:
        gr.Markdown("# Operon Adaptive Assembly\nTemplate selection, organism construction, and experience-driven intervention.")

        with gr.Tab("Template Selection"):
            preset = gr.Dropdown(choices=list(PRESETS.keys()), label="Task Preset", value="Enterprise Analysis")
            btn = gr.Button("Select Template")
            out = gr.HTML()
            btn.click(_run_selection, inputs=[preset], outputs=[out])

        with gr.Tab("Experience Pool"):
            exp_preset = gr.Dropdown(choices=list(EXPERIENCE_PRESETS.keys()), label="Experience Preset", value="Fresh (no experience)")
            btn2 = gr.Button("Show Pool & Recommendation")
            out2 = gr.HTML()
            btn2.click(_run_experience, inputs=[exp_preset], outputs=[out2])

    return demo


if __name__ == "__main__":
    app = build_app()
    app.launch()