Skip to content
macayavenPublic

About

Computational literary analysis of Cortázar's Rayuela — latent space discovery via embeddings, LLMs, and TDA

Resources

Stars

0 stars

Watchers

0 watching

Forks

Repository files navigation

Project Rayuela

Pre-commit Type Check Tests Documentation Coverage Coverage Threshold Docstring Threshold

Project Rayuela is a computational literary analysis of Julio Cortazar's Rayuela. The repository contains the full workflow behind the project: text cleanup, embeddings, classical stylometrics, LLM-based style and semantic scoring, permutation tests, interactive visualizations, and article assets for a two-part series.

This workspace is already well past the planning stage. The analysis code and article source markdown are versioned here, while raw corpus files, derived outputs, and generated publishing assets stay local-only so the repo carries reproducible code rather than copyrighted or heavyweight artifacts.

Current State

  • Rayuela parsing outputs live in the local data/ working tree and are intentionally not versioned.
  • The project currently works across four analysis scales: holistic embeddings (A), content-light stylometrics (A'), LLM-perceived style (B'), and narrative DNA semantics (B).
  • The validated semantic instrument is currently 19-dimensional. temporal_clarity was excluded after inter-rater replication with Nemotron 70B showed a rubric polarity failure.
  • The two long-form article sources live at ARTICLE_PART1_MEDIUM.md and ARTICLE_PART2_MEDIUM.md.
  • Interactive Plotly outputs are generated locally into outputs/figures/, and the published GitHub Pages bundle is tracked in docs/.
  • Phase 8 work extends the same methodology to a 10-work Latin American comparison corpus stored in the local data/corpus/ working tree.

Main Findings Captured In The Repo

  • The linear reading order (chapters 1-56) is smoother than random across all four scales.
  • The hopscotch path keeps moderate structure in the holistic embedding space, but that signal fades in the more explicit stylistic and semantic representations.
  • The gap between holistic coherence and explicit semantic disorder is the central interpretive result of the current article series.

Where To Look First

Repository Map

  • src/: core analysis pipeline, including parsing, embeddings, stylometrics, semantic extraction, replication, trajectory analysis, corpus expansion, and figure generation.
  • scripts/: article export, deployment, monitoring, EPUB/PDF parsing, and corpus utility scripts.
  • data/: local-only source texts, parsed Rayuela JSON, calibration passages, and the raw/clean comparison corpus.
  • outputs/: local-only embeddings, semantic and stylistic vectors, audits, review notes, summary JSON, and figure HTML.
  • docs/: published GitHub Pages bundle generated from outputs/figures/.
  • article_images/: local-only PNG exports referenced by the root article markdown.
  • prompts/: prompt templates for semantic and stylistic extraction, plus article review prompts.
  • notebooks/: reserved notebook area; currently empty in this checkout.

Local-Only Asset Policy

  • data/, outputs/, article_images/, and the root ARTICLE_PART*_MEDIUM.html exports are working artifacts and are intentionally ignored by git.
  • docs/ remains versioned because the current custom-domain GitHub Pages site publishes from main:/docs.
  • Keep copyrighted corpus material and regenerated publishing bundles in your own local storage.
  • Use the source markdown and the helper scripts in this repo to regenerate local artifacts when needed.

Workspace Notes

  • CLAUDE.md still describes the project as if it were in Phase 1. The codebase and outputs show a much later state, so treat that file as historical guidance rather than the current status document.
  • The root ARTICLE_PART*_MEDIUM.md files are now the canonical article sources.
  • Most expensive outputs are intentionally local-only, so avoid rerunning the full pipeline unless you actually need refreshed artifacts.

Environment

The repository was built around an ARM64 NVIDIA DGX Spark workflow. The Docker and Compose setup assumes:

  • NVIDIA Container Toolkit / Docker GPU runtime
  • an HF_TOKEN environment variable for model downloads
  • an external Docker volume named vllm-models
  • GPU access for the vLLM services

The analysis container is defined in Dockerfile. Multi-service orchestration lives in docker-compose.yml.

Quick Start

