Metadata-Version: 2.4
Name: sparcllm
Version: 0.1.3
Summary: SPARC-LLM framework with sklearn-inspired object API
Author: SPARC-LLM Contributors
License: Apache-2.0
Project-URL: Repository, https://github.com/mustashot/adaptive-llms
Requires-Python: >=3.10
Description-Content-Type: text/markdown
License-File: LICENSE
Requires-Dist: torch
Requires-Dist: transformers
Requires-Dist: hydra-core
Requires-Dist: peft
Provides-Extra: dev
Requires-Dist: pytest>=8.0; extra == "dev"
Requires-Dist: pre-commit>=3.7; extra == "dev"
Requires-Dist: ruff>=0.6.8; extra == "dev"
Dynamic: license-file

# SPARC-LLM Framework

SPARC-LLM is now organized as a reusable Python framework (scikit-learn inspired) with object-oriented APIs, typed configs, service layers, and a stable CLI.

![CI](https://img.shields.io/badge/CI-GitHub_Actions-2088FF)
![Tests](https://img.shields.io/badge/tests-pytest-0A9EDC)
![Lint](https://img.shields.io/badge/lint-ruff-46A758)
![Versioning](https://img.shields.io/badge/versioning-SemVer%20x.y.z-F59E0B)
![Package](https://img.shields.io/badge/package-sparcllm-7C3AED)

---

## Functional overview (what it does)

This framework helps you run **continual learning for LLMs** in a clean, reusable way:

- decompose a base model in SVD space,
- train an SVF policy to preserve important capabilities,
- inject new knowledge with orthogonal LoRA,
- evaluate base/SVF/LoRA/hybrid variants on knowledge + downstream tasks.

### Typical user scenarios

- **Research iteration**: quickly test a new knowledge dataset while tracking skill retention.
- **Benchmarking**: compare `none` vs `svf_only` vs `lora_only` vs `both`.
- **Pipeline integration**: call framework objects from another Python project.
- **Progressive industrialization**: keep research code, but expose a clean API for tests and automation.

### End-to-end functional flow

```mermaid
flowchart LR
    A[Base model] --> B[SVD decomposition]
    B --> C[SVF training]
    C --> D[Orthogonal LoRA training]
    D --> E[Merge modes: none/svf/lora/both]
    E --> F[Knowledge + downstream evaluation]
```

### Quick execution paths

- **Python-first**: use `SPARCExperiment.run(...)` for orchestration.
- **CLI-first**: use `sparcllm run ...` for end-to-end execution.
- **Stage-by-stage**: use `sparcllm decompose`, `train-svf`, `train-orthogonal`, `evaluate`.

---

## Explainability first (non-technical)

If you are not deeply technical, think about this framework as a way to **teach new knowledge to an AI model** without making it forget what it already knows.

### Intuition of each method

- **SVD (decomposition)**  
  The model is split into reusable building blocks, like separating a recipe into ingredients.  
  Why: it makes later updates more controlled and easier to analyze.

- **SVF (stability policy training)**  
  The framework learns which internal directions are important to keep stable.  
  Why: this protects useful existing skills (reasoning, coding patterns, etc.).

- **Orthogonal LoRA (knowledge injection)**  
  LoRA adds new knowledge through lightweight adapters. “Orthogonal” means the update is guided to avoid conflicting with protected directions.  
  Why: inject new facts while reducing catastrophic forgetting.

- **Merge + evaluation**  
  You can compare multiple versions of the model:
  - base only,
  - SVF only,
  - LoRA only,
  - SVF + LoRA together.  
  Why: measure what really works before deployment.

### What happens when you run one full command

When you launch:

```bash
sparcllm run \
  --with-svd \
  --with-svf \
  --with-orthogonal \
  --with-eval \
  --model-id meta-llama/Llama-3.1-8B-Instruct \
  --base-model-key llama3i8b \
  --svf-checkpoint-path svf_adapters/gsm_131224_policy_params.pt \
  --knowledge-dataset-file knowledge_injection_data/500UK_3rHK.csv \
  --svf-params-path svf_adapters/gsm_131224_policy_params.pt \
  --svd-params-path svd_parameters/llama3.1_decomposed_params.pt \
  --lora-adapter-path results_orthogonal/<run>/lora_epoch_10 \
  --eval-mode both \
  --task-names gsm8k arc
```

the framework does this, in order:

1. decomposes the base model (`--with-svd`),
2. trains/uses SVF to mark stability directions (`--with-svf`),
3. trains orthogonal LoRA for new knowledge (`--with-orthogonal`),
4. evaluates the final setup (`--with-eval`) on selected tasks.

### Human explanation of key flags

- `--model-id ...`  
  Which foundation model you start from.

- `--base-model-key llama3i8b`  
  Which internal preset/config to use for training defaults.

- `--svf-checkpoint-path ...`  
  Previously learned SVF stability knowledge (what should be preserved).

- `--knowledge-dataset-file ...`  
  The file containing new knowledge you want to teach.

- `--svf-params-path ...` and `--svd-params-path ...`  
  Technical files needed to rebuild and apply stability-aware modifications.

- `--lora-adapter-path ...`  
  The LoRA adapter produced by orthogonal training (the “new knowledge module”).

- `--eval-mode both`  
  Evaluate the combined model (SVF + LoRA).  
  Other modes let you compare components separately.

- `--task-names gsm8k arc`  
  Which benchmarks to run for quality checks.

### How to read results simply

- If knowledge metrics improve and baseline skills stay stable, your run is successful.
- If knowledge improves but old skills drop strongly, increase preservation constraints (SVF/orthogonal settings).
- If everything drops, reduce training aggressiveness (learning rate, epochs, adapter rank, batch size).

---

## What you get

- clean Python objects for each stage (`fit`, `evaluate`, `run`)
- typed configs with dataclasses
- layered architecture (`datasets`, `models`, `trainers`, `evaluators`)
- one orchestrator object for full experiments
- installable package with CLI (`sparcllm`)
- tests + pre-commit baseline for quality

---

## Installation

```bash
python3.11 -m venv .venv
source .venv/bin/activate
pip install --upgrade pip
pip install -r requirements.txt
cd evaluation/fishfarm && pip install -e . && cd ../..
pip install -e .
```

Auth + env:

```bash
hf auth login --token YOURTOKEN
export HF_TOKEN=YOURTOKEN
export CUDA_VISIBLE_DEVICES=0
```

---

## Python API examples

### 1) Full workflow with `SPARCExperiment`

```python
from sparcllm import (
    MergeConfig,
    OrthogonalTrainingConfig,
    SPARCExperiment,
    SVDConfig,
    SVFTrainingConfig,
)

exp = SPARCExperiment(project_root=".")
report = exp.run(
    svd_cfg=SVDConfig(
        model_id="meta-llama/Llama-3.1-8B-Instruct",
        output_file="svd_parameters/llama3.1_decomposed_params.pt",
    ),
    svf_cfg=SVFTrainingConfig(
        base_model_key="llama3i8b",
        mode="training",
        optimization="reinforce",
    ),
    orthogonal_cfg=OrthogonalTrainingConfig(
        base_model_key="llama3i8b",
        svf_checkpoint_path="svf_adapters/gsm_131224_policy_params.pt",
        knowledge_dataset_file="knowledge_injection_data/500UK_3rHK.csv",
        run_name="exp_v2",
    ),
    merge_cfg=MergeConfig(
        base_model_id="meta-llama/Llama-3.1-8B-Instruct",
        lora_adapter_path="results_orthogonal/<run>/lora_epoch_10",
        svf_params_path="svf_adapters/gsm_131224_policy_params.pt",
        svd_params_path="svd_parameters/llama3.1_decomposed_params.pt",
        mode="both",
        task_names=["gsm8k", "arc"],
    ),
)

print(report.executed_stages, report.metrics)
```

### 2) Estimator-style partial execution

```python
from sparcllm import MergeConfig, ModelMergerEvaluator

metrics = ModelMergerEvaluator(
    MergeConfig(
        base_model_id="meta-llama/Llama-3.1-8B-Instruct",
        mode="none",
        task_names=["gsm8k"],
        skip_knowledge_eval=True,
    )
).evaluate()

print(metrics)
```

---

## CLI usage

### Stage-by-stage

```bash
sparcllm decompose \
  --model-id meta-llama/Llama-3.1-8B-Instruct \
  --output-file svd_parameters/llama3.1_decomposed_params.pt
```

```bash
sparcllm train-svf \
  --base-model-key llama3i8b \
  --mode training \
  --optimization reinforce
```

```bash
sparcllm train-orthogonal \
  --base-model-key llama3i8b \
  --svf-checkpoint-path svf_adapters/gsm_131224_policy_params.pt \
  --knowledge-dataset-file knowledge_injection_data/500UK_3rHK.csv \
  --num-epochs 10 \
  --batch-size 8 \
  --lr 2e-4 \
  --lambda-ip 0 \
  --lambda-ortho 100
```

```bash
sparcllm evaluate \
  --base-model-id meta-llama/Llama-3.1-8B-Instruct \
  --mode both \
  --lora-adapter-path results_orthogonal/<run>/lora_epoch_10 \
  --svf-params-path svf_adapters/gsm_131224_policy_params.pt \
  --svd-params-path svd_parameters/llama3.1_decomposed_params.pt \
  --knowledge-dataset-file knowledge_injection_data/500UK_3rHK.csv \
  --task-names gsm8k arc
```

### One command orchestration

```bash
sparcllm run \
  --with-svd \
  --with-svf \
  --with-orthogonal \
  --with-eval \
  --model-id meta-llama/Llama-3.1-8B-Instruct \
  --base-model-key llama3i8b \
  --svf-checkpoint-path svf_adapters/gsm_131224_policy_params.pt \
  --knowledge-dataset-file knowledge_injection_data/500UK_3rHK.csv \
  --svf-params-path svf_adapters/gsm_131224_policy_params.pt \
  --svd-params-path svd_parameters/llama3.1_decomposed_params.pt \
  --lora-adapter-path results_orthogonal/<run>/lora_epoch_10 \
  --eval-mode both \
  --task-names gsm8k arc
```

---

## Plot and visualize results

You can now generate plots directly from output files with the framework CLI.

### 1) Plot orthogonal training losses

Input file: `training_history.json` generated by orthogonal training.

```bash
sparcllm plot \
  --kind orthogonal_history \
  --input-file results_orthogonal/<run>/training_history.json \
  --output-dir plots
```

### 2) Plot LoRA training curves (Unknown/HighlyKnown/Loss)

Input file: `training_log.json` (JSONL or concatenated JSON objects).

```bash
sparcllm plot \
  --kind lora_training_log \
  --input-file results_sft/<run>/training_log.json \
  --output-dir plots
```

### 3) Plot merge/evaluation metrics

Input file: `results.json` from merge/eval runs.

```bash
sparcllm plot \
  --kind merge_results \
  --input-file results_merge_adapters/<timestamp>/results.json \
  --output-dir plots
```

The command prints the output image path(s), so you can open them directly.

---

## Internal architecture

```mermaid
flowchart LR
    A[Config objects] --> B[Workflow object]
    B --> C[Training services]
    B --> D[Model composition service]
    B --> E[Evaluation engine]
    C --> F[Hydra research entrypoints]
    D --> G[Base/SVF/LoRA merged model]
    E --> H[Task metrics + report]
```

### Package map

```text
sparcllm/
  __init__.py
  base.py                 # BaseEstimator
  config.py               # dataclass configs
  runner.py               # command execution utility
  pipeline.py             # sklearn-like stage estimators
  workflows.py            # SPARCExperiment + ExperimentReport
  cli.py                  # `sparcllm` command line
  datasets/registry.py    # task registry/factory
  models/composer.py      # base/SVF/LoRA composition
  trainers/stages.py      # SVD/SVF/Orthogonal services
  evaluators/engine.py    # knowledge + downstream evaluation engine
examples/
  framework_quickstart.py
tests/
  test_base_estimator.py
  test_config_and_pipeline.py
```

---

## Main objects and what they do

### Config objects (`sparcllm.config`)

- `SVDConfig`: controls decomposition stage.
- `SVFTrainingConfig`: controls SVF policy training.
- `OrthogonalTrainingConfig`: controls orthogonal LoRA training.
- `MergeConfig`: controls merge + evaluation stage.
- `ComposeRequest`: internal model-composition request.

### Estimator objects (`sparcllm.pipeline`)

- `SVDDecomposer.fit()`
- `SVFTrainer.fit()`
- `OrthogonalLoRATrainer.fit()`
- `ModelMergerEvaluator.evaluate()`
- `SPARCPipeline.run(...)` (compose multiple stages)

### Workflow object (`sparcllm.workflows`)

- `SPARCExperiment.run(...)` returns an `ExperimentReport`:
  - `executed_stages`
  - `notes`
  - `metrics`

This is the recommended object for production orchestration.

---

## Object interaction diagram

```mermaid
flowchart TD
    C1[SVDConfig]
    C2[SVFTrainingConfig]
    C3[OrthogonalTrainingConfig]
    C4[MergeConfig]

    E[SPARCExperiment]
    R[CommandRunner]
    S1[SVDService]
    S2[SVFTrainingService]
    S3[OrthogonalTrainingService]
    M[ModelComposer]
    EV[EvaluationEngine]
    TR[TaskRegistry]
    REP[ExperimentReport]

    C1 --> E
    C2 --> E
    C3 --> E
    C4 --> E

    E --> R
    E --> S1
    E --> S2
    E --> S3
    E --> EV

    S1 --> R
    S2 --> R
    S3 --> R

    EV --> M
    EV --> TR
    EV --> REP
    E --> REP
```

### How to read this diagram

- `SPARCExperiment` is the top-level orchestrator.
- Config objects define what each stage should do.
- Training services execute stage logic (currently through managed command calls).
- `EvaluationEngine` composes models via `ModelComposer`, resolves tasks via `TaskRegistry`, and returns metrics.
- `ExperimentReport` is the final structured output for executed stages, notes, and results.

---

## About scripts in `scripts/`

Legacy shell scripts are still present for reproducibility of older runs.  
For new integrations, prefer:

- Python API via `sparcllm` objects
- CLI via `sparcllm ...`

This keeps your codebase cleaner and easier to test.

---

## Local quality gates

```bash
pip install -e ".[dev]"
pre-commit install
pre-commit run --all-files
pytest
```

---

## CI/CD and Python artifact publishing

This repository now includes GitHub Actions workflows to validate quality and prepare package publishing.

### Workflows added

- `.github/workflows/ci.yml`
  - runs lint (`ruff`) and unit tests (`pytest`) on PRs and key branches
- `.github/workflows/release.yml`
  - resolves version bump from labels (or manual input),
  - bumps package version in `pyproject.toml`,
  - builds wheel/sdist (`python -m build`),
  - uploads build artifacts,
  - supports controlled publishing target: `none`, `pypi`, `artifactory`, `both`,
  - uses Trusted Publisher (OIDC) for PyPI publication.

### Versioning strategy (x.y.z)

The release workflow uses semantic versioning:

- **x = major** (breaking change)
- **y = minor** (new backward-compatible features)
- **z = patch** (bug fixes)

Label mapping used by the workflow:

- **major**: `x`, `major`, `semver:major`
- **minor**: `y`, `minor`, `semver:minor`
- **patch**: `z`, `bug`, `bugfix`, `patch`, `semver:patch`

If no label is detected, default bump is `patch`.

### Branch strategy for now (before main)

The release workflow is configured to run on:

- push to `code_refactoring`
- push to `main`/`master`
- manual `workflow_dispatch`

Recommended while preparing:

- trigger manual runs with `publish_target=none` to validate build/versioning only,
- then use `publish_target=pypi` when you are ready to publish.

**Important:** on **`push`**, the workflow has **no** `publish_target` input, so it always stays **`none`**. The PyPI and Artifactory steps are **skipped on purpose** (only build, artifact upload, and version commit run). That is why you may see a “skipped” icon on those steps after a push. To actually upload to PyPI, run **Release Package** manually and set **`publish_target`** to **`pypi`** (or **`both`**).

### Required secrets for publishing

To publish to your Python artifact repository (Artifactory), set:

- `ARTIFACTORY_PYPI_URL`
- `ARTIFACTORY_PYPI_USERNAME`
- `ARTIFACTORY_PYPI_PASSWORD`

For public PyPI (Trusted Publisher), no API token secret is required when OIDC is configured in PyPI.
If publishing target is `none`, workflow still builds and stores artifacts in GitHub Actions.

The **commit version bump** step runs only on `push` and uses the identity `github-actions[bot]` so `git commit` works on the runner (no extra secrets needed for that).

### Install after publication

Once published in your artifact repository, you can install with:

```bash
pip install sparcllm
```

If your registry is private, configure pip index/auth first (according to your Artifactory or private PyPI setup).

---

## Cleanup performed

- framework now uses explicit service layers for datasets/models/trainers/evaluators
- reusable workflow object added (`SPARCExperiment`)
- CLI expanded to cover stage-by-stage and end-to-end runs
- legacy shell scripts kept for reproducibility, framework API prioritized for new usage

This is now a solid base for next steps (deeper tests and artifact publishing pipeline).
