Files
physicalCombinatorics/src/physcom_web/templates/pipeline/run.html
Andrew Simonson 434df718d7 close guardrail gaps and fix the scoring pipeline top to bottom
Constraint resolver: aggregate mass/footprint across a combo instead of
pairwise-only checks, treat medium/atmosphere as agreement not supply/demand,
reduce multi-provider checks by best/sum instead of AND-ing every provider,
fail closed on unrecognized mutex values, add a propulsion-viability
(thrust-to-weight) rule. Seed data updated to match (nuclear/solar-sail
footprint floors, water-medium exclusions, explicit ground/gravity providers).

Domain metric units were stored globally per metric name instead of
per-domain, silently corrupting cost_efficiency for every domain but the
first one seeded — fixed with a schema migration.

Stub estimator's cost_efficiency/safety/availability/reliability were a
backwards formula and flat constants; replaced with heuristics grounded in
each entity's thrust_profile/energy_form/infrastructure.

LLM estimate_physics() now receives each metric's unit and expected range
instead of a bare name, fixing wildly miscalibrated estimates traced back to
the prompt's own hardcoded example anchoring the model to the wrong order of
magnitude. Sharpened the safety-estimation and plausibility-review prompts.
Deduped provider parsing logic into llm/parsing.py.

Web pipeline form can now pick an LLM provider per run instead of only via
server env var.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-26 00:13:16 -05:00