docker compose build rayuela
docker compose up rayuela
docker compose --profile llm up vllm
docker compose --profile llm-nemotron up vllm-nemotron
  • rayuela exposes JupyterLab on port 8888.
  • vllm serves Qwen 3.5 on port 8000.
  • vllm-nemotron serves the Nemotron replication model on port 8000 and should not run at the same time as vllm.
  • The tracked Qwen vLLM command includes --reasoning-parser qwen3, which keeps Qwen reasoning enabled while separating reasoning_content from final content.

For the official DGX Spark Nemotron reasoning lane, use the helper in src/reconstruction_spark_nemotron.py instead of Docker Compose. That path follows NVIDIA's published Spark playbook for Nemotron 3 Nano via llama.cpp, which is currently the cleanest official reasoning-control surface for this workstation.

.venv/bin/python src/reconstruction_spark_nemotron.py print-commands

The helper prints the exact build, model-download, server, and launchcheck-plan commands so the Spark-specific path stays explicit and reproducible.

Common Commands

# Rebuild cleaned Rayuela JSON
docker compose run --rm rayuela python src/parsing.py

# Scale A: chapter embeddings
docker compose run --rm rayuela python src/embeddings.py

# Scale A': classical stylometrics
docker compose run --rm rayuela python src/stylometrics.py

# Scale B': LLM-perceived style
docker compose run --rm rayuela python src/stylistic_extraction.py --resume

# Scale B: narrative DNA semantics
docker compose run --rm rayuela python src/semantic_extraction.py --resume

# Cross-scale analysis and figure generation
docker compose run --rm rayuela python src/scale_comparison.py
docker compose run --rm rayuela python src/article_figures.py

# Prepare GitHub Pages output from generated figure HTML
docker compose run --rm rayuela python scripts/prepare_ghpages.py

Publication Helpers

  • scripts/prepare_ghpages.py: copies selected figure HTML from the local outputs/figures/ tree into the local docs/ publishing bundle and swaps inline Plotly bundles for the CDN version.
  • scripts/md_to_html.py: converts the root Medium article markdown files into self-contained HTML. The helper depends on the Python markdown package, which is included in requirements-dev.txt for local QA but is not preinstalled in the base analysis container. Install it before running the export helper inside Docker with docker compose run --rm rayuela pip install markdown, or locally with python3 -m pip install markdown.
  • scripts/export_article_pngs.py: exports static PNGs from Plotly figures for article use. It depends on kaleido; install it the same way with docker compose run --rm rayuela pip install kaleido or python3 -m pip install kaleido.

Quality Gates

python3 -m pip install -r requirements-dev.txt
python3 -m pre_commit install
python3 -m pre_commit run --all-files
python3 -m mypy
python3 -m pytest
python3 -m interrogate src/parsing.py src/project_config.py src/reconstruction_contract.py scripts/md_to_html.py scripts/prepare_ghpages.py

Reconstruction Contract

Part 3 reconstruction runs are rooted under outputs/reconstruction/. Phase 0 establishes an immutable run policy:

  • each run writes its manifest to outputs/reconstruction/runs/<run_id>/manifest.json
  • each manifest is mirrored to outputs/reconstruction/manifests/<run_id>.json
  • failed runs are retained in place and their run IDs are never reused
  • all manifest paths are stored project-relative so the run can be replayed on another checkout

The required manifest fields are:

  • schema_version
  • run_id
  • phase
  • status
  • created_at
  • updated_at
  • git_sha
  • model_id
  • prompt_template_id
  • seed
  • config_hash
  • corpus_manifest
  • split_manifest
  • paths
  • config_payload
  • error_message

Phase 0 dry run:

python3 src/reconstruction_contract.py --run-id phase0-dry-run --phase phase-0-quality-envelope

Reconstruction Audit

Phase 1 adds src/reconstruction_audit.py to verify that the cleaned comparison corpus and its derived outputs remain operationally decoupled from stale or orphaned artifacts.

Targeted Phase 1 verification:

python3 -m pytest tests/test_reconstruction_audit.py -q
python3 src/reconstruction_audit.py

