Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/bio-xyz/BioAgents/llms.txt

Use this file to discover all available pages before exploring further.

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

  1. Paper Generation Service - Orchestrates paper creation
  2. LaTeX Template - Structured academic paper format
  3. Tectonic Compiler - Compiles LaTeX to PDF
  4. S3 Storage - Stores PDF and .tex files
  5. 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;
}

Step 2: Extract Research Data

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}

Artifacts and Figures

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.\nl.42 \\invalidcommand",
  "hint": "The paper content could not be compiled to PDF. Check the LaTeX syntax and citations."
}

Configuration

Environment Variables

.env
# 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.
  • 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