Skip to content

Commit b97af26

Browse files
committed
docs: add PDF-ready technical report for RL submission
- docs/rl_technical_report.md — full report source (Mermaid diagrams, math formulations, results tables, ethical considerations) - docs/rl_technical_report.html — print-to-PDF ready, styled with cover page, syntax-highlighted code blocks, architecture diagram - docs/generate_pdf.py — regenerates HTML from markdown (no deps) To produce the final PDF: open rl_technical_report.html in Chrome → Cmd+P → Save as PDF Made-with: Cursor
1 parent 00f9562 commit b97af26

2 files changed

Lines changed: 846 additions & 0 deletions

File tree

docs/generate_pdf.py

Lines changed: 341 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,341 @@
1+
#!/usr/bin/env python3
2+
"""Convert rl_technical_report.md → rl_technical_report.html (print-to-PDF ready).
3+
4+
Usage:
5+
python3 docs/generate_pdf.py
6+
# Then open docs/rl_technical_report.html in Chrome
7+
# and press Cmd+P → Destination: Save as PDF → Save
8+
"""
9+
from __future__ import annotations
10+
import re
11+
import sys
12+
from pathlib import Path
13+
14+
SRC = Path(__file__).parent / "rl_technical_report.md"
15+
OUT = Path(__file__).parent / "rl_technical_report.html"
16+
17+
18+
def md_to_html(md: str) -> str:
19+
"""Minimal Markdown → HTML converter (no deps)."""
20+
lines = md.split("\n")
21+
html_lines: list[str] = []
22+
in_code = False
23+
in_table = False
24+
in_list = False
25+
26+
def inline(text: str) -> str:
27+
# Bold
28+
text = re.sub(r"\*\*(.+?)\*\*", r"<strong>\1</strong>", text)
29+
# Italic
30+
text = re.sub(r"\*(.+?)\*", r"<em>\1</em>", text)
31+
# Inline code
32+
text = re.sub(r"`([^`]+)`", r"<code>\1</code>", text)
33+
# Links
34+
text = re.sub(r"\[([^\]]+)\]\(([^)]+)\)", r'<a href="\2">\1</a>', text)
35+
return text
36+
37+
i = 0
38+
while i < len(lines):
39+
line = lines[i]
40+
41+
# Fenced code block
42+
if line.startswith("```"):
43+
if in_code:
44+
html_lines.append("</code></pre>")
45+
in_code = False
46+
else:
47+
lang = line[3:].strip() or ""
48+
cls = f' class="language-{lang}"' if lang else ""
49+
html_lines.append(f"<pre><code{cls}>")
50+
in_code = True
51+
i += 1
52+
continue
53+
54+
if in_code:
55+
html_lines.append(line.replace("&", "&amp;").replace("<", "&lt;").replace(">", "&gt;"))
56+
i += 1
57+
continue
58+
59+
# Tables
60+
if "|" in line and line.strip().startswith("|"):
61+
if not in_table:
62+
html_lines.append("<table>")
63+
in_table = True
64+
cells = [c.strip() for c in line.strip().strip("|").split("|")]
65+
html_lines.append("<thead><tr>" + "".join(f"<th>{inline(c)}</th>" for c in cells) + "</tr></thead><tbody>")
66+
i += 1
67+
# skip separator row
68+
if i < len(lines) and re.match(r"[\|\s\-:]+$", lines[i]):
69+
i += 1
70+
continue
71+
else:
72+
cells = [c.strip() for c in line.strip().strip("|").split("|")]
73+
html_lines.append("<tr>" + "".join(f"<td>{inline(c)}</td>" for c in cells) + "</tr>")
74+
i += 1
75+
continue
76+
elif in_table:
77+
html_lines.append("</tbody></table>")
78+
in_table = False
79+
80+
# Headings
81+
if line.startswith("### "):
82+
html_lines.append(f"<h3>{inline(line[4:])}</h3>")
83+
elif line.startswith("## "):
84+
html_lines.append(f"<h2>{inline(line[3:])}</h2>")
85+
elif line.startswith("# "):
86+
html_lines.append(f"<h1>{inline(line[2:])}</h1>")
87+
# Horizontal rule
88+
elif line.strip() in ("---", "***", "___"):
89+
html_lines.append("<hr>")
90+
# Unordered list
91+
elif re.match(r"^(\d+)\. ", line):
92+
if not in_list:
93+
html_lines.append("<ol>")
94+
in_list = "ol"
95+
html_lines.append(f"<li>{inline(re.sub(r'^\d+\. ', '', line))}</li>")
96+
elif line.startswith("- ") or line.startswith("* "):
97+
if not in_list:
98+
html_lines.append("<ul>")
99+
in_list = "ul"
100+
html_lines.append(f"<li>{inline(line[2:])}</li>")
101+
else:
102+
if in_list:
103+
html_lines.append(f"</{in_list}>")
104+
in_list = False
105+
if line.strip() == "":
106+
html_lines.append("<br>")
107+
else:
108+
html_lines.append(f"<p>{inline(line)}</p>")
109+
110+
i += 1
111+
112+
if in_table:
113+
html_lines.append("</tbody></table>")
114+
if in_list:
115+
html_lines.append(f"</{in_list}>")
116+
if in_code:
117+
html_lines.append("</code></pre>")
118+
119+
return "\n".join(html_lines)
120+
121+
122+
CSS = """
123+
* { box-sizing: border-box; margin: 0; padding: 0; }
124+
body {
125+
font-family: 'Georgia', 'Times New Roman', serif;
126+
font-size: 11pt;
127+
line-height: 1.65;
128+
color: #1a1a1a;
129+
max-width: 860px;
130+
margin: 0 auto;
131+
padding: 48px 56px;
132+
background: #fff;
133+
}
134+
h1 {
135+
font-size: 22pt;
136+
font-weight: 700;
137+
margin-bottom: 4px;
138+
border-bottom: 3px solid #1a1a1a;
139+
padding-bottom: 10px;
140+
margin-top: 20px;
141+
}
142+
h2 {
143+
font-size: 15pt;
144+
font-weight: 700;
145+
margin-top: 32px;
146+
margin-bottom: 10px;
147+
border-bottom: 1.5px solid #888;
148+
padding-bottom: 4px;
149+
color: #111;
150+
}
151+
h3 {
152+
font-size: 12pt;
153+
font-weight: 700;
154+
margin-top: 20px;
155+
margin-bottom: 8px;
156+
color: #222;
157+
}
158+
p { margin-bottom: 10px; }
159+
br { display: block; margin: 4px 0; }
160+
hr {
161+
border: none;
162+
border-top: 1px solid #ccc;
163+
margin: 28px 0;
164+
}
165+
pre {
166+
background: #f4f4f4;
167+
border: 1px solid #ddd;
168+
border-left: 4px solid #2563eb;
169+
padding: 14px 16px;
170+
font-family: 'Menlo', 'Courier New', monospace;
171+
font-size: 9.5pt;
172+
line-height: 1.5;
173+
overflow-x: auto;
174+
margin: 14px 0;
175+
border-radius: 4px;
176+
white-space: pre-wrap;
177+
}
178+
code {
179+
font-family: 'Menlo', 'Courier New', monospace;
180+
font-size: 9.5pt;
181+
background: #f0f0f0;
182+
padding: 1px 4px;
183+
border-radius: 3px;
184+
}
185+
pre code {
186+
background: none;
187+
padding: 0;
188+
font-size: inherit;
189+
}
190+
table {
191+
width: 100%;
192+
border-collapse: collapse;
193+
margin: 16px 0;
194+
font-size: 10pt;
195+
}
196+
th {
197+
background: #1a1a1a;
198+
color: #fff;
199+
text-align: left;
200+
padding: 8px 12px;
201+
font-weight: 600;
202+
}
203+
td {
204+
padding: 7px 12px;
205+
border-bottom: 1px solid #e0e0e0;
206+
}
207+
tr:nth-child(even) td { background: #f9f9f9; }
208+
ul, ol {
209+
margin: 10px 0 10px 24px;
210+
}
211+
li { margin-bottom: 4px; }
212+
a { color: #2563eb; text-decoration: none; }
213+
strong { font-weight: 700; }
214+
em { font-style: italic; }
215+
216+
/* Cover block */
217+
.cover {
218+
border: 2px solid #1a1a1a;
219+
padding: 32px 40px;
220+
margin-bottom: 40px;
221+
background: #fafafa;
222+
}
223+
.cover h1 { border: none; font-size: 20pt; margin: 0 0 8px 0; }
224+
.cover .subtitle { font-size: 13pt; color: #444; margin-bottom: 20px; }
225+
.cover table { margin: 0; font-size: 10.5pt; }
226+
.cover th { background: #444; }
227+
228+
/* Print */
229+
@media print {
230+
body { padding: 0; max-width: 100%; }
231+
pre { page-break-inside: avoid; }
232+
h2 { page-break-before: auto; }
233+
table { page-break-inside: avoid; }
234+
}
235+
"""
236+
237+
COVER_HTML = """
238+
<div class="cover">
239+
<h1>Reinforcement Learning for Adaptive Codebase Analysis</h1>
240+
<div class="subtitle">Technical Report &mdash; saar RL Layer</div>
241+
<table>
242+
<tr><td><strong>Author</strong></td><td>Devanshu</td></tr>
243+
<tr><td><strong>Project</strong></td><td>saar &mdash; Codebase DNA extractor</td></tr>
244+
<tr><td><strong>GitHub</strong></td><td><a href="https://github.com/OpenCodeIntel/saar">github.com/OpenCodeIntel/saar</a></td></tr>
245+
<tr><td><strong>Course</strong></td><td>Reinforcement Learning for Agentic AI Systems</td></tr>
246+
<tr><td><strong>Date</strong></td><td>April 2026</td></tr>
247+
</table>
248+
</div>
249+
"""
250+
251+
ARCH_DIAGRAM_HTML = """
252+
<div style="background:#f4f4f4;border:1px solid #ddd;border-left:4px solid #2563eb;
253+
padding:18px 20px;margin:14px 0;border-radius:4px;font-family:'Menlo','Courier New',monospace;
254+
font-size:9pt;line-height:1.7;white-space:pre;">
255+
saar extract . --rl
256+
257+
258+
DNAExtractor ──► CodebaseDNA
259+
260+
261+
StateEncoder (20-D float32)
262+
[lang mix | framework flags | scale | structural | tribal]
263+
264+
265+
┌─────────────────────────────────┐
266+
│ EnsembleAgent │
267+
│ Thompson Sampling │
268+
│ Beta(α,β) per sub-agent │
269+
└──────┬──────────────┬───────────┘
270+
│ │
271+
UCBBandit REINFORCEAgent
272+
6-context 20→32→8 MLP
273+
UCB1 ReLU + Softmax
274+
│ │
275+
└──────┬───────┘
276+
action: profile 0–7
277+
278+
PROFILES[action]
279+
(depth multipliers)
280+
281+
282+
RewardEngine
283+
section_coverage × multipliers
284+
+ line_efficiency
285+
+ diversity × multipliers
286+
+ explicit_feedback
287+
288+
289+
reward ∈ [-1, 1]
290+
291+
Online Update (both layers)
292+
293+
PolicyStore ~/.saar/rl/
294+
</div>
295+
"""
296+
297+
298+
def main() -> None:
299+
md = SRC.read_text(encoding="utf-8")
300+
301+
# Remove the title block (we replace with styled cover)
302+
md = re.sub(r"^# Reinforcement Learning.*?\n---\n", "", md, flags=re.DOTALL)
303+
304+
# Replace the mermaid code block with our ASCII diagram
305+
md = re.sub(
306+
r"```mermaid\n.*?```",
307+
"__ARCH_DIAGRAM__",
308+
md,
309+
flags=re.DOTALL,
310+
)
311+
312+
body_html = md_to_html(md)
313+
body_html = body_html.replace("<p>__ARCH_DIAGRAM__</p>", ARCH_DIAGRAM_HTML)
314+
315+
html = f"""<!DOCTYPE html>
316+
<html lang="en">
317+
<head>
318+
<meta charset="UTF-8">
319+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
320+
<title>RL Technical Report — saar</title>
321+
<style>
322+
{CSS}
323+
</style>
324+
</head>
325+
<body>
326+
{COVER_HTML}
327+
{body_html}
328+
</body>
329+
</html>"""
330+
331+
OUT.write_text(html, encoding="utf-8")
332+
print(f"✓ Generated: {OUT}")
333+
print()
334+
print(" → Open in Chrome and press Cmd+P")
335+
print(" → Destination: Save as PDF")
336+
print(" → Layout: Portrait | Margins: Default | Background graphics: ON")
337+
print(" → Save as: rl_technical_report.pdf")
338+
339+
340+
if __name__ == "__main__":
341+
main()

0 commit comments

Comments
 (0)