Primary outputs:

  • outputs/corpus/corpus_metadata.json: work- and author-level segment counts from the cleaned corpus
  • outputs/reconstruction/analysis/corpus_sync_audit.json: machine-readable synchronization report

Reconstruction Metrics

Phase 2 adds src/reconstruction_metrics.py to lock the measurement contract before any rewrite generation starts.

The module provides:

  • typed stylometric and semantic baseline reports under outputs/reconstruction/baselines/
  • deterministic rewrite scoring against source semantics and target stylistic envelopes
  • identity, copy-source, and random-target control diagnostics

Targeted Phase 2 verification:

python3 -m pytest tests/test_reconstruction_metrics.py -q
python3 src/reconstruction_metrics.py

The CLI intentionally refuses to lock live baselines if the Phase 1 corpus outputs are stale or incomplete.

Primary outputs:

  • outputs/reconstruction/baselines/stylometric_baseline.json
  • outputs/reconstruction/baselines/semantic_baseline.json
  • outputs/reconstruction/baselines/control_diagnostics.json

Reconstruction Dataset

Phase 3 adds src/reconstruction_dataset.py to lock the pilot design before prompt baselines or training.

The module provides:

  • deterministic 128-256 word source windows with chapter-level reference vectors
  • chapter-aware train/validation/test split manifests with automated leakage checks
  • target work envelopes with explicit train-split provenance
  • serialized success criteria for the locked pilot under operational decoupling language

Targeted Phase 3 verification:

python3 -m pytest tests/test_reconstruction_dataset.py -q
python3 src/reconstruction_dataset.py

Primary outputs:

  • outputs/reconstruction/pilots/source_windows.json
  • outputs/reconstruction/pilots/target_envelopes.json
  • outputs/reconstruction/pilots/split_manifest.json
  • outputs/reconstruction/pilots/success_criteria.json

Reconstruction Baselines

Phase 4 adds src/reconstruction_baselines.py to establish a prompt-only baseline before any fine-tuning.

The module provides:

  • versioned identity, paraphrase, style-shift, and revise prompt templates
  • traceable generate-score-revise histories with raw responses and per-iteration scores
  • immutable Phase 4 run manifests under outputs/reconstruction/runs/
  • baseline case, summary, and markdown report artifacts for the locked pilot

Targeted Phase 4 verification:

python3 -m pytest tests/test_reconstruction_baselines.py -q
python3 src/reconstruction_baselines.py --run-id phase4-dry-run --dry-run --max-cases 2

If the serving stack is exposing parser-separated reasoning, record that expectation in the run config:

python3 src/reconstruction_baselines.py --run-id phase4-live-parser-check --max-cases 2 --reasoning-parser qwen3 --generation-max-tokens 2048 --semantic-generation-max-tokens 2048

--generation-max-tokens is now a first-class experiment parameter. When Qwen reasoning is kept enabled behind --reasoning-parser qwen3, the model may consume a large fraction of the budget in hidden reasoning before it emits final content. Phase 4 therefore records the token budget in the run manifest and now fails fast if the serving stack returns reasoning without a final passage, instead of silently scoring an empty rewrite.

The same parser edge applies to the semantic evaluator. Phase 4 now also exposes --semantic-generation-max-tokens, forwards that budget into the structured semantic-extraction calls, and fails fast if the evaluator receives hidden reasoning without the final JSON payload.

The default rewrite templates are now style_shift_v2 / revise_v2. They keep hidden reasoning enabled but tighten the visible-output contract by explicitly requiring the first visible character to belong to the passage itself, with no headings, labels, markdown, or explanatory text.

For the official DGX Spark Nemotron lane, use src/reconstruction_spark_nemotron.py to prepare a bounded llama.cpp launchcheck against the OpenAI-compatible API that NVIDIA documents for Nemotron 3 Nano. This keeps the reasoning-control surface aligned with Spark guidance instead of relying on ad hoc visible-marker scraping.

Primary outputs:

  • outputs/reconstruction/runs/<run_id>/prompt_baseline_cases.json
  • outputs/reconstruction/runs/<run_id>/prompt_baseline_summary.json
  • outputs/reconstruction/runs/<run_id>/prompt_baseline_report.md

