Overview
BioAgents can generate publication-ready LaTeX papers from Deep Research conversations. The system synthesizes all accumulated knowledge (hypotheses, discoveries, tasks, artifacts) into a structured academic paper with proper citations and figures.
Papers are generated as both PDF (compiled LaTeX) and raw .tex files for further editing.
Architecture
Components
Paper Generation Service - Orchestrates paper creation
LaTeX Template - Structured academic paper format
Tectonic Compiler - Compiles LaTeX to PDF
S3 Storage - Stores PDF and .tex files
Presigned URLs - Secure, time-limited download links
API Endpoints
Generate Paper (Sync)
curl -X POST https://api.bioagents.ai/api/deep-research/conversations/abc123/paper \
-H "Authorization: Bearer YOUR_JWT_TOKEN"
Response:
{
"success" : true ,
"paperId" : "550e8400-e29b-41d4-a716-446655440000" ,
"conversationId" : "abc123" ,
"conversationStateId" : "def456" ,
"pdfPath" : "user/user456/conversation/abc123/papers/550e8400/paper.pdf" ,
"pdfUrl" : "https://bucket.s3.amazonaws.com/.../paper.pdf?X-Amz-..." ,
"rawLatexUrl" : "https://bucket.s3.amazonaws.com/.../main.tex?X-Amz-..."
}
Generate Paper (Async)
For large conversations, use async mode:
curl -X POST https://api.bioagents.ai/api/deep-research/conversations/abc123/paper/async \
-H "Authorization: Bearer YOUR_JWT_TOKEN"
Response (202 Accepted):
{
"success" : true ,
"paperId" : "550e8400-e29b-41d4-a716-446655440000" ,
"jobId" : "550e8400-e29b-41d4-a716-446655440000" ,
"conversationId" : "abc123" ,
"status" : "queued" ,
"statusUrl" : "/api/deep-research/paper/550e8400-e29b-41d4-a716-446655440000/status"
}
Check Paper Status
curl https://api.bioagents.ai/api/deep-research/paper/550e8400-e29b-41d4-a716-446655440000/status \
-H "Authorization: Bearer YOUR_JWT_TOKEN"
Response:
{
"paperId" : "550e8400-e29b-41d4-a716-446655440000" ,
"conversationId" : "abc123" ,
"status" : "completed" ,
"pdfUrl" : "https://bucket.s3.amazonaws.com/.../paper.pdf?X-Amz-..." ,
"rawLatexUrl" : "https://bucket.s3.amazonaws.com/.../main.tex?X-Amz-..." ,
"createdAt" : "2024-01-15T10:30:00.000Z"
}
Possible Statuses:
pending - Paper record created, waiting for processing
processing - LaTeX generation or compilation in progress
completed - Paper ready, URLs available
failed - Generation failed (check error field)
Get Paper with Fresh URLs
Presigned URLs expire after 1 hour. Regenerate them:
curl https://api.bioagents.ai/api/deep-research/paper/550e8400-e29b-41d4-a716-446655440000 \
-H "Authorization: Bearer YOUR_JWT_TOKEN"
List All Papers for Conversation
curl https://api.bioagents.ai/api/deep-research/conversations/abc123/papers \
-H "Authorization: Bearer YOUR_JWT_TOKEN"
Response:
{
"success" : true ,
"conversationId" : "abc123" ,
"papers" : [
{
"paperId" : "550e8400-e29b-41d4-a716-446655440000" ,
"pdfPath" : "user/.../paper.pdf" ,
"createdAt" : "2024-01-15T10:30:00.000Z" ,
"status" : "completed"
}
]
}
Paper Generation Flow
Step 1: Fetch Conversation State
src/services/paper/generatePaper.ts
export async function generatePaperFromConversation (
conversationId : string ,
userId : string
) {
// Verify conversation ownership
const conversation = await getConversation ( conversationId );
if ( ! conversation ) {
throw new Error ( `Conversation ${ conversationId } not found` );
}
if ( conversation . user_id !== userId ) {
throw new Error ( `User ${ userId } does not own conversation ${ conversationId } ` );
}
// Fetch conversation state (world state)
const conversationState = await getConversationState ( conversation . conversation_state_id );
if ( ! conversationState ) {
throw new Error ( `Conversation state not found` );
}
// Extract research data
const {
objective ,
currentHypothesis ,
keyInsights ,
discoveries ,
methodology ,
plan ,
} = conversationState . values ;
}
src/services/paper/generatePaper.ts
// Extract all tasks with outputs
const completedTasks = ( plan || []). filter (( task ) => task . output );
// Extract citations from task outputs
const citations = new Set < string >();
for ( const task of completedTasks ) {
const dois = extractDOIs ( task . output || "" );
dois . forEach (( doi ) => citations . add ( doi ));
}
// Link discoveries to evidence
const discoveryData = ( discoveries || []). map (( discovery ) => ({
claim: discovery . claim ,
evidence: discovery . evidence ,
novelty: discovery . novelty ,
confidence: discovery . confidence ,
supportingTaskIds: discovery . supportingTaskIds || [],
supportingTasks: completedTasks . filter (( task ) =>
discovery . supportingTaskIds ?. includes ( task . id )
),
}));
Step 3: Generate LaTeX
src/services/paper/generatePaper.ts
const paperContent = generateLatexContent ({
title: conversationState . values . conversationTitle || "Research Report" ,
abstract: currentHypothesis || "" ,
introduction: objective || "" ,
methodology ,
results: completedTasks ,
discussion: keyInsights ?. join ( " \n\n " ) || "" ,
discoveries: discoveryData ,
citations: Array . from ( citations ),
});
function generateLatexContent ( data : PaperData ) : string {
return `
\\ documentclass{article}
\\ usepackage[utf8]{inputenc}
\\ usepackage{graphicx}
\\ usepackage{cite}
\\ usepackage{hyperref}
\\ title{ ${ escapeLaTeX ( data . title ) } }
\\ author{BioAgents Research Assistant}
\\ date{ \\ today}
\\ begin{document}
\\ maketitle
\\ begin{abstract}
${ escapeLaTeX ( data . abstract ) }
\\ end{abstract}
\\ section{Introduction}
${ escapeLaTeX ( data . introduction ) }
\\ section{Methodology}
${ escapeLaTeX ( data . methodology ) }
\\ section{Results}
${ generateResultsSection ( data . results ) }
\\ section{Discussion}
${ escapeLaTeX ( data . discussion ) }
\\ section{Novel Discoveries}
${ generateDiscoveriesSection ( data . discoveries ) }
\\ bibliographystyle{plain}
\\ bibliography{references}
\\ end{document}
` ;
}
Step 4: Compile with Tectonic
src/services/paper/generatePaper.ts
import { exec } from "child_process" ;
import { promisify } from "util" ;
const execAsync = promisify ( exec );
async function compileLaTeX ( texPath : string , outputDir : string ) : Promise < string > {
try {
// Compile LaTeX to PDF using Tectonic
await execAsync ( `tectonic ${ texPath } ` , {
cwd: outputDir ,
timeout: 60000 , // 60 second timeout
});
const pdfPath = texPath . replace ( ".tex" , ".pdf" );
return pdfPath ;
} catch ( error ) {
logger . error ({ error }, "latex_compilation_failed" );
throw new Error ( `LaTeX compilation failed: ${ error . message } ` );
}
}
Tectonic is a modern LaTeX compiler that automatically downloads required packages. No manual package management needed.
Step 5: Upload to S3
src/services/paper/generatePaper.ts
// Upload PDF
const pdfBuffer = await fs . readFile ( pdfPath );
await storageProvider . upload (
` ${ basePath } /paper.pdf` ,
pdfBuffer ,
"application/pdf"
);
// Upload raw LaTeX
const texBuffer = await fs . readFile ( texPath );
await storageProvider . upload (
` ${ basePath } /main.tex` ,
texBuffer ,
"text/plain"
);
// Generate presigned URLs (1 hour expiry)
const pdfUrl = await storageProvider . getPresignedUrl (
` ${ basePath } /paper.pdf` ,
3600
);
const rawLatexUrl = await storageProvider . getPresignedUrl (
` ${ basePath } /main.tex` ,
3600
);
Step 6: Save Paper Record
src/services/paper/generatePaper.ts
const paperId = randomUUID ();
await supabase . from ( "paper" ). insert ({
id: paperId ,
user_id: userId ,
conversation_id: conversationId ,
pdf_path: ` ${ basePath } /paper.pdf` ,
status: "completed" ,
});
return {
paperId ,
conversationId ,
conversationStateId: conversation . conversation_state_id ,
pdfPath: ` ${ basePath } /paper.pdf` ,
pdfUrl ,
rawLatexUrl ,
};
LaTeX Paper Structure
Title and Abstract
\title {Investigation of Caloric Restriction and Lifespan Extension in Mammals}
\author {BioAgents Research Assistant}
\date { \today }
\maketitle
\begin { abstract }
Caloric restriction (CR) extends lifespan across multiple species through modulation of autophagy, mTOR signaling, and stress resistance pathways. Our analysis reveals a coordinated molecular response characterized by upregulation of Atg7, Ulk1, and Sirt1, alongside downregulation of mTOR and IGF-1R.
\end { abstract }
Results with Task Outputs
\section { Results }
\subsection { Literature Review: Autophagy Mechanisms }
% Task: lit-1 (LITERATURE)
Recent studies demonstrate that autophagy induction is a key mediator of CR-induced lifespan extension~ \cite { 10.1038 / nature24630 , 10.1016 / j.cell.2019.02.013 }.
\subsection { Differential Gene Expression Analysis }
% Task: ana-1 (ANALYSIS)
Analysis of RNA-seq data from CR vs control groups revealed significant upregulation of autophagy genes (Figure~ \ref { fig:analysis-1 }).
\begin { figure }[h]
\centering
\includegraphics [width=0.8 \textwidth ]{artifacts/ana-1/heatmap.png}
\caption {Differential gene expression heatmap showing upregulated autophagy genes}
\label { fig:analysis-1 }
\end { figure }
Novel Discoveries
\section { Novel Discoveries }
\begin { enumerate }
\item \textbf { Sirt1-Autophagy Coordination: } We identified a novel regulatory connection between Sirt1 upregulation (1.64-fold) and autophagy activation, supported by correlation analysis (r = 0.87, p < 0.001). \textit { Evidence: } Analysis task ana-1, Literature task lit-2. \textit { Confidence: } High.
\item \textbf { mTOR-Lifespan Inverse Relationship: } mTOR expression inversely correlates with lifespan (r = -0.81, p = 0.001), suggesting mTOR inhibition as a sufficient mechanism for CR-mediated longevity. \textit { Evidence: } Analysis task ana-1. \textit { Confidence: } Medium.
\end { enumerate }
Bibliography
\bibliographystyle {plain}
\begin { thebibliography }{99}
\bibitem {10.1038/nature24630}
Autophagy gene 7 upregulation promotes longevity.
\textit { Nature }, 2017.
\url { https://doi.org/10.1038/nature24630 }
\bibitem {10.1016/j.cell.2019.02.013}
ULK1 activation extends lifespan in mammals.
\textit { Cell }, 2019.
\url { https://doi.org/10.1016/j.cell.2019.02.013 }
\end { thebibliography }
Analysis tasks can generate artifacts (plots, tables) that are included in the paper:
interface PlanTask {
id : string ;
type : "LITERATURE" | "ANALYSIS" ;
objective : string ;
output ?: string ;
artifacts ?: Array <{
filename : string ;
path : string ; // S3 path
description : string ;
}>;
}
LaTeX Figure Inclusion:
\begin { figure }[h]
\centering
\includegraphics [width=0.8 \textwidth ]{artifacts/ana-1/heatmap.png}
\caption {Differential gene expression heatmap}
\label { fig:analysis-1 }
\end { figure }
Artifacts are downloaded from S3 during compilation. Ensure presigned URLs are still valid or use public URLs.
Concurrency Control
User-Level Limits
src/routes/deep-research/paper.ts
async function checkUserHasConcurrentPaperJob ( userId : string ) : Promise < boolean > {
const { data } = await supabase
. from ( "paper" )
. select ( "id" )
. eq ( "user_id" , userId )
. in ( "status" , [ "pending" , "processing" ])
. limit ( 1 );
return ( data ?. length ?? 0 ) > 0 ;
}
Users can only have one active paper generation job at a time.
Global Limits
src/routes/deep-research/paper.ts
async function checkGlobalConcurrentPaperLimit () : Promise <{
exceeded : boolean ;
current : number ;
}> {
const maxConcurrent = parseInt ( process . env . MAX_CONCURRENT_PAPER_JOBS || "3" );
const { count } = await supabase
. from ( "paper" )
. select ( "id" , { count: "exact" , head: true })
. in ( "status" , [ "pending" , "processing" ]);
const current = count ?? 0 ;
return { exceeded: current >= maxConcurrent , current };
}
System-wide limit: 3 concurrent paper jobs (configurable).
Error Handling
// 403 Forbidden - Not conversation owner
{
"error" : "Access denied" ,
"message" : "You do not have permission to generate a paper for this conversation"
}
// 404 Not Found - Conversation not found
{
"error" : "Resource not found" ,
"message" : "Conversation abc123 not found"
}
// 429 Too Many Requests - Concurrent job limit
{
"error" : "Concurrent paper limit exceeded" ,
"message" : "You already have a paper generation job in progress. Please wait for it to complete."
}
// 500 Internal Server Error - LaTeX compilation failed
{
"error" : "LaTeX compilation failed" ,
"message" : "! Undefined control sequence. \n l.42 \\ invalidcommand" ,
"hint" : "The paper content could not be compiled to PDF. Check the LaTeX syntax and citations."
}
Configuration
Environment Variables
# Paper Generation
MAX_CONCURRENT_PAPER_JOBS = 3 # Global concurrent limit
# Storage (required for paper uploads)
STORAGE_PROVIDER = s3
AWS_ACCESS_KEY_ID = ...
AWS_SECRET_ACCESS_KEY = ...
S3_BUCKET = ...
LaTeX Compiler
Install Tectonic:
# macOS
brew install tectonic
# Linux
curl --proto '=https' --tlsv1.2 -fsSL https://drop-sh.fullyjustified.net | sh
# Docker (already included in Dockerfile)
RUN apt-get install -y tectonic
Best Practices
After Deep Research cycles complete : Ensure all tasks have outputs
After discoveries are identified : Rich discovery data improves paper quality
Before sharing results : Papers provide professional presentation
Download raw .tex file : Edit locally with LaTeX editor
Recompile manually : Use tectonic main.tex or Overleaf
Add custom sections : Methodology details, acknowledgments, etc.
Handling Compilation Errors
Check LaTeX syntax : Ensure special characters are escaped
Verify citations : DOIs must be valid
Simplify complex content : Remove unsupported LaTeX commands
URLs expire after 1 hour
Use /api/deep-research/paper/:paperId to regenerate URLs
Store paperId for future access
Deep Research Generate research for papers
Chat Mode Quick research questions
File Upload Upload datasets for analysis
Tectonic Docs LaTeX compiler documentation