feat(06-02): create exporter module for markdown export
- export_discussion() formats discussion with rounds and messages - export_project() creates full project export with header - _format_consensus() helper for consensus section formatting - Follows SPEC.md markdown format
This commit is contained in:
parent
edb4ab5593
commit
152d6173d6
1 changed files with 117 additions and 0 deletions
117
src/moai/core/exporter.py
Normal file
117
src/moai/core/exporter.py
Normal file
|
|
@ -0,0 +1,117 @@
|
|||
"""Markdown export functionality for MoAI discussions and projects.
|
||||
|
||||
Exports discussions and projects to shareable markdown documents following
|
||||
the SPEC format with sections for initial responses, rounds, and consensus.
|
||||
"""
|
||||
|
||||
from datetime import datetime
|
||||
|
||||
from moai.core.models import Consensus, Discussion, Project, RoundType
|
||||
|
||||
|
||||
def _format_consensus(consensus: Consensus | None) -> str:
|
||||
"""Format consensus section as markdown.
|
||||
|
||||
Args:
|
||||
consensus: The Consensus object to format, or None.
|
||||
|
||||
Returns:
|
||||
Formatted markdown string for consensus, or empty string if none.
|
||||
"""
|
||||
if consensus is None:
|
||||
return ""
|
||||
|
||||
lines = ["### Consensus"]
|
||||
|
||||
if consensus.agreements:
|
||||
lines.append("**Agreements:**")
|
||||
for agreement in consensus.agreements:
|
||||
lines.append(f"- {agreement}")
|
||||
lines.append("")
|
||||
|
||||
if consensus.disagreements:
|
||||
lines.append("**Disagreements:**")
|
||||
for disagreement in consensus.disagreements:
|
||||
topic = disagreement.get("topic", "Unknown topic")
|
||||
positions = disagreement.get("positions", {})
|
||||
lines.append(f"- **{topic}:**")
|
||||
for model, position in positions.items():
|
||||
lines.append(f" - {model}: {position}")
|
||||
lines.append("")
|
||||
|
||||
return "\n".join(lines)
|
||||
|
||||
|
||||
def export_discussion(discussion: Discussion) -> str:
|
||||
"""Export a discussion as markdown.
|
||||
|
||||
Args:
|
||||
discussion: The Discussion object to export (with rounds/messages loaded).
|
||||
|
||||
Returns:
|
||||
Markdown string formatted according to SPEC.
|
||||
"""
|
||||
lines = [f"## Discussion: {discussion.question}", ""]
|
||||
|
||||
# Group messages by round
|
||||
sorted_rounds = sorted(discussion.rounds, key=lambda r: r.round_number)
|
||||
|
||||
for round_ in sorted_rounds:
|
||||
# Label round type appropriately
|
||||
if round_.type == RoundType.PARALLEL:
|
||||
lines.append("### Initial Responses (Open)")
|
||||
else:
|
||||
lines.append(f"### Round {round_.round_number}")
|
||||
lines.append("")
|
||||
|
||||
# Format each message
|
||||
sorted_messages = sorted(round_.messages, key=lambda m: m.timestamp)
|
||||
for message in sorted_messages:
|
||||
model_name = message.model.capitalize()
|
||||
lines.append(f"**{model_name}:**")
|
||||
# Quote the response content
|
||||
for content_line in message.content.split("\n"):
|
||||
lines.append(f"> {content_line}")
|
||||
lines.append("")
|
||||
|
||||
# Add consensus section if exists
|
||||
consensus_md = _format_consensus(discussion.consensus)
|
||||
if consensus_md:
|
||||
lines.append(consensus_md)
|
||||
|
||||
return "\n".join(lines)
|
||||
|
||||
|
||||
def export_project(project: Project, discussions: list[Discussion]) -> str:
|
||||
"""Export a project and its discussions as markdown.
|
||||
|
||||
Args:
|
||||
project: The Project object to export.
|
||||
discussions: List of Discussion objects (with rounds/messages loaded).
|
||||
|
||||
Returns:
|
||||
Full markdown string for the project export.
|
||||
"""
|
||||
# Header section
|
||||
date_str = datetime.now().strftime("%Y-%m-%d")
|
||||
models_str = ", ".join(m.capitalize() for m in project.models) if project.models else "None"
|
||||
|
||||
lines = [
|
||||
f"# {project.name}",
|
||||
"",
|
||||
f"**Date:** {date_str}",
|
||||
f"**Models:** {models_str}",
|
||||
f"**Discussions:** {len(discussions)}",
|
||||
"",
|
||||
"---",
|
||||
"",
|
||||
]
|
||||
|
||||
# Add each discussion
|
||||
for i, discussion in enumerate(discussions):
|
||||
if i > 0:
|
||||
lines.append("---")
|
||||
lines.append("")
|
||||
lines.append(export_discussion(discussion))
|
||||
|
||||
return "\n".join(lines)
|
||||
Loading…
Add table
Reference in a new issue