The live prompt-control path expects the local Qwen vLLM service. If the host is currently serving another model (for example the Nemotron replication endpoint), use --dry-run to validate the Phase 4 contract without broadening the claim.

Reconstruction Training

Phase 5 adds src/reconstruction_train.py and src/reconstruction_infer.py to prepare the training envelope before real adapter fine-tuning.

The DGX Spark adapter path follows the official NVIDIA PyTorch/Unsloth playbooks with Rayuela-specific run contracts; see plans/phase5_dgx_spark_finetune_playbook.md. For an interactive DGX Spark fine-tuning container:

scripts/bootstrap_dgx_spark_finetune.sh pytorch

The modules provide:

  • deterministic identity_smoke and contract_smoke dataset assembly from the locked pilot split
  • bounded seq2seq_smoke training for verifying the real fine-tuning execution path
  • immutable run directories with training config, tokenizer config, metrics, and split-specific JSONL training datasets, and checkpoint metadata
  • placeholder-adapter handling that is explicit in metadata and rejected by inference when a real adapter is not present yet

Targeted Phase 5 verification:

python3 -m pytest tests/test_reconstruction_training.py -q
python3 src/reconstruction_train.py --run-id phase5-smoke --split-manifest-path outputs/reconstruction/pilots/split_manifest.json --target-envelopes-path outputs/reconstruction/pilots/target_envelopes.json
python3 src/reconstruction_train.py --run-id phase5-contract-smoke --dataset-mode contract_smoke --split-manifest-path outputs/reconstruction/pilots/split_manifest.json --target-envelopes-path outputs/reconstruction/pilots/target_envelopes.json
python3 src/reconstruction_train.py --run-id phase5-seq2seq-smoke --training-mode seq2seq_smoke --dataset-mode contract_smoke --model-id hf-internal-testing/tiny-random-t5 --max-steps 1 --max-train-examples 2 --max-eval-examples 1 --split-manifest-path outputs/reconstruction/pilots/split_manifest.json --target-envelopes-path outputs/reconstruction/pilots/target_envelopes.json
python3 src/reconstruction_train.py --run-id phase5-lora-smoke --training-mode lora_sft --dataset-mode contract_smoke --model-id hf-internal-testing/tiny-random-LlamaForCausalLM --max-steps 1 --max-train-examples 2 --max-source-length 128 --lora-rank 4 --lora-alpha 8 --split-manifest-path outputs/reconstruction/pilots/split_manifest.json --target-envelopes-path outputs/reconstruction/pilots/target_envelopes.json

Primary outputs:

  • outputs/reconstruction/runs/<run_id>/training_config.json
  • outputs/reconstruction/runs/<run_id>/training_metrics.json
  • outputs/reconstruction/runs/<run_id>/training_dataset/{train,val,test}.jsonl
  • outputs/reconstruction/runs/<run_id>/checkpoint_metadata.json

Style-transfer adapter work starts by distilling scored Phase 4 teacher generations into supervised JSONL examples. The generated passages remain under ignored outputs/; tracked files only contain the code and plan:

python3 src/reconstruction_style_distill.py --dataset-id phase5-style-distill --teacher-cases-path outputs/reconstruction/runs/<teacher_run>/prompt_baseline_cases.json --min-weighted-objective 0.14
python3 src/reconstruction_train.py --run-id phase5-style-distilled-scaffold --dataset-mode style_transfer_distilled --training-dataset-dir outputs/reconstruction/style_distill/phase5-style-distill/training_dataset

Contract probes for LoRA adapters should run inside the same pinned DGX Spark container lane:

python3 src/reconstruction_infer.py --checkpoint-metadata-path outputs/reconstruction/runs/<run_id>/checkpoint_metadata.json --probe-split-path outputs/reconstruction/runs/<run_id>/training_dataset/val.jsonl --contract-probe-output-path outputs/reconstruction/runs/<run_id>/contract_probe.json --probe-examples 8
python3 src/reconstruction_infer.py --checkpoint-metadata-path outputs/reconstruction/runs/<run_id>/checkpoint_metadata.json --probe-split-path outputs/reconstruction/runs/<run_id>/training_dataset/val.jsonl --contract-probe-output-path outputs/reconstruction/runs/<run_id>/contract_probe_base.json --probe-examples 8 --base-only

