1"""Markdown formatter.
2
3This module implements a Markdown formatter for synthesis results with
4structured output and citations.
5"""
6
7from __future__ import annotations
8
9from lexigram.ai.rag.synthesis.types import SynthesisResult
10
11
12class MarkdownFormatter:
13 """Markdown response formatter.
14
15 This formatter outputs synthesis results as structured Markdown with
16 headers, lists, and formatted citations.
17
18 Attributes:
19 include_title: Whether to include title header
20 include_metadata: Whether to include metadata section
21 include_quality: Whether to include quality metrics
22 citation_style: Citation style ("numeric" or "footnote")
23 """
24
25 def __init__(
26 self,
27 include_title: bool = True,
28 include_metadata: bool = True,
29 include_quality: bool = False,
30 citation_style: str = "numeric",
31 ):
32 """Initialize the markdown formatter.
33
34 Args:
35 include_title: Include title header
36 include_metadata: Include metadata section
37 include_quality: Include quality metrics
38 citation_style: Citation style
39 """
40 self.include_title = include_title
41 self.include_metadata = include_metadata
42 self.include_quality = include_quality
43 self.citation_style = citation_style
44
45 def format(self, result: SynthesisResult) -> str:
46 """Format result as Markdown.
47
48 Args:
49 result: The synthesis result
50
51 Returns:
52 Markdown output
53 """
54 parts = []
55
56 # Add title
57 if self.include_title:
58 parts.append("# Response\n")
59
60 # Add response text
61 parts.append(result.response)
62 parts.append("\n")
63
64 # Add sources/citations
65 if result.sources or result.citations:
66 parts.append("\n## Sources\n")
67
68 if result.citations:
69 for citation in result.citations:
70 num = citation.get("number", "?")
71 source = citation.get("source", "Unknown")
72 score = citation.get("score", 0.0)
73 parts.append(f"{num}. {source} (relevance: {score:.2f})\n")
74 else:
75 for i, source in enumerate(result.sources, 1):
76 parts.append(f"{i}. {source}\n")
77
78 # Add metadata
79 if self.include_metadata:
80 parts.append("\n## Metadata\n")
81 parts.append(f"- **Strategy**: {result.strategy.value}\n")
82 parts.append(f"- **Chunks Used**: {result.num_chunks_used}\n")
83 parts.append(f"- **Created**: {result.created_at.isoformat()}\n")
84
85 # Add quality metrics
86 if self.include_quality and result.quality_metrics:
87 metrics = result.quality_metrics
88 parts.append("\n## Quality Metrics\n")
89 parts.append(f"- **Faithfulness**: {metrics.faithfulness:.2f}\n")
90 parts.append(f"- **Relevance**: {metrics.relevance:.2f}\n")
91 parts.append(f"- **Coherence**: {metrics.coherence:.2f}\n")
92 parts.append(f"- **Confidence**: {metrics.confidence:.2f}\n")
93
94 if metrics.has_hallucinations:
95 parts.append(
96 f"- **Hallucinations**: {metrics.hallucination_count} detected\n",
97 )
98
99 return "".join(parts)