172 lines
6.8 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
{% extends "base.html" %}
{% block title %}Run Pipeline — PhysCom{% endblock %}
{% block content %}
<h1>Run Pipeline</h1>
<div class="card">
<form method="post" action="{{ url_for('pipeline.pipeline_run') }}">
<div class="form-group">
<label for="domain">Domain</label>
<p class="form-hint">The evaluation context that defines which metrics matter and how they're weighted.</p>
<select name="domain" id="domain" required>
<option value="">— select —</option>
{% for d in domains %}
<option value="{{ d.name }}">{{ d.name }} — {{ d.description }}</option>
{% endfor %}
</select>
</div>
<fieldset>
<legend>Passes</legend>
<p class="form-hint">Each pass progressively filters and enriches combinations. Later passes depend on earlier ones.</p>
<div class="checkbox-col">
<label>
<input type="checkbox" name="passes" value="1" checked>
<strong>Pass 1 — Constraint Resolution</strong>
<span class="form-hint">Checks requires/provides/excludes compatibility between entities. Blocks impossible combinations.</span>
</label>
<label>
<input type="checkbox" name="passes" value="2" checked>
<strong>Pass 2 — Physics Estimation</strong>
<span class="form-hint">Estimates raw metric values (speed, cost, etc.) using heuristics or an LLM. Without an LLM provider, uses a force/mass stub.</span>
</label>
<label>
<input type="checkbox" name="passes" value="3" checked>
<strong>Pass 3 — Scoring &amp; Ranking</strong>
<span class="form-hint">Normalizes estimates against domain bounds and computes a weighted geometric mean composite score.</span>
</label>
<label>
<input type="checkbox" name="passes" value="4">
<strong>Pass 4 — LLM Review</strong>
<span class="form-hint">Sends top combinations to an LLM for a plausibility and novelty assessment. Requires an LLM provider to be configured.</span>
</label>
<label>
<input type="checkbox" name="passes" value="5">
<strong>Pass 5 — Human Review</strong>
<span class="form-hint">Marks results as ready for human review on the Results page.</span>
</label>
</div>
</fieldset>
<fieldset>
<legend>LLM Provider</legend>
<p class="form-hint">Used for Pass 2 estimation and Pass 4 review. Leave on "server default" to use whatever LLM_PROVIDER is configured in the server environment (or the physics stub if none).</p>
<div class="form-group">
<select name="llm_provider" id="llm_provider">
<option value="">— server default —</option>
<option value="stub">Stub (fast, no LLM)</option>
<option value="ollama">Ollama (local)</option>
<option value="gemini">Gemini (cloud, requires server-side GEMINI_API_KEY)</option>
</select>
</div>
<div class="form-group">
<label for="llm_model">Model</label>
<p class="form-hint">Leave blank to use the provider's default model.</p>
<input type="text" name="llm_model" id="llm_model" placeholder="e.g. qwen2.5:7b or gemini-2.0-flash">
</div>
<div class="form-group">
<label for="llm_host">Ollama host</label>
<p class="form-hint">Only used when Ollama is selected. Leave blank for http://localhost:11434.</p>
<input type="text" name="llm_host" id="llm_host" placeholder="http://localhost:11434">
</div>
</fieldset>
<div class="form-group">
<label for="threshold">Score Threshold</label>
<p class="form-hint">Minimum composite score (01) for a combination to pass scoring. Lower values keep more results; higher values are more selective.</p>
<input type="number" name="threshold" id="threshold" value="0.1" step="0.01" min="0" max="1">
</div>
<fieldset>
<legend>Dimensions</legend>
<p class="form-hint">Which entity dimensions to combine. The pipeline generates the Cartesian product of all entities in the selected dimensions.</p>
<div class="checkbox-row">
{% for d in dimensions %}
<label>
<input type="checkbox" name="dimensions" value="{{ d.name }}" checked>
{{ d.name }}
</label>
{% endfor %}
</div>
</fieldset>
<div class="form-actions">
<button type="submit" class="btn btn-primary">Run Pipeline</button>
</div>
</form>
</div>
{% set active_runs = runs | selectattr('status', 'in', ['pending', 'running', 'rate_limited']) | list %}
{% if active_runs %}
<h2>Active Runs</h2>
{% for run in active_runs %}
<div class="card"
hx-get="{{ url_for('pipeline.run_status', run_id=run.id) }}"
hx-trigger="every 2s"
hx-swap="innerHTML">
{% include "pipeline/_run_status.html" %}
</div>
{% endfor %}
{% endif %}
{% if runs %}
<h2>Run History</h2>
<table>
<thead>
<tr>
<th>ID</th>
<th>Domain</th>
<th>Status</th>
<th>Total</th>
<th>P1 Checked</th>
<th>P1 Failed</th>
<th>P2 Estimated</th>
<th>P3 Scored</th>
<th>P4 Reviewed</th>
<th>Started</th>
</tr>
</thead>
<tbody>
{% for run in runs %}
{% set blocked = (run.combos_pass1 or 0) - (run.combos_pass2 or 0) if (run.combos_pass2 or 0) > 0 and (run.combos_pass1 or 0) > (run.combos_pass2 or 0) else 0 %}
<tr>
<td>{{ run.id }}</td>
<td>{{ run.domain_name }}</td>
<td><span class="badge badge-{{ run.status }}">{{ run.status }}</span></td>
<td>{{ run.total_combos or '—' }}</td>
<td>{{ run.combos_pass1 or '—' }}</td>
<td>{% if blocked %}<span class="badge badge-p1_fail">{{ blocked }}</span>{% else %}—{% endif %}</td>
<td>{{ run.combos_pass2 or '—' }}</td>
<td>{{ run.combos_pass3 or '—' }}</td>
<td>{{ run.combos_pass4 or '—' }}</td>
<td>{{ run.started_at or run.created_at }}</td>
</tr>
{% endfor %}
</tbody>
</table>
{% endif %}
{% if summaries.values()|select|list %}
<h2>Domain Summaries</h2>
{% for d in domains %}
{% set s = summaries[d.name] %}
{% if s %}
<div class="card">
<h3>{{ d.name }} <span class="subtitle">{{ d.description }}</span></h3>
<dl class="summary-dl">
<dt>Results</dt><dd>{{ s.total_results }} scored combinations</dd>
<dt>Failed</dt><dd>{{ s.failed }} combinations</dd>
<dt>Score range</dt><dd class="score-cell">{{ "%.4f"|format(s.min_score) }} — {{ "%.4f"|format(s.max_score) }}</dd>
<dt>Avg score</dt><dd class="score-cell">{{ "%.4f"|format(s.avg_score) }}</dd>
<dt>Last pass</dt><dd>{{ s.last_pass }}</dd>
</dl>
<div style="margin-top:0.5rem">
<a href="{{ url_for('results.results_domain', domain_name=d.name) }}" class="btn btn-sm">View results</a>
</div>
</div>
{% endif %}
{% endfor %}
{% endif %}
{% endblock %}