Reconstruction Analysis

Phase 6 adds src/reconstruction_analysis.py to turn immutable Phase 4+ run artifacts into synthesis-ready summaries.

The module provides:

  • complete cross-run case aggregation from prompt_baseline_cases.json
  • explicit failure-mode labeling from the saved scoring contract
  • a compact experiment reading guide that explains how to interpret one batch
  • source-side bias slices by work and author
  • concrete output examples with source/output excerpts and reasoning-leak flags
  • pairwise run-comparison summaries over overlapping case identities
  • provenance comparability gates over run invariants such as model, prompt, corpus/pilot artifacts, backend, and generation seed
  • paired bootstrap intervals for mean run-to-run objective deltas
  • explicit whether the paired mean-delta interval excludes zero
  • failure-transition summaries that separate persistent, resolved, and introduced failure labels across overlapping cases
  • explicit promotion recommendations that stay separate from scheduler keep/discard decisions
  • article-ready summary/report artifacts and a close-reading queue with stable run_id links
  • optional W&B analysis logging for aggregate counts, failure-mode totals, run-summary tables, run-comparison tables, failure-transition tables, promotion tables, provenance tables, reading-guide tables, output-example tables, close-reading queues, and attached analysis artifacts

Targeted Phase 6 verification:

.venv/bin/python -m pytest tests/test_reconstruction_analysis.py -q
.venv/bin/python src/reconstruction_analysis.py --wandb-project rayuela --wandb-mode offline

Promotion criteria are configurable at analysis time, for example:

.venv/bin/python src/reconstruction_analysis.py \
  --schedule-summary-path outputs/reconstruction/analysis/schedules/<schedule_id>/schedule_summary.json \
  --schedule-run-selection nonfailed \
  --promotion-min-overlapping-cases 4 \
  --promotion-min-mean-delta 0.005 \
  --promotion-min-median-delta 0.0 \
  --promotion-min-non-negative-share 0.5 \
  --promotion-max-failure-case-delta 0 \
  --promotion-require-comparable-provenance

Primary outputs:

  • outputs/reconstruction/analysis/reconstruction_analysis_summary.json
  • outputs/reconstruction/analysis/reconstruction_analysis_report.md
  • outputs/reconstruction/analysis/reconstruction_article_inputs.json

Guided Scheduler

src/reconstruction_scheduler.py adds a finite experiment scheduler inspired by the keep/discard discipline of Andrej Karpathy's autoresearch, but adapted to the reconstruction run contract.

The module provides:

  • JSON-defined experiment queues with explicit commands, timeouts, and metric paths
  • append-only scheduler results under outputs/reconstruction/analysis/schedules/
  • automatic keep / discard / failed decisions based on a metric key extracted from produced run artifacts
  • schedule summaries that record kept/discarded/failed run_ids for direct handoff into Phase 6 analysis
  • optional W&B per-experiment logging for run metadata, scheduler decisions, extracted reconstruction metrics, and attached immutable run/scheduler artifacts

Targeted scheduler verification:

.venv/bin/python -m pytest tests/test_reconstruction_scheduler.py -q
.venv/bin/python src/reconstruction_scheduler.py --plan-path plans/reconstruction_guided_schedule.example.json --wandb-project rayuela --wandb-mode offline
.venv/bin/python src/reconstruction_analysis.py --schedule-summary-path outputs/reconstruction/analysis/schedules/<schedule_id>/schedule_summary.json --schedule-run-selection nonfailed --wandb-project rayuela --wandb-mode offline --wandb-group <schedule_id>

