This repository is a research-paper automation pipeline. Its core flow is:
PDF papers
-> ingestion/doc2json + Grobid
-> raw S2ORC-style JSON
-> cleaned JSON
-> agent swarm discussion / brainstorming
-> paper-to-code planning, analysis, code generation, evaluation, reproduction
The repo currently contains three major systems:
run.py: the top-level CLI that ties the main workflows together.agentswarm/: a paper-grounded multi-agent discussion and brainstorming system.paper2code/codes/: a numbered LLM pipeline that turns a cleaned paper JSON into generated source code and reproduction artifacts.ingestion/: a bundleddoc2json/S2ORC conversion package for PDF, LaTeX, JATS, and SPP-style inputs, with the top-level project using the PDF/Grobid path.
There are also preloaded paper PDFs in papers/, raw and cleaned JSONs in data/, generated artifacts in outputs/, prompt templates in paper2code/prompts/, and tests for the agent swarm in agentswarm/test_agentswarm.py.
.
+-- run.py
+-- Makefile
+-- README.md
+-- HELP.md
+-- requirements.txt
+-- agentswarm/
+-- paper2code/
| +-- codes/
| +-- examples/
| +-- prompts/
+-- ingestion/
| +-- scripts/
| +-- doc2json/
+-- papers/
+-- data/
| +-- raw_json/
| +-- cleaned_json/
+-- outputs/
Important note: README.md currently contains unresolved merge-conflict markers. HELP.md has the complete project-level quickstart text without the conflict markers.
Top-level requirements.txt includes:
- LLM and codegen:
openai,tiktoken,transformers,tqdm. - PDF ingestion:
beautifulsoup4,requests,lxml,python-magic,latex2mathml,Flask,itsdangerous,boto3. - Optional local model backend:
vllm.
The agentswarm package intentionally uses only Python stdlib plus this repo's own modules. The LLM call is made through urllib directly.
Environment variables used by the code:
OPENROUTER_API_KEY: required byagentswarmand the OpenRouter-backedpaper2codescripts.OPENAI_API_KEY: mentioned by docs as an alternative, but most currentpaper2codescripts instantiateOpenAI(base_url="https://openrouter.ai/api/v1", api_key=os.environ["OPENROUTER_API_KEY"]).
run.py is the main entry point. It exposes four subcommands: ingest, discuss, brainstorm, and codegen.
Purpose: Convert a PDF into cleaned JSON.
Flow:
- Validates that the PDF path exists.
- Defaults output to
data/cleaned_json/. - Calls
ingestion/scripts/ingest_pdf.sh. - The script checks that Grobid is alive at
localhost:8070. - It runs
ingestion/doc2json/grobid2json/process_pdf.pyto createdata/raw_json/<paper>.json. - It runs
paper2code/codes/0_pdf_process.pyto remove noisy metadata and write<paper>_cleaned.json.
Purpose: Ask a question over one or more cleaned paper JSON files.
Flow:
- Builds an
agentswarmCLI argument list. - Defaults papers to
data/cleaned_json/BERT_cleaned.json. - Passes
--max-agents,--top-k,--critique-rounds, model, logging, and streaming options through toagentswarm.cli. - Prints the final synthesis, consensus, disagreements, and citations.
Purpose: Generate future research directions by combining ideas across papers.
Flow:
- Uses the same paper loading, retriever, LLM client, and agent construction as discussion.
- Runs
BrainstormOrchestrator. - Prints seed ideas, cross-pollinated ideas, and a final research agenda.
Purpose: Generate a code repository from a cleaned paper JSON.
Flow:
- Validates the cleaned JSON path.
- Derives
paper_namefrom the JSON stem unless--nameis given. - Creates:
outputs/<paper_name>/for planning, analysis, cost, and raw LLM artifacts.outputs/<paper_name>_repo/for generated source files.
- Chooses script variants:
- OpenRouter path:
1_planning.py,2_analyzing.py,3_coding.py. - Local vLLM path:
1_planning_llm.py,2_analyzing_llm.py,3_coding_llm.py.
- OpenRouter path:
- Runs stages in order:
- planning
- config extraction
- analysis
- coding
- Copies
outputs/<paper_name>/planning_config.yamlinto the generated repo asconfig.yaml.
Makefile wraps common commands:
make setup: installs partial Python dependencies and editableingestion/.make setup-grobid: pullsgrobid/grobid:0.9.0-crf.make start-grobid: starts the Grobid Docker container on port8070.make ingest PDF=papers/bert.pdf: runsrun.py ingest.make ingest-all: ingests every PDF inpapers/.make discuss QUESTION="...": asks over all JSON files indata/cleaned_json/.make codegen JSON=...: runs OpenRouter-backed codegen.make codegen-local JSON=...: runs local-vLLM codegen.make clean: removes outputs, raw JSON, and cache directories.
papers/ contains PDF inputs:
attention_is_all_you_need.pdfbert.pdfog_attention.pdfvision_transformer.pdf
data/raw_json/*.json is S2ORC-style JSON generated from PDF parsing. It includes metadata, body paragraphs, citation spans, reference spans, equation spans, bibliography entries, and reference entries.
data/cleaned_json/*_cleaned.json is produced by 0_pdf_process.py. Cleaning recursively removes keys that are noisy or large for downstream LLM use:
cite_spansref_spanseq_spansauthorsbib_entriesyearvenueidentifiers_pdf_hashheader
The cleaned JSON remains structured around pdf_parse.abstract, pdf_parse.body_text, pdf_parse.back_matter, and pdf_parse.ref_entries.
For a paper named foo, codegen writes:
outputs/foo/planning_response.json: raw completion responses from planning.outputs/foo/planning_trajectories.json: conversation history across planning prompts.outputs/foo/planning_config.yaml: extracted training/configuration YAML.outputs/foo/planning_artifacts/: human-readable plan/design/config files.outputs/foo/analyzing_artifacts/: per-file logic analysis text.outputs/foo/*_simple_analysis_response.json: raw per-file analysis responses.outputs/foo/*_simple_analysis_trajectories.json: per-file analysis prompt histories.outputs/foo/coding_artifacts/: raw coding responses.outputs/foo/accumulated_cost.json: accumulated estimated model cost.outputs/foo/cost_info.log: token and cost logs.outputs/foo_repo/: generated source repository.
The agent swarm provides paper-grounded Q&A and brainstorming.
agentswarm/__init__.py re-exports the public API:
- Data state:
Blackboard,Claim,Critique,Evidence,Synthesis. - Brainstorming state:
BrainstormBlackboard,ResearchIdea,CrossPollinatedIdea. - Agents and orchestrators:
PaperExpertAgent,SwarmOrchestrator,BrainstormOrchestrator. - Loading/retrieval/LLM/logging helpers:
load_paper,load_papers,KeywordRetriever,OpenRouterLLM,SwarmLogger.
Core dataclasses:
PaperChunk: one searchable text unit withchunk_id,paper_id,paper_title,section,sec_num,text, andsource.Paper: loaded paper withpaper_id,title,abstract, sourcepath, andchunks.
load_paper(path) reads a cleaned JSON file and normalizes it into chunks:
- abstract blocks from
pdf_parse.abstract - body paragraphs from
pdf_parse.body_text - back matter from
pdf_parse.back_matter - figure/table/reference entries from
pdf_parse.ref_entries
If no structured chunks exist but top-level abstract text exists, it creates one fallback abstract chunk.
load_papers(paths) preserves input order and calls load_paper for each file.
KeywordRetriever is a dependency-free BM25-style retriever.
How it works:
- Flattens all paper chunks into one corpus.
- Tokenizes text with regex
TOKEN_RE = r"[A-Za-z0-9][A-Za-z0-9_\\-]*". - Builds per-chunk term frequencies and corpus document frequencies.
- Scores query/chunk pairs with BM25-like weighting using
k1 = 1.5andb = 0.75. - Can scope search to a single
paper_id. - Returns either raw
RetrievalResultobjects orEvidencedataclasses.
The discussion pipeline uses auditable dataclasses:
Evidence: retrieved paper chunk plus a computed citation label.Claim: one expert's answer to a question.Critique: one expert's response to another expert's claim.Synthesis: final answer, consensus list, disagreement list, and citations.Blackboard: mutable session state containing question, selected agents, claims, critiques, synthesis, and timestamp.
The orchestrator appends claims and critiques to the blackboard and sets the final synthesis.
OpenRouterLLM is a minimal stdlib OpenRouter client.
Features:
- Default model:
nvidia/nemotron-3-super-120b-a12b:free. - Reads API key from
OPENROUTER_API_KEY. - Uses
urllib.requestrather than the OpenAI SDK. - Supports blocking completions via
complete(messages). - Supports SSE streaming via
complete_stream(messages, on_token). - Parses OpenRouter chat-completion responses and raises explicit runtime errors for HTTP, URL, empty-response, and malformed-response failures.
SwarmLogger writes progress to stderr and optionally to a file.
Features:
- Colorful TTY output with automatic color disabling for non-TTY streams.
- Phase headers and completion lines.
- Agent start/done timing.
- Token streaming through
on_token. - Optional timestamped file logging via Python
logging.
PaperExpertAgent scopes one agent to one paper.
Main methods:
relevance(question): retrieves one evidence chunk from its paper and returns the top score.answer(question): retrievestop_kchunks, asks the LLM to answer using only that evidence, and returns aClaim.critique(question, target): retrieves evidence for the question plus target claim and returns aCritique.propose_research(area, id_counter): asks the LLM for 2-3 structured future-research seed ideas grounded in the paper.cross_pollinate(area, seed, id_counter): asks the LLM for one hybrid idea combining this paper with another paper's seed.summarize_position(claim): formats a claim with a citation.
Confidence is heuristic: no evidence gives 0.0; otherwise confidence is min(0.95, 0.35 + top_score / 20) rounded to two decimals.
SwarmOrchestrator coordinates a discussion.
Flow:
- Builds a fresh
Blackboard(question=...). - Selects agents by descending
agent.relevance(question). - Keeps agents with positive relevance, or the single top-ranked agent if none score above zero.
- Caps participation at
max_agents. - Asks each selected expert to answer.
- Runs
critique_roundsof all selected experts critiquing every other expert's claims. - Synthesizes a final
Synthesis.
The current synthesis is deterministic and simple: it concatenates expert positions and critiques rather than making another LLM call. Consensus and disagreement text are fixed templates plus the single-agent caveat.
Brainstorming has its own dataclasses:
ResearchIdea: a seed direction from one paper.CrossPollinatedIdea: a hybrid direction generated by one paper expert after reading another paper's seed.BrainstormBlackboard: area, seeds, cross-pollinations, agenda, timestamp.
Flow:
- Select top
max_agentsby relevance to the research area. - Seed round: each expert proposes 2-3 ideas from its own paper.
- Cross-pollination: each expert reads every other expert's seeds and proposes hybrid ideas. This repeats for
cross_pollinate_rounds. - Final agenda: the orchestrator calls the LLM with all seed and hybrid ideas and asks for 3-5 actionable research directions.
Parsing helpers expect structured model output delimited by:
---IDEA---/---END---TEXT:GROUNDING:GAP:---CROSSPOLLINATE---CONNECTION:
If seed parsing fails, the whole response is reduced to one fallback idea.
The package CLI supports:
agentswarm discuss <question>agentswarm brainstorm <area>
Shared flags:
--papers--model--top-k--log-file--no-stream
Discussion-specific flags:
--max-agents--critique-rounds
Brainstorm-specific flags:
--max-agents--cp-rounds
The tests cover:
- Loading BERT cleaned JSON into chunks.
- Keyword retrieval for masked language modeling.
- Orchestrator execution with a stub LLM.
paper2code/codes is a collection of numbered scripts. Most scripts execute immediately at module import after parsing arguments, so they are meant to be run as CLI scripts, not imported as libraries.
There are two backend families:
- OpenRouter/OpenAI SDK scripts:
1_planning.py,2_analyzing.py,3_coding.py. - Local vLLM scripts:
1_planning_llm.py,2_analyzing_llm.py,3_coding_llm.py.
The top-level run.py codegen path only invokes stages 1, 1.1, 2, and 3. Stages 1.2, 3.1, 4, 5, and eval.py are utility/extension stages that are present but not part of the default run.py codegen pipeline.
Important helpers:
extract_planning(path): loadsplanning_trajectories.json, keeps assistant turns, strips</think>, and returns the first three assistant messages.content_to_json(...): attempts multiple regex-based cleanup strategies to parse model output wrapped in[CONTENT]...[/CONTENT]into JSON.extract_code_from_content(...): extracts the first fenced code block.extract_code_from_content2(...): extracts a fenced Python code block.format_json_data(data): converts a JSON object into a text artifact with section dividers.cal_cost(response_json, model_name): estimates cost from token usage for a hard-coded table of OpenAI model prices.print_log_cost(...): prints and appends token/cost info tocost_info.log.num_tokens_from_messages(...): estimates chat tokens usingtiktoken.read_all_files(directory, allowed_ext, is_print=True): recursively reads code/text files while skipping hidden paths, files without extensions, and oversized files.read_python_files(directory): recursively reads.pyfiles.extract_json_from_string(text): extracts fenced JSON.get_now_str(): timestamp string for result filenames.
Purpose: clean raw S2ORC JSON.
remove_spans(data) recursively removes noisy metadata keys from dictionaries and lists. main(args) loads --input_json_path, writes cleaned JSON to --output_json_path, and prints the saved path.
Purpose: use an LLM to build a reproduction plan, software architecture, task list, and config.
Inputs:
--paper_name--gpt_version--paper_format JSON|LaTeX--pdf_json_path--pdf_latex_path--output_dir
Flow:
- Loads the paper as JSON or LaTeX.
- Creates an OpenAI SDK client pointed at OpenRouter.
- Builds four prompt steps:
- overall reproduction plan
- architecture design with file list and Mermaid diagrams
- logic/task breakdown with package requirements
config.yamlgeneration
- Appends each prompt and assistant response to
trajectories. - Saves:
planning_response.jsonplanning_trajectories.jsonaccumulated_cost.jsoncost_info.log
Caveat: api_call(msg, gpt_version) ignores its gpt_version parameter and currently calls model "tencent/hy3-preview:free" directly.
Purpose: local-vLLM version of planning.
Differences:
- Uses
transformers.AutoTokenizerandvllm.LLM. - Supports Qwen-specific Yarn rope scaling and DeepSeek-specific stop token setup.
- Does not calculate OpenRouter/OpenAI costs.
- Saves
planning_response.jsonandplanning_trajectories.json.
Purpose: extract usable artifacts from planning trajectories.
Flow:
- Reads
planning_trajectories.json. - Takes turn index
8as the YAML-producing response. - Extracts fenced
yamland writesplanning_config.yaml. - Calls
extract_planningto obtain the first three assistant planning outputs. - Parses architecture and task JSON.
- Writes:
planning_artifacts/1.1_overall_plan.txtplanning_artifacts/1.2_arch_design.txtplanning_artifacts/1.3_logic_design.txtplanning_artifacts/1.4_config.yaml
Caveat: this stage assumes the planning trajectory has the same turn layout created by 1_planning.py/1_planning_llm.py, especially the YAML at index 8.
Purpose: refine generated config model/dataset names into Hugging Face IDs.
Flow:
- Loads
planning_config.yaml. - Asks OpenRouter to identify model and dataset names as a JSON list.
- Optionally uses
huggingface_hub.HfApito search model IDs by downloads. - Replaces detected names in the config with refined names.
- Backs up the original config to
planning_config.yaml.bak.
Caveats:
- The CLI accepts
--gpt_version, but the actual OpenRouter call is hard-coded to"tencent/hy3-preview:free". - It searches Hugging Face models only, even though the prompt mentions datasets too.
Purpose: generate detailed per-file logic analysis before code generation.
Inputs are the paper, planning config, planning trajectories, and task list.
Flow:
- Loads paper JSON or LaTeX.
- Loads
planning_config.yaml. - Extracts planning context with
extract_planning. - Reads
task_list.jsonif present; otherwise parses the third planning output. - Extracts
Task listandLogic Analysis. - For each file in the task list except
config.yaml:- prompts the LLM for detailed logic analysis
- writes text to
analyzing_artifacts/<file>_simple_analysis.txt - writes raw response and trajectories to output root
- updates accumulated cost
Caveat: api_call also hard-codes "tencent/hy3-preview:free".
Purpose: local-vLLM version of analysis.
It mirrors 2_analyzing.py but uses local model generation and skips cost accounting.
Purpose: generate each source file in dependency/task order.
Flow:
- Loads paper content, config, and planning context.
- Parses the task list from planning output.
- Loads each file's detailed analysis from stage 2.
- Maintains
done_file_lstanddone_file_dict. - For each todo file except
config.yaml:- includes already generated code files in the prompt
- asks the LLM to write only the current file
- extracts fenced code from the response
- writes raw response to
coding_artifacts/<file>_coding.txt - writes code to
output_repo_dir/<file> - updates accumulated cost
The generated config.yaml is copied by run.py, not produced directly by this stage.
Caveat: api_call hard-codes "tencent/hy3-preview:free".
Purpose: local-vLLM version of coding.
It mirrors 3_coding.py, but uses local model generation. If normal fenced-code extraction fails, it falls back to Python-specific code extraction and then to the raw response.
Potential issue: the local version loads detailed logic analysis from *_simple_analysis_trajectories.json using detailed_logic_analysis_trajectories[0]['content'], which is the system prompt rather than the assistant analysis in the trajectories written by stage 2 local. This may reduce analysis quality for local coding.
Purpose: generate a reproduce.sh runner for the generated repo.
Flow:
- Loads generated Python files from
output_repo_dir. - Loads config and planning context.
- Prompts OpenRouter to write a self-contained Bash script that installs dependencies and runs the project.
- Extracts code and writes
output_repo_dir/reproduce.sh.
This stage is not invoked by default from run.py codegen.
Purpose: ask an LLM to patch a generated repo based on an execution error log.
Core behavior:
- Reads
--error_file_name. - Loads planning trajectories and task list from
--output_dir. - Reads generated Python files and optionally
config.yaml/reproduce.sh. - Prompts OpenRouter to output SEARCH/REPLACE blocks.
parse_and_apply_changesapplies each replacement against files in the debug repo and writes.NNN.bakbackups.
Known issue: parse_args() does not define --output_repo_dir, but the script later reads args.output_repo_dir. As written, the script will raise an attribute error unless this argument is added.
Purpose: generate and run a reproduction experiment around a generated repo.
Flow:
- Loads paper JSON, planning artifacts, config, and generated code.
- Asks an LLM to extract dataset/evaluation/model/training information as JSON.
- Falls back to hard-coded Transformer/WMT2014 defaults if extraction fails.
- Saves
reproduce_dataset_info.json. - Asks an LLM to generate a complete
reproduce.py. - Falls back to
reproduce_template.pyif generation fails. - Writes
requirements.txtandrun_reproduce.shinto the reproduction directory. - Installs required packages with pip.
- Runs
reproduce.py. - Reads
reproduce_results.jsonif present and writesreproduce_summary.json.
This stage is relatively specialized around Transformer-style sequence-to-sequence reproduction. The fallback template is specifically for "Attention Is All You Need".
Purpose: built-in fallback reproduction script for a scaled-down Transformer.
Main features:
- Downloads a Hugging Face translation dataset.
- Trains or reuses a SentencePiece BPE tokenizer.
- Defines PyTorch dataset/collate utilities.
- Implements:
- sinusoidal positional encoding
- multi-head attention
- feed-forward network
- encoder layer
- decoder layer
- encoder-decoder Transformer
- label smoothing loss
- Noam-style learning-rate schedule
- beam search
- Trains for demo-scale steps and dataset size.
- Evaluates with BLEU and writes
reproduce_results.json.
Purpose: use an LLM evaluator to score generated code correctness.
Modes:
ref_free: evaluate paper + generated repo only.ref_based: evaluate paper + generated repo + gold/reference repo.
Flow:
- Loads paper JSON.
- Reads generated code. In
--papercodermode, it uses planning task order and includesconfig.yaml. - Loads the selected prompt template from
paper2code/prompts/. - Optionally includes gold code for reference-based evaluation.
- Counts tokens and exits if above 128k.
- Requests
generated_nindependent model evaluations. - Parses JSON responses with
scoreandcritique_list. - Averages valid scores.
- Writes an evaluation JSON into
eval_result_dir. - Logs cost.
Caveats:
- The request model is hard-coded to
"tencent/hy3-preview:free"despite a--gpt_versionargument. avg_score = sum(all_scores) / len(all_scores)will fail if all responses are invalid.- The output key is misspelled as
"scroe_lst".
The top-level project uses the PDF path, but the bundled doc2json package supports multiple source formats.
ingestion/scripts/setup_grobid.sh pulls grobid/grobid:0.9.0-crf.
ingestion/scripts/run_grobid.sh runs Grobid in Docker:
sudo docker run --rm --init --ulimit core=0 -p 8070:8070 grobid/grobid:0.9.0-crf
ingestion/scripts/ingest_pdf.sh is the top-level ingest worker:
- Validates the PDF file.
- Resolves repo paths.
- Checks Grobid
/api/isalive. - Runs
grobid2json/process_pdf.py. - Runs
0_pdf_process.py. - Prints raw and cleaned output paths.
ingestion/doc2json/grobid2json/process_pdf.py has two main entry points:
process_pdf_stream(...): sends in-memory PDF bytes to Grobid and converts the returned TEI soup to S2ORC JSON.process_pdf_file(...): sends a local PDF to Grobid, writes a.tei.xmltemp file, converts it to aPaper, and writes release JSON.
ingestion/doc2json/grobid2json/grobid/grobid_client.py handles the Grobid HTTP API:
- Default server:
localhost:8070. - Supports
processFulltextDocument, citation parsing, header-name parsing, and affiliation parsing. - Writes failed PDF IDs to
failed.logon non-200 responses. - Retries on HTTP 503 after sleeping.
ingestion/doc2json/grobid2json/tei_to_json.py converts TEI XML into the internal S2ORC Paper object.
Major responsibilities:
- Normalize Grobid object IDs like bibliography, figure, table, and equation references.
- Parse bibliography entries.
- Extract formulas.
- Convert TEI tables to HTML table markup.
- Extract figures and tables into
ref_entries. - Detect bracket-style citations.
- Convert notes to paragraphs.
- Process paragraphs into text plus citation/reference/equation spans.
- Extract abstract, body text, and back matter.
- Return a
Paperobject viaconvert_tei_xml_soup_to_s2orc_jsonorconvert_tei_xml_file_to_s2orc_json.
Core classes:
ReferenceEntry: figure/table/footnote/section/equation reference payloads.BibliographyEntry: parsed citation metadata.Affiliation: author affiliation.Author: author name, affiliation, and email.Metadata: title, authors, year, venue, identifiers.Paragraph: text, citation spans, reference spans, equation spans, section info.Paper: complete parsed paper with metadata, abstract, body, back matter, bibliography, and references.
Paper.release_json(doc_type="pdf") emits the release format used by downstream code:
{
"paper_id": ...,
"header": ...,
"title": ...,
"authors": ...,
"abstract": "...",
"pdf_parse": {
"abstract": [...],
"body_text": [...],
"back_matter": [...],
"bib_entries": {...},
"ref_entries": {...}
}
}
load_s2orc(paper_dict) loads either older grobid_parse layouts or current pdf_parse/body-text layouts into a Paper.
ingestion/doc2json/flask/app.py exposes a simple upload service on port 8080.
Supported inputs:
.pdf: processed byprocess_pdf_stream..gz: processed as LaTeX viaprocess_tex_stream..nxml: processed as JATS viaprocess_jats_stream./upload_url?url=...: downloads a PDF URL and returns JSON.
ingestion/doc2json/tex2json/ handles gzipped LaTeX sources.
Key files:
process_tex.py: stream/file wrapper for LaTeX processing.tex_to_xml.py: extracts LaTeX archives, normalizes LaTeX, converts normalized LaTeX to XML, then to S2ORC JSON.xml_to_json.py: the main XML-to-S2ORC converter. It processes authors, bibliography entries, reference tokens, lists, paragraphs, equations, footnotes, figures, tables, sections, abstract, and body text.
The LaTeX path can call Grobid for bibliography and author parsing.
ingestion/doc2json/jats2json/ handles JATS/NXML.
Key files:
process_jats.py: stream/file wrapper.jats_to_json.py: converts JATS XML into S2ORC-style JSON.pmc_utils/front_tag_utils.py: parses journal metadata, IDs, title, categories, dates, funding, authors, affiliations, and abstract.pmc_utils/back_tag_utils.py: parses back-matter bibliography entries.pmc_utils/all_tag_utils.py: parses paragraphs, spans, formulas, xrefs, and section recursion.pmc_utils/extract_utils.py: extracts figure, table, and supplement blobs.
ingestion/doc2json/spp2json/ supports SPP conversion.
Key files:
process_pdf.py: local PDF processing wrapper for SPP output.spp/spp_client.py: SPP service client.spp/spp_json_to_s2orc_json.py: converts SPP JSON to the shared S2ORCPaperrepresentation.
ingestion/doc2json/utils/ contains shared parsing helpers:
grobid_util.py: metadata extraction from Grobid XML, bibliography parsing, title/authors/venue/pages/IDs helpers.citation_util.py: citation span utilities and cleanup for Grobid authors.refspan_util.py: replaces reference span placeholders and updates indices.latex_util.py: LaTeX normalization, math removal, and LaTeX-to-XML helpers.soup_utils.py: BeautifulSoup tag manipulation helpers.
User command:
python run.py ingest papers/bert.pdfImplementation path:
run.py cmd_ingest
-> ingestion/scripts/ingest_pdf.sh
-> curl Grobid /api/isalive
-> ingestion/doc2json/grobid2json/process_pdf.py
-> GrobidClient.process_pdf
-> convert_tei_xml_file_to_s2orc_json
-> Paper.release_json
-> paper2code/codes/0_pdf_process.py
-> remove_spans
Outputs:
data/raw_json/bert.jsondata/cleaned_json/bert_cleaned.json
User command:
python run.py discuss "What is masked language modeling?"Implementation path:
run.py cmd_discuss
-> agentswarm.cli cmd_discuss
-> load_papers
-> KeywordRetriever
-> PaperExpertAgent per paper
-> SwarmOrchestrator.run
-> select_agents
-> PaperExpertAgent.answer
-> PaperExpertAgent.critique
-> synthesize
Key behavior:
- Evidence is keyword/BM25 retrieved, not embedding retrieved.
- Expert answers are LLM-generated and constrained by prompt to retrieved paper evidence.
- Final synthesis is deterministic concatenation of claims/critiques.
User command:
python run.py brainstorm "efficient transformers" --papers data/cleaned_json/*.jsonImplementation path:
run.py cmd_brainstorm
-> agentswarm.cli cmd_brainstorm
-> BrainstormOrchestrator.run
-> select relevant paper experts
-> propose_research seed round
-> cross_pollinate with other experts' seeds
-> LLM synthesize agenda
Outputs are printed to stdout rather than written by default.
User command:
python run.py codegen data/cleaned_json/bert_cleaned.jsonImplementation path:
run.py cmd_codegen
-> 1_planning.py
-> 1.1_extract_config.py
-> 2_analyzing.py
-> 3_coding.py
-> copy planning_config.yaml to generated repo
The generated code is only as reliable as the LLM output. The pipeline saves raw prompts/responses extensively so failures can be inspected.
User command:
python run.py codegen data/cleaned_json/bert_cleaned.json --localImplementation path:
run.py cmd_codegen
-> 1_planning_llm.py
-> 1.1_extract_config.py
-> 2_analyzing_llm.py
-> 3_coding_llm.py
The local path requires vllm, transformers, model weights, and suitable GPU resources.
Manual command:
python paper2code/codes/1.2_rag_config.py --output_dir outputs/<paper>This attempts to replace model/dataset strings in planning_config.yaml with concrete Hugging Face IDs.
Manual command:
python paper2code/codes/3.1_coding_sh.py \
--paper_name <paper> \
--gpt_version <model> \
--pdf_json_path data/cleaned_json/<paper>_cleaned.json \
--output_dir outputs/<paper> \
--output_repo_dir outputs/<paper>_repoThis creates outputs/<paper>_repo/reproduce.sh.
Intended manual command shape:
python paper2code/codes/4_debugging.py \
--error_file_name error.txt \
--output_dir outputs/<paper> \
--paper_name <paper> \
--save_num 1 \
--output_repo_dir outputs/<paper>_repoHowever, the script currently does not declare --output_repo_dir, so this feature needs a small parser fix before it can run.
Manual command:
python paper2code/codes/5_reproduce.py \
--paper_name <paper> \
--pdf_json_path data/cleaned_json/<paper>_cleaned.json \
--output_dir outputs/<paper> \
--output_repo_dir outputs/<paper>_repoThis generates a reproduction directory, writes reproduce.py, installs dependencies, runs the reproduction, and saves summary JSON if results are produced.
Manual command:
python paper2code/codes/eval.py \
--paper_name <paper> \
--pdf_json_path data/cleaned_json/<paper>_cleaned.json \
--data_dir paper2code \
--output_dir outputs/<paper> \
--target_repo_dir outputs/<paper>_repo \
--eval_result_dir outputs/eval \
--eval_type ref_free \
--generated_n 8 \
--papercoderThis asks an LLM to rate generated code correctness on a 1-5 scale and write a JSON result.
README.mdhas unresolved merge-conflict markers.HELP.mdappears to contain the intended full documentation.- Several
paper2codescripts accept a model argument but hard-code"tencent/hy3-preview:free"in the actual API call. 4_debugging.pyreferencesargs.output_repo_dirwithout declaring that CLI argument.- Some scripts are not import-safe because they parse args and execute at module top level.
- The OpenRouter-backed scripts require
OPENROUTER_API_KEYand will fail immediately if it is missing. - Cost calculation in
utils.pyis based on a static model-price table and may not reflect OpenRouter pricing. 1.1_extract_config.pyassumes a fixed planning trajectory index for YAML extraction.eval.pycan divide by zero if every model evaluation response fails JSON parsing.eval.pywrites"scroe_lst"instead of"score_lst".paper2codecode extraction relies on fenced code blocks. If the model returns malformed fences, scripts fall back to raw content in some stages.make cleanremoves alloutputs/anddata/raw_json/; use carefully if generated artifacts matter.
To add a new paper-processing feature, prefer using cleaned JSON as the integration boundary. paper_loader.load_paper already normalizes cleaned S2ORC into chunks, and KeywordRetriever works over any loaded Paper.
To add a new agent behavior, extend PaperExpertAgent with a prompt method and add an orchestrator method or new orchestrator class. Keep shared state as dataclasses, following Blackboard and BrainstormBlackboard.
To add a new codegen stage, follow the existing artifact pattern:
- Read
outputs/<paper>/planning_trajectories.json. - Read
outputs/<paper>/planning_config.yaml. - Read/write raw response artifacts into a stage-specific directory.
- Keep generated repo changes inside
outputs/<paper>_repo/.
To make paper2code easier to maintain, the highest-impact refactor would be to extract shared argument parsing, model calling, task-list parsing, and artifact writing into reusable functions instead of duplicating them across numbered scripts.