Initial commit
This commit is contained in:
123
skills/debate-orchestrator/debate_ops/mermaid.py
Normal file
123
skills/debate-orchestrator/debate_ops/mermaid.py
Normal file
@@ -0,0 +1,123 @@
|
||||
"""Generate mermaid argument graph from debate state."""
|
||||
from __future__ import annotations
|
||||
|
||||
import json
|
||||
from pathlib import Path
|
||||
from debate_ops import frontmatter
|
||||
|
||||
|
||||
def generate_graph(debate: str) -> None:
|
||||
"""Generate mermaid flowchart showing argument relationships and scores.
|
||||
|
||||
Reads argument structure from frontmatter, scores from scores.json.
|
||||
Updates or creates {debate}/argument-graph.mmd.
|
||||
"""
|
||||
debate_dir = Path.cwd() / debate
|
||||
args_dir = debate_dir / 'arguments'
|
||||
|
||||
if not args_dir.exists():
|
||||
return
|
||||
|
||||
# Load scores from scores.json
|
||||
scores_file = debate_dir / 'scores.json'
|
||||
scores_data = json.load(open(scores_file)) if scores_file.exists() else {}
|
||||
|
||||
# Collect argument data
|
||||
arguments = []
|
||||
for arg_file in sorted(args_dir.glob('*.md')):
|
||||
doc = frontmatter.load(arg_file)
|
||||
meta = doc.metadata
|
||||
arg_id = meta.get('id', arg_file.stem)
|
||||
|
||||
# Get score from scores.json instead of frontmatter
|
||||
score = scores_data.get(arg_id, {}).get('current_score', None)
|
||||
|
||||
# Get attacks and defends (expect dict format with target_id and type)
|
||||
attacks = meta.get('attacks', [])
|
||||
defends = meta.get('defends', [])
|
||||
|
||||
# Use title if available, otherwise fallback to truncated claim
|
||||
display_text = meta.get('title', meta.get('claim', 'No claim')[:50] + ('...' if len(meta.get('claim', '')) > 50 else ''))
|
||||
|
||||
arguments.append({
|
||||
'id': arg_id,
|
||||
'side': meta.get('side', 'unknown'),
|
||||
'display': display_text,
|
||||
'score': score,
|
||||
'attacks': attacks,
|
||||
'defends': defends
|
||||
})
|
||||
|
||||
if not arguments:
|
||||
return
|
||||
|
||||
# Build mermaid syntax with ELK layout for better visualization
|
||||
lines = [
|
||||
'---',
|
||||
'config:',
|
||||
' layout: elk',
|
||||
' elk:',
|
||||
' nodePlacementStrategy: NETWORK_SIMPLEX',
|
||||
'---',
|
||||
'graph TD',
|
||||
''
|
||||
]
|
||||
|
||||
# Nodes - dark fills with white text for GitHub theme compatibility
|
||||
for arg in arguments:
|
||||
score = arg['score'] if arg['score'] is not None else 0
|
||||
score_display = f"{score:.2f}" if score is not None else "—"
|
||||
|
||||
# Proposition: dark green, Opposition: dark red
|
||||
fill, stroke, border_width = (
|
||||
('#1B5E20', '#4CAF50', '3px') if arg['side'] == 'prop' and score >= 0.75
|
||||
else ('#1B5E20', '#4CAF50', '2px') if arg['side'] == 'prop'
|
||||
else ('#B71C1C', '#F44336', '3px') if score >= 0.75
|
||||
else ('#B71C1C', '#F44336', '2px')
|
||||
)
|
||||
|
||||
lines.extend([
|
||||
f' {arg["id"]}["{arg["id"]}<br/>{arg["display"]}<br/>⭐ {score_display}"]',
|
||||
f' style {arg["id"]} fill:{fill},stroke:{stroke},stroke-width:{border_width},color:#FFFFFF'
|
||||
])
|
||||
|
||||
lines.append('')
|
||||
|
||||
# Edges - track index for linkStyle coloring
|
||||
edge_index = 0
|
||||
link_styles = []
|
||||
|
||||
for arg in arguments:
|
||||
# Attacks: solid lines, orange color
|
||||
for attack in arg['attacks']:
|
||||
target_id = attack['target_id']
|
||||
attack_type = attack['type'].replace('_attack', '') if '_attack' in attack['type'] else attack['type']
|
||||
lines.append(f' {arg["id"]} -->|⚔️ {attack_type}| {target_id}')
|
||||
link_styles.append(f' linkStyle {edge_index} stroke:#ff9800,stroke-width:2px')
|
||||
edge_index += 1
|
||||
|
||||
# Defends: blue color, style varies by type
|
||||
for defend in arg['defends']:
|
||||
target_id = defend['target_id']
|
||||
defense_type = defend['type']
|
||||
|
||||
if defense_type == 'concede_and_pivot':
|
||||
# Concede and pivot: dotted line (retreat/weakness)
|
||||
emoji = '↩️'
|
||||
lines.append(f' {arg["id"]} -.->|{emoji} {defense_type}| {target_id}')
|
||||
else:
|
||||
# Reinforce/clarify: solid line (strengthening)
|
||||
emoji = '🛡️'
|
||||
lines.append(f' {arg["id"]} -->|{emoji} {defense_type}| {target_id}')
|
||||
|
||||
link_styles.append(f' linkStyle {edge_index} stroke:#2196F3,stroke-width:2px')
|
||||
edge_index += 1
|
||||
|
||||
# Add link styles at the end
|
||||
if link_styles:
|
||||
lines.append('')
|
||||
lines.extend(link_styles)
|
||||
|
||||
# Write to file
|
||||
output_file = debate_dir / 'argument-graph.mmd'
|
||||
output_file.write_text('\n'.join(lines) + '\n')
|
||||
Reference in New Issue
Block a user