W&B logging is operationally decoupled from experiment execution. The scheduler logs one run per planned experiment with the explicit keep/discard/failed decision, the chosen advancement metric, and attached local artifacts so the research loop can compare runs without rereading raw filesystem state. The analysis step logs aggregate failure counts, run summaries, run comparisons, comparability gates, paired uncertainty intervals, failure-transition tables, promotion recommendations, reading guides, concrete output examples, and close-reading queues so the third article can cite stable synthesis outputs rather than hand-maintained notes. Those paired intervals should be read as approximate uncertainty summaries, not as a stronger significance claim than the saved case table supports.

This separation is deliberate: scheduler decisions are local operational decisions about one finite queue, while promotion recommendations are research-facing judgments derived later from the saved case table and explicit criteria.

Live prompt baselines now pass the configured reconstruction seed through the OpenAI-compatible backend request surface. That does not make every upstream serving stack perfectly reproducible, but it closes the avoidable gap where the manifest claimed a seed while the generation call ignored it.

Phase 4 also now trims obvious post-passage meta commentary such as Nota:, Note:, Explicación:, or Changes made: suffixes before scoring the candidate rewrite. That keeps the weighted objective focused on the rewritten passage itself while preserving audit metadata in the saved iteration record and reporting the trimmed-case count in the Phase 4 summary/report.

For detached execution, use src/reconstruction_launcher.py or the thin shell shims in scripts/launch_reconstruction_schedule.sh, scripts/status_reconstruction_schedule.sh, and scripts/stop_reconstruction_schedule.sh. The launcher validates the plan, checks the backend and required environment keys, writes non-secret launch metadata, starts a tmux session keyed by schedule_id, and keeps scheduler.log and analysis.log separate under the schedule directory.

Minimal plan shape:

{
  "schedule_id": "guided-20260310a",
  "experiments": [
    {
      "experiment_id": "baseline-a",
      "run_id": "phase4-live-20260310a",
      "phase": "phase-4-prompt-baselines",
      "command": [
        ".venv/bin/python",
        "src/reconstruction_baselines.py",
        "--run-id",
        "{run_id}"
      ],
      "timeout_seconds": 3600,
      "metric_path_template": "outputs/reconstruction/runs/{run_id}/prompt_baseline_summary.json",
      "metric_key": "controls.style_shift.mean_weighted_objective",
      "higher_is_better": true
    }
  ]
}

Primary outputs:

  • outputs/reconstruction/analysis/schedules/<schedule_id>/schedule_results.jsonl
  • outputs/reconstruction/analysis/schedules/<schedule_id>/schedule_summary.json

A ready-to-edit example lives at plans/reconstruction_guided_schedule.example.json. Treat scheduler plan files as trusted executable specifications: the scheduler passes the declared command directly to subprocess.run() and does not sandbox or validate contributor-authored plans.

For an unattended Qwen hidden-reasoning batch bounded to roughly 10 hours, use plans/reconstruction_guided_schedule.autonomous-10h-20260430.json. It runs seeded 1-, 2-, and 3-iteration comparisons over the same first four pilot cases, then spends the remaining budget on a 6-case 2-iteration confirmation run. Launch it only when the Qwen parser-enabled endpoint is already serving at http://localhost:8000/v1:

scripts/launch_reconstruction_schedule.sh \
  --plan-path plans/reconstruction_guided_schedule.autonomous-10h-20260430.json \
  --wandb-project rayuela \
  --wandb-mode offline

scripts/status_reconstruction_schedule.sh \
  --schedule-id guided-phase4-autonomous-10h-20260430a

The launcher automatically runs Phase 6 analysis with --schedule-run-selection nonfailed after the scheduler finishes, so the article-facing output should be read from the schedule analysis directory rather than reconstructed manually from individual run folders.

Corpus Extension

The repo is no longer only about Rayuela. src/corpus_cleanup.py, src/corpus_stylometrics.py, and src/corpus_semantic.py extend the same methods to a broader Latin American corpus stored in data/corpus/.

Data Note

Raw and derived literary texts belong in the local-only data/ working tree, not in version control. Before redistributing the project or reusing the source texts elsewhere, verify that you have the rights to do so.

About

Computational literary analysis of Cortázar's Rayuela — latent space discovery via embeddings, LLMs, and TDA

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages