deepagents-printshop 0.1.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- agents/content_editor/__init__.py +1 -0
- agents/content_editor/agent.py +279 -0
- agents/content_editor/content_reviewer.py +327 -0
- agents/content_editor/versioned_agent.py +455 -0
- agents/latex_specialist/__init__.py +1 -0
- agents/latex_specialist/agent.py +531 -0
- agents/latex_specialist/latex_analyzer.py +510 -0
- agents/latex_specialist/latex_optimizer.py +1192 -0
- agents/qa_orchestrator/__init__.py +1 -0
- agents/qa_orchestrator/agent.py +603 -0
- agents/qa_orchestrator/langgraph_workflow.py +733 -0
- agents/qa_orchestrator/pipeline_types.py +72 -0
- agents/qa_orchestrator/quality_gates.py +495 -0
- agents/qa_orchestrator/workflow_coordinator.py +139 -0
- agents/research_agent/__init__.py +1 -0
- agents/research_agent/agent.py +258 -0
- agents/research_agent/llm_report_generator.py +1023 -0
- agents/research_agent/report_generator.py +536 -0
- agents/visual_qa/__init__.py +1 -0
- agents/visual_qa/agent.py +410 -0
- deepagents_printshop-0.1.0.dist-info/METADATA +744 -0
- deepagents_printshop-0.1.0.dist-info/RECORD +37 -0
- deepagents_printshop-0.1.0.dist-info/WHEEL +4 -0
- deepagents_printshop-0.1.0.dist-info/entry_points.txt +2 -0
- deepagents_printshop-0.1.0.dist-info/licenses/LICENSE +86 -0
- tools/__init__.py +1 -0
- tools/change_tracker.py +419 -0
- tools/content_type_loader.py +171 -0
- tools/graph_generator.py +281 -0
- tools/latex_generator.py +374 -0
- tools/llm_latex_generator.py +678 -0
- tools/magazine_layout.py +462 -0
- tools/pattern_injector.py +250 -0
- tools/pattern_learner.py +477 -0
- tools/pdf_compiler.py +386 -0
- tools/version_manager.py +346 -0
- tools/visual_qa.py +799 -0
|
@@ -0,0 +1,536 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Research Report Generator using LaTeX.
|
|
3
|
+
|
|
4
|
+
This module demonstrates the LaTeX report generation capabilities
|
|
5
|
+
for the DeepAgents PrintShop research agent.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
import os
|
|
9
|
+
import sys
|
|
10
|
+
from pathlib import Path
|
|
11
|
+
from typing import Dict
|
|
12
|
+
|
|
13
|
+
# Add tools to path
|
|
14
|
+
sys.path.insert(0, str(Path(__file__).parent.parent.parent))
|
|
15
|
+
|
|
16
|
+
from tools.latex_generator import (
|
|
17
|
+
LaTeXGenerator, DocumentConfig, markdown_to_latex
|
|
18
|
+
)
|
|
19
|
+
from tools.pdf_compiler import PDFCompiler
|
|
20
|
+
from tools.content_type_loader import ContentTypeLoader
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
class ResearchReportGenerator:
|
|
24
|
+
"""Generate comprehensive LaTeX research reports."""
|
|
25
|
+
|
|
26
|
+
def __init__(self, output_dir: str = "artifacts/output"):
|
|
27
|
+
"""
|
|
28
|
+
Initialize the report generator.
|
|
29
|
+
|
|
30
|
+
Args:
|
|
31
|
+
output_dir: Directory to save generated files
|
|
32
|
+
"""
|
|
33
|
+
self.output_dir = Path(output_dir)
|
|
34
|
+
self.output_dir.mkdir(parents=True, exist_ok=True)
|
|
35
|
+
self.artifacts_dir = Path("artifacts")
|
|
36
|
+
self.content_dir = self.artifacts_dir / "sample_content"
|
|
37
|
+
self.data_dir = self.content_dir / "data"
|
|
38
|
+
self.images_dir = self.content_dir / "images"
|
|
39
|
+
|
|
40
|
+
def load_markdown_content(self, filename: str) -> str:
|
|
41
|
+
"""Load markdown content from the sample_content directory."""
|
|
42
|
+
file_path = self.content_dir / filename
|
|
43
|
+
if file_path.exists():
|
|
44
|
+
with open(file_path, 'r', encoding='utf-8') as f:
|
|
45
|
+
return f.read()
|
|
46
|
+
return ""
|
|
47
|
+
|
|
48
|
+
def load_config_from_markdown(self) -> Dict:
|
|
49
|
+
"""Load document configuration from config.md file.
|
|
50
|
+
|
|
51
|
+
Uses ContentTypeLoader to resolve the content type and extract
|
|
52
|
+
document class, font size, and paper size from the type definition.
|
|
53
|
+
Parses remaining config sections (metadata, manifest, options) from config.md.
|
|
54
|
+
"""
|
|
55
|
+
config_md = self.load_markdown_content("config.md")
|
|
56
|
+
config = {}
|
|
57
|
+
|
|
58
|
+
if not config_md:
|
|
59
|
+
return config
|
|
60
|
+
|
|
61
|
+
lines = config_md.split('\n')
|
|
62
|
+
current_section = None
|
|
63
|
+
content_lines = []
|
|
64
|
+
|
|
65
|
+
for line in lines:
|
|
66
|
+
if line.startswith('## '):
|
|
67
|
+
if current_section and content_lines:
|
|
68
|
+
config[current_section] = self._parse_config_section_simple(current_section, content_lines)
|
|
69
|
+
current_section = line.replace('## ', '').strip().lower()
|
|
70
|
+
content_lines = []
|
|
71
|
+
elif line.strip() and not line.startswith('#'):
|
|
72
|
+
content_lines.append(line)
|
|
73
|
+
|
|
74
|
+
if current_section and content_lines:
|
|
75
|
+
config[current_section] = self._parse_config_section_simple(current_section, content_lines)
|
|
76
|
+
|
|
77
|
+
# Load content type definition
|
|
78
|
+
type_id = config.get('content type', 'research_report')
|
|
79
|
+
if isinstance(type_id, str):
|
|
80
|
+
type_id = type_id.strip()
|
|
81
|
+
|
|
82
|
+
loader = ContentTypeLoader()
|
|
83
|
+
content_type = loader.load_type(type_id)
|
|
84
|
+
|
|
85
|
+
# Inject type defaults into config
|
|
86
|
+
config['document class'] = content_type.document_class
|
|
87
|
+
config['_content_type'] = content_type
|
|
88
|
+
config['_type_font_size'] = content_type.default_font_size
|
|
89
|
+
config['_type_paper_size'] = content_type.default_paper_size
|
|
90
|
+
|
|
91
|
+
# Parse project metadata into top-level fields
|
|
92
|
+
project_meta = config.get('project metadata', '')
|
|
93
|
+
if isinstance(project_meta, str):
|
|
94
|
+
for line in project_meta.split('\n'):
|
|
95
|
+
line = line.strip()
|
|
96
|
+
if line.startswith('- ') and ':' in line:
|
|
97
|
+
key, value = line[2:].split(':', 1)
|
|
98
|
+
key = key.strip().strip('*').lower()
|
|
99
|
+
value = value.strip()
|
|
100
|
+
if key == 'title':
|
|
101
|
+
config['title'] = value
|
|
102
|
+
elif key == 'authors':
|
|
103
|
+
config['authors'] = [a.strip() for a in value.split(',')]
|
|
104
|
+
|
|
105
|
+
return config
|
|
106
|
+
|
|
107
|
+
def _parse_config_section_simple(self, section_name: str, content_lines: list):
|
|
108
|
+
"""Parse configuration sections from config.md."""
|
|
109
|
+
if section_name in ['document options', 'headers and footers']:
|
|
110
|
+
result = {}
|
|
111
|
+
for line in content_lines:
|
|
112
|
+
if line.startswith('- ') and ':' in line:
|
|
113
|
+
key_value = line[2:].split(':', 1)
|
|
114
|
+
if len(key_value) == 2:
|
|
115
|
+
key = key_value[0].strip()
|
|
116
|
+
value = key_value[1].strip()
|
|
117
|
+
if value.lower() in ['true', 'false']:
|
|
118
|
+
value = value.lower() == 'true'
|
|
119
|
+
result[key] = value
|
|
120
|
+
return result
|
|
121
|
+
elif section_name == 'content manifest':
|
|
122
|
+
structure = []
|
|
123
|
+
for line in content_lines:
|
|
124
|
+
if line.strip() and line[0].isdigit():
|
|
125
|
+
parts = line.split('.', 1)
|
|
126
|
+
if len(parts) == 2:
|
|
127
|
+
section_def = parts[1].strip()
|
|
128
|
+
if ':' in section_def:
|
|
129
|
+
title, source = section_def.split(':', 1)
|
|
130
|
+
structure.append({
|
|
131
|
+
'title': title.strip(),
|
|
132
|
+
'source': source.strip(),
|
|
133
|
+
'type': 'markdown' if source.strip().endswith('.md') else 'auto'
|
|
134
|
+
})
|
|
135
|
+
else:
|
|
136
|
+
structure.append({
|
|
137
|
+
'title': section_def,
|
|
138
|
+
'source': None,
|
|
139
|
+
'type': 'auto'
|
|
140
|
+
})
|
|
141
|
+
return structure
|
|
142
|
+
else:
|
|
143
|
+
content = '\n'.join(content_lines).strip()
|
|
144
|
+
return content
|
|
145
|
+
|
|
146
|
+
def _process_markdown_with_csv(self, markdown_content: str) -> str:
|
|
147
|
+
"""Process markdown content with CSV table support using LaTeX optimizer."""
|
|
148
|
+
from agents.latex_specialist.latex_optimizer import LaTeXOptimizer
|
|
149
|
+
|
|
150
|
+
optimizer = LaTeXOptimizer()
|
|
151
|
+
# Use the LaTeX optimizer's enhanced markdown processing
|
|
152
|
+
return optimizer._markdown_to_latex_content(markdown_content)
|
|
153
|
+
|
|
154
|
+
def generate_document_from_structure(self, gen, structure: list, config_data: dict):
|
|
155
|
+
"""Generate document sections based on configurable structure."""
|
|
156
|
+
section_config = config_data.get('section configuration', {})
|
|
157
|
+
main_level = int(section_config.get('main_section_level', 1))
|
|
158
|
+
sub_level = int(section_config.get('subsection_level', 2))
|
|
159
|
+
|
|
160
|
+
for section in structure:
|
|
161
|
+
title = section['title']
|
|
162
|
+
source = section.get('source')
|
|
163
|
+
section_type = section.get('type', 'auto')
|
|
164
|
+
|
|
165
|
+
if title.lower() == 'abstract':
|
|
166
|
+
# Handle abstract specially
|
|
167
|
+
abstract_content = config_data.get('abstract', 'Abstract content not found.')
|
|
168
|
+
gen.add_section("Abstract", abstract_content.strip(), level=main_level)
|
|
169
|
+
|
|
170
|
+
elif section_type == 'markdown' and source:
|
|
171
|
+
# Load and process markdown file
|
|
172
|
+
markdown_content = self.load_markdown_content(source)
|
|
173
|
+
if markdown_content:
|
|
174
|
+
if source in ['performance_table.md', 'research_areas.md', 'detailed_results.md']:
|
|
175
|
+
# Files that should be processed as raw markdown with CSV support
|
|
176
|
+
processed_content = self._process_markdown_with_csv(markdown_content)
|
|
177
|
+
gen.add_raw_latex(processed_content)
|
|
178
|
+
else:
|
|
179
|
+
# Files that should be processed with section headers
|
|
180
|
+
self.process_markdown_with_sections(gen, markdown_content, title, main_level, sub_level)
|
|
181
|
+
else:
|
|
182
|
+
gen.add_section(title, f"Content not found: {source}", level=main_level)
|
|
183
|
+
|
|
184
|
+
elif section_type == 'auto':
|
|
185
|
+
# Auto-generated sections (like Visualizations)
|
|
186
|
+
if title.lower() == 'visualizations':
|
|
187
|
+
self.generate_visualizations_section(gen, main_level)
|
|
188
|
+
else:
|
|
189
|
+
gen.add_section(title, "Auto-generated content placeholder", level=main_level)
|
|
190
|
+
|
|
191
|
+
def process_markdown_with_sections(self, gen, markdown: str, main_title: str, main_level: int, sub_level: int):
|
|
192
|
+
"""Process markdown content with section handling."""
|
|
193
|
+
lines = markdown.split('\n')
|
|
194
|
+
current_section = []
|
|
195
|
+
section_title = None
|
|
196
|
+
|
|
197
|
+
# Add main section
|
|
198
|
+
gen.add_section(main_title, "", level=main_level)
|
|
199
|
+
|
|
200
|
+
for line in lines:
|
|
201
|
+
if line.startswith('# '):
|
|
202
|
+
# Skip main title (already added)
|
|
203
|
+
continue
|
|
204
|
+
elif line.startswith('## '):
|
|
205
|
+
# Save previous subsection if exists
|
|
206
|
+
if section_title and current_section:
|
|
207
|
+
content = '\n'.join(current_section).strip()
|
|
208
|
+
gen.add_section(section_title, content, level=sub_level)
|
|
209
|
+
# Start new subsection
|
|
210
|
+
section_title = line.replace('## ', '').strip()
|
|
211
|
+
current_section = []
|
|
212
|
+
elif line.startswith('### '):
|
|
213
|
+
# Save previous subsection if exists
|
|
214
|
+
if section_title and current_section:
|
|
215
|
+
content = '\n'.join(current_section).strip()
|
|
216
|
+
gen.add_section(section_title, content, level=sub_level)
|
|
217
|
+
# Start new subsection
|
|
218
|
+
section_title = line.replace('### ', '').strip()
|
|
219
|
+
current_section = []
|
|
220
|
+
else:
|
|
221
|
+
current_section.append(line)
|
|
222
|
+
|
|
223
|
+
# Add last subsection
|
|
224
|
+
if section_title and current_section:
|
|
225
|
+
content = '\n'.join(current_section).strip()
|
|
226
|
+
gen.add_section(section_title, content, level=sub_level)
|
|
227
|
+
|
|
228
|
+
def generate_visualizations_section(self, gen, level: int):
|
|
229
|
+
"""Generate the visualizations section."""
|
|
230
|
+
gen.add_section("Visualizations", "", level=level)
|
|
231
|
+
|
|
232
|
+
tikz_code = """
|
|
233
|
+
% Simple neural network diagram
|
|
234
|
+
\\node[circle, draw, minimum size=1cm] (input) at (0,0) {Input};
|
|
235
|
+
\\node[circle, draw, minimum size=1cm] (hidden1) at (3,1) {H1};
|
|
236
|
+
\\node[circle, draw, minimum size=1cm] (hidden2) at (3,-1) {H2};
|
|
237
|
+
\\node[circle, draw, minimum size=1cm] (output) at (6,0) {Output};
|
|
238
|
+
|
|
239
|
+
\\draw[->] (input) -- (hidden1);
|
|
240
|
+
\\draw[->] (input) -- (hidden2);
|
|
241
|
+
\\draw[->] (hidden1) -- (output);
|
|
242
|
+
\\draw[->] (hidden2) -- (output);
|
|
243
|
+
"""
|
|
244
|
+
gen.add_raw_latex(f"""
|
|
245
|
+
\\begin{{figure}}[htbp]
|
|
246
|
+
\\centering
|
|
247
|
+
\\begin{{tikzpicture}}
|
|
248
|
+
{tikz_code}
|
|
249
|
+
\\end{{tikzpicture}}
|
|
250
|
+
\\caption{{Neural Network Architecture}}
|
|
251
|
+
\\label{{fig:neural_net}}
|
|
252
|
+
\\end{{figure}}
|
|
253
|
+
""")
|
|
254
|
+
|
|
255
|
+
gen.add_raw_latex("""
|
|
256
|
+
The neural network architecture is shown in Figure~\\ref{fig:neural_net}.
|
|
257
|
+
In a complete report, you would include figures using commands like:
|
|
258
|
+
|
|
259
|
+
\\begin{verbatim}
|
|
260
|
+
\\includegraphics[width=0.8\\textwidth]{artifacts/images/results_graph.jpg}
|
|
261
|
+
\\end{verbatim}
|
|
262
|
+
|
|
263
|
+
For wrapped figures with text flow, use the wrapfig environment.
|
|
264
|
+
""")
|
|
265
|
+
|
|
266
|
+
def generate_sample_report(self) -> str:
|
|
267
|
+
"""
|
|
268
|
+
Generate a comprehensive sample research report demonstrating all features.
|
|
269
|
+
|
|
270
|
+
Returns:
|
|
271
|
+
Path to the generated .tex file
|
|
272
|
+
"""
|
|
273
|
+
# Load configuration from markdown
|
|
274
|
+
config_data = self.load_config_from_markdown()
|
|
275
|
+
|
|
276
|
+
# Get document options and type defaults
|
|
277
|
+
doc_options = config_data.get('document options', {})
|
|
278
|
+
headers_footers = config_data.get('headers and footers', {})
|
|
279
|
+
type_font_size = config_data.get('_type_font_size', '12pt')
|
|
280
|
+
type_paper_size = config_data.get('_type_paper_size', 'letterpaper')
|
|
281
|
+
|
|
282
|
+
# Configure the document (type defaults, overridden by config options)
|
|
283
|
+
config = DocumentConfig(
|
|
284
|
+
doc_class=config_data.get('document class', 'article'),
|
|
285
|
+
font_size=doc_options.get('font_size', type_font_size),
|
|
286
|
+
paper_size=doc_options.get('paper_size', type_paper_size),
|
|
287
|
+
title=config_data.get('title', 'Research Report'),
|
|
288
|
+
author=config_data.get('authors', ['Anonymous'])[0] if isinstance(config_data.get('authors'), list) else config_data.get('authors', 'Anonymous'),
|
|
289
|
+
date=r"\today",
|
|
290
|
+
include_toc=doc_options.get('include_toc', True),
|
|
291
|
+
include_bibliography=doc_options.get('include_bibliography', True),
|
|
292
|
+
two_column=doc_options.get('two_column', False),
|
|
293
|
+
header_left=headers_footers.get('header_left', 'Research Report'),
|
|
294
|
+
header_right=headers_footers.get('header_right', r'\today'),
|
|
295
|
+
footer_center=headers_footers.get('footer_center', r'\thepage')
|
|
296
|
+
)
|
|
297
|
+
|
|
298
|
+
# Create the generator
|
|
299
|
+
gen = LaTeXGenerator(config)
|
|
300
|
+
|
|
301
|
+
# Get document structure from config
|
|
302
|
+
document_structure = config_data.get('content manifest', [])
|
|
303
|
+
|
|
304
|
+
if document_structure:
|
|
305
|
+
# Use configurable document structure
|
|
306
|
+
self.generate_document_from_structure(gen, document_structure, config_data)
|
|
307
|
+
else:
|
|
308
|
+
# Fallback to default structure if no config found
|
|
309
|
+
gen.add_section("Abstract", config_data.get('abstract', 'No abstract provided'), level=1)
|
|
310
|
+
|
|
311
|
+
# Note: CSV tables are now handled via inline markdown references
|
|
312
|
+
|
|
313
|
+
# Add citations in bibliography
|
|
314
|
+
self.add_bibliography_entries(gen)
|
|
315
|
+
|
|
316
|
+
# Add CSV-based table
|
|
317
|
+
csv_file = self.data_dir / "model_performance.csv"
|
|
318
|
+
if csv_file.exists():
|
|
319
|
+
gen.add_raw_latex("""
|
|
320
|
+
|
|
321
|
+
\\subsection{Detailed Performance Metrics}
|
|
322
|
+
|
|
323
|
+
The complete performance data is presented below:
|
|
324
|
+
|
|
325
|
+
""")
|
|
326
|
+
# For CSV tables, we'll read and create a table manually
|
|
327
|
+
# since csvsimple might need specific configuration
|
|
328
|
+
import csv
|
|
329
|
+
with open(csv_file, 'r') as f:
|
|
330
|
+
reader = csv.reader(f)
|
|
331
|
+
rows = list(reader)
|
|
332
|
+
if rows:
|
|
333
|
+
headers = rows[0]
|
|
334
|
+
data_rows = rows[1:]
|
|
335
|
+
gen.add_table(
|
|
336
|
+
caption="Complete Model Performance Data",
|
|
337
|
+
headers=headers,
|
|
338
|
+
rows=data_rows,
|
|
339
|
+
label="tab:complete_perf"
|
|
340
|
+
)
|
|
341
|
+
|
|
342
|
+
# Add training metrics table
|
|
343
|
+
csv_file2 = self.data_dir / "training_metrics.csv"
|
|
344
|
+
if csv_file2.exists():
|
|
345
|
+
import csv
|
|
346
|
+
with open(csv_file2, 'r') as f:
|
|
347
|
+
reader = csv.reader(f)
|
|
348
|
+
rows = list(reader)
|
|
349
|
+
if rows:
|
|
350
|
+
headers = rows[0]
|
|
351
|
+
# Only show first 5 rows to keep table concise
|
|
352
|
+
data_rows = rows[1:6]
|
|
353
|
+
gen.add_table(
|
|
354
|
+
caption="Training Progression (First 5 Epochs)",
|
|
355
|
+
headers=headers,
|
|
356
|
+
rows=data_rows,
|
|
357
|
+
label="tab:training"
|
|
358
|
+
)
|
|
359
|
+
|
|
360
|
+
# Add Results discussion
|
|
361
|
+
results_md = self.load_markdown_content("results.md")
|
|
362
|
+
if results_md:
|
|
363
|
+
# Process results markdown
|
|
364
|
+
results_lines = results_md.split('\n')
|
|
365
|
+
current_section = []
|
|
366
|
+
section_title = None
|
|
367
|
+
|
|
368
|
+
for line in results_lines:
|
|
369
|
+
if line.startswith('# '):
|
|
370
|
+
continue
|
|
371
|
+
elif line.startswith('## '):
|
|
372
|
+
if section_title and current_section:
|
|
373
|
+
content = '\n'.join(current_section).strip()
|
|
374
|
+
gen.add_section(section_title, content, level=2)
|
|
375
|
+
section_title = line.replace('## ', '').strip()
|
|
376
|
+
current_section = []
|
|
377
|
+
elif line.startswith('### '):
|
|
378
|
+
if section_title and current_section:
|
|
379
|
+
content = '\n'.join(current_section).strip()
|
|
380
|
+
gen.add_section(section_title, content, level=2)
|
|
381
|
+
section_title = line.replace('### ', '').strip()
|
|
382
|
+
current_section = []
|
|
383
|
+
else:
|
|
384
|
+
current_section.append(line)
|
|
385
|
+
|
|
386
|
+
if section_title and current_section:
|
|
387
|
+
content = '\n'.join(current_section).strip()
|
|
388
|
+
gen.add_section(section_title, content, level=2)
|
|
389
|
+
|
|
390
|
+
# Add a TikZ diagram (simple graph example)
|
|
391
|
+
gen.add_section("Visualizations", "", level=1)
|
|
392
|
+
|
|
393
|
+
tikz_code = """
|
|
394
|
+
% Simple neural network diagram
|
|
395
|
+
\\node[circle, draw, minimum size=1cm] (input) at (0,0) {Input};
|
|
396
|
+
\\node[circle, draw, minimum size=1cm] (hidden1) at (3,1) {H1};
|
|
397
|
+
\\node[circle, draw, minimum size=1cm] (hidden2) at (3,-1) {H2};
|
|
398
|
+
\\node[circle, draw, minimum size=1cm] (output) at (6,0) {Output};
|
|
399
|
+
|
|
400
|
+
\\draw[->] (input) -- (hidden1);
|
|
401
|
+
\\draw[->] (input) -- (hidden2);
|
|
402
|
+
\\draw[->] (hidden1) -- (output);
|
|
403
|
+
\\draw[->] (hidden2) -- (output);
|
|
404
|
+
"""
|
|
405
|
+
gen.add_tikz_diagram(tikz_code, "Simple Neural Network Architecture", label="fig:nn")
|
|
406
|
+
|
|
407
|
+
# Add note about images (since we may not have actual image files)
|
|
408
|
+
gen.add_raw_latex("""
|
|
409
|
+
|
|
410
|
+
\\subsection{Image Placement Examples}
|
|
411
|
+
|
|
412
|
+
In a complete report, you would include figures using commands like:
|
|
413
|
+
|
|
414
|
+
\\begin{verbatim}
|
|
415
|
+
\\includegraphics[width=0.8\\textwidth]{artifacts/images/results_graph.jpg}
|
|
416
|
+
\\end{verbatim}
|
|
417
|
+
|
|
418
|
+
For wrapped figures with text flow, use the wrapfig environment.
|
|
419
|
+
""")
|
|
420
|
+
|
|
421
|
+
# Add citations in bibliography
|
|
422
|
+
gen.add_bib_entry(
|
|
423
|
+
"\\bibitem{vaswani2017attention}\n"
|
|
424
|
+
"Vaswani, A., et al. (2017). "
|
|
425
|
+
"Attention is all you need. "
|
|
426
|
+
"In Advances in neural information processing systems (pp. 5998-6008)."
|
|
427
|
+
)
|
|
428
|
+
gen.add_bib_entry(
|
|
429
|
+
"\\bibitem{devlin2018bert}\n"
|
|
430
|
+
"Devlin, J., et al. (2018). "
|
|
431
|
+
"BERT: Pre-training of deep bidirectional transformers for language understanding. "
|
|
432
|
+
"arXiv preprint arXiv:1810.04805."
|
|
433
|
+
)
|
|
434
|
+
gen.add_bib_entry(
|
|
435
|
+
"\\bibitem{brown2020gpt3}\n"
|
|
436
|
+
"Brown, T., et al. (2020). "
|
|
437
|
+
"Language models are few-shot learners. "
|
|
438
|
+
"Advances in neural information processing systems, 33, 1877-1901."
|
|
439
|
+
)
|
|
440
|
+
|
|
441
|
+
# Add conclusion from markdown
|
|
442
|
+
conclusion_md = self.load_markdown_content("conclusion.md")
|
|
443
|
+
if conclusion_md:
|
|
444
|
+
gen.add_raw_latex(markdown_to_latex(conclusion_md))
|
|
445
|
+
|
|
446
|
+
# Save the document
|
|
447
|
+
output_file = self.output_dir / "research_report.tex"
|
|
448
|
+
gen.save(str(output_file))
|
|
449
|
+
|
|
450
|
+
return str(output_file)
|
|
451
|
+
|
|
452
|
+
# Note: CSV table handling has been moved to inline markdown references
|
|
453
|
+
# Tables are now defined in markdown files with CSV_TABLE comments
|
|
454
|
+
|
|
455
|
+
def add_bibliography_entries(self, gen):
|
|
456
|
+
"""Add bibliography entries to the document."""
|
|
457
|
+
gen.add_bib_entry(
|
|
458
|
+
"\\bibitem{vaswani2017attention}\n"
|
|
459
|
+
"Vaswani, A., et al. (2017). "
|
|
460
|
+
"Attention is all you need. "
|
|
461
|
+
"In Advances in neural information processing systems (pp. 5998-6008)."
|
|
462
|
+
)
|
|
463
|
+
gen.add_bib_entry(
|
|
464
|
+
"\\bibitem{devlin2018bert}\n"
|
|
465
|
+
"Devlin, J., et al. (2018). "
|
|
466
|
+
"BERT: Pre-training of deep bidirectional transformers for language understanding. "
|
|
467
|
+
"arXiv preprint arXiv:1810.04805."
|
|
468
|
+
)
|
|
469
|
+
gen.add_bib_entry(
|
|
470
|
+
"\\bibitem{brown2020gpt3}\n"
|
|
471
|
+
"Brown, T., et al. (2020). "
|
|
472
|
+
"Language models are few-shot learners. "
|
|
473
|
+
"Advances in neural information processing systems, 33, 1877-1901."
|
|
474
|
+
)
|
|
475
|
+
|
|
476
|
+
def compile_to_pdf(self, tex_file: str) -> bool:
|
|
477
|
+
"""
|
|
478
|
+
Compile the LaTeX file to PDF.
|
|
479
|
+
|
|
480
|
+
Args:
|
|
481
|
+
tex_file: Path to the .tex file
|
|
482
|
+
|
|
483
|
+
Returns:
|
|
484
|
+
True if successful, False otherwise
|
|
485
|
+
"""
|
|
486
|
+
compiler = PDFCompiler(output_dir=str(self.output_dir))
|
|
487
|
+
|
|
488
|
+
# Check LaTeX installation
|
|
489
|
+
is_installed, message = compiler.validate_latex_installation()
|
|
490
|
+
print(f"LaTeX Installation: {message}")
|
|
491
|
+
|
|
492
|
+
if not is_installed:
|
|
493
|
+
print("ERROR: LaTeX is not installed. Cannot compile PDF.")
|
|
494
|
+
return False
|
|
495
|
+
|
|
496
|
+
# Compile the document (2 runs for references)
|
|
497
|
+
success, message = compiler.compile(tex_file, runs=2)
|
|
498
|
+
print(f"\nCompilation result: {message}")
|
|
499
|
+
|
|
500
|
+
return success
|
|
501
|
+
|
|
502
|
+
|
|
503
|
+
def main():
|
|
504
|
+
"""Main function to demonstrate report generation."""
|
|
505
|
+
print("=" * 60)
|
|
506
|
+
print("DeepAgents PrintShop - LaTeX Research Report Generator")
|
|
507
|
+
print("=" * 60)
|
|
508
|
+
print()
|
|
509
|
+
|
|
510
|
+
generator = ResearchReportGenerator()
|
|
511
|
+
|
|
512
|
+
print("Generating LaTeX report...")
|
|
513
|
+
tex_file = generator.generate_sample_report()
|
|
514
|
+
print(f"✓ LaTeX file created: {tex_file}")
|
|
515
|
+
print()
|
|
516
|
+
|
|
517
|
+
print("Compiling to PDF...")
|
|
518
|
+
success = generator.compile_to_pdf(tex_file)
|
|
519
|
+
|
|
520
|
+
if success:
|
|
521
|
+
print()
|
|
522
|
+
print("=" * 60)
|
|
523
|
+
print("✓ Report generation complete!")
|
|
524
|
+
print(f" LaTeX file: {tex_file}")
|
|
525
|
+
print(f" PDF file: {tex_file.replace('.tex', '.pdf')}")
|
|
526
|
+
print("=" * 60)
|
|
527
|
+
else:
|
|
528
|
+
print()
|
|
529
|
+
print("=" * 60)
|
|
530
|
+
print("⚠ LaTeX file created but PDF compilation failed.")
|
|
531
|
+
print(f" You can manually compile: pdflatex {tex_file}")
|
|
532
|
+
print("=" * 60)
|
|
533
|
+
|
|
534
|
+
|
|
535
|
+
if __name__ == "__main__":
|
|
536
|
+
main()
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"""Visual QA Agent - PDF quality analysis and validation."""
|