Metadata-Version: 2.4
Name: code2lora
Version: 0.1.0
Summary: Connect a git repo to the Code2LoRA-GRU hypernetwork and incrementally regenerate a LoRA adapter every few commits.
Author: code2lora
License: Apache-2.0
Project-URL: Homepage, https://github.com/lilianahotsko/code2lora_framework
Project-URL: Repository, https://github.com/lilianahotsko/code2lora_framework
Keywords: lora,peft,git,hypernetwork,qwen,code,adapter
Classifier: Programming Language :: Python :: 3
Classifier: License :: OSI Approved :: Apache Software License
Classifier: Operating System :: OS Independent
Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
Requires-Python: >=3.9
Description-Content-Type: text/markdown
License-File: LICENSE
Requires-Dist: torch>=2.0
Requires-Dist: transformers>=4.40
Requires-Dist: huggingface_hub>=0.23
Requires-Dist: safetensors>=0.4
Requires-Dist: numpy>=1.23
Requires-Dist: tomli>=2.0; python_version < "3.11"
Provides-Extra: infer
Requires-Dist: peft>=0.10; extra == "infer"
Requires-Dist: accelerate>=0.28; extra == "infer"
Provides-Extra: watch
Requires-Dist: watchdog>=3.0; extra == "watch"
Provides-Extra: dev
Requires-Dist: pytest>=7.0; extra == "dev"
Requires-Dist: ruff>=0.4; extra == "dev"
Provides-Extra: all
Requires-Dist: peft>=0.10; extra == "all"
Requires-Dist: accelerate>=0.28; extra == "all"
Requires-Dist: watchdog>=3.0; extra == "all"
Dynamic: license-file

# code2lora

Connect a git repository to the pre-trained **Code2LoRA-GRU** hypernetwork and
incrementally regenerate a LoRA adapter for your code model **every few commits**
— no per-repo training required.

`code2lora` streams your commit history through a frozen embedder and a trained
GRU hypernetwork. The GRU's hidden state is persisted between runs, so each
update only has to process the commits that arrived since last time. Every *N*
commits it emits a standard [PEFT](https://github.com/huggingface/peft) adapter
you can load on top of the base model.

```mermaid
flowchart LR
  newCommits["New commits since cursor"] --> embed["Qwen3 diff embedding"]
  embed --> gruStep["GRU step (per commit)"]
  state["Persisted hidden state + cursor SHA"] --> gruStep
  gruStep --> state
  gruStep -->|"every N commits"| head["LoRA head -> A,B per type"]
  head --> exporter["PEFT adapter (safetensors + config)"]
  exporter --> consume["peft.PeftModel / generate"]
```

## Install

```bash
pip install code2lora            # core: stream commits + export adapters
pip install 'code2lora[infer]'   # + run generation with the adapter (peft)
pip install 'code2lora[all]'     # + watcher extra
```

The first run downloads the base model config and the hypernetwork checkpoint
(`gru_head.best.pt`) from the Hugging Face Hub.

## Quick start (CLI)

```bash
cd my-repo
code2lora init            # write .code2lora/config.toml
code2lora install-hook    # update automatically after every `git commit`
code2lora sync            # process new commits now; export adapter when due
code2lora status          # cursor SHA, pending commits, adapter path
code2lora generate "def add(a, b):"
```

Wire it into CI/cron instead of (or alongside) the hook by running
`code2lora sync` in your pipeline, or run the foreground watcher:

```bash
code2lora watch
```

## Quick start (Python)

```python
from code2lora import Code2Lora

c2l = Code2Lora.from_repo(".")
result = c2l.sync()              # fold in new commits, export if due
print(result.summary())

model = c2l.load_model()         # base model + latest adapter (needs [infer])
print(c2l.generate("def add(a, b):"))

# the exported adapter is a normal PEFT adapter:
print(c2l.adapter_path())        # .code2lora/adapters/<branch>/
```

You can also load it yourself, anywhere:

```python
from peft import PeftModel
from transformers import AutoModelForCausalLM

base = AutoModelForCausalLM.from_pretrained("Qwen/Qwen2.5-Coder-1.5B")
model = PeftModel.from_pretrained(base, ".code2lora/adapters/main")
```

## How "update every few commits" works

The GRU consumes one commit at a time. `code2lora` persists, **per branch**, the
GRU hidden state plus a *cursor* (the last commit folded in) in
`.code2lora/state.pt`. A `sync`:

1. lists new first-parent commits after the cursor,
2. keeps the ones matching `commit_filter`,
3. steps the GRU on each (one cheap embedder forward + one GRU cell — no LLM),
4. re-exports the adapter once at least `update.every` commits have accrued since
   the last export (and always on the first export, or with `--force`).

Stepping is cheap; only the export touches disk. Nothing re-processes history
you've already folded in.

## Configuration

Everything lives in `.code2lora/config.toml`. Defaults work out of the box; edit
to taste.

```toml
[model]
base_model = "Qwen/Qwen2.5-Coder-1.5B"
hypernetwork = "code2lora/code2lora-gru"   # HF repo holding gru_head.*.pt
checkpoint_file = "gru_head.best.pt"
checkpoint_path = ""                        # local gru_head.*.pt (overrides HF)
embedder = "Qwen/Qwen3-Embedding-0.6B"
device = "auto"                             # auto | cpu | cuda | cuda:N
rank = 16
target_modules = ["q_proj", "k_proj", "v_proj", "o_proj", "up_proj", "gate_proj", "down_proj"]
max_repo_state_files = 400                  # cap files embedded for the cold-start seed

[update]
every = 5                                   # re-export adapter every N commits
commit_filter = "production"                # production | test-touching | all
branch_strategy = "current"                 # current | per-branch | main-only
main_branch = "main"
export_on_every_sync = false

[hook]
background = true                           # don't block `git commit`

[watch]
interval = 30                               # seconds between HEAD polls
```

### `update.every`
How many newly-processed commits must accrue before the adapter is re-exported.
Set it low for tight feedback, high to amortise export cost.

### `update.commit_filter`
Which commits advance the GRU:

| value           | commits that count                                                        |
|-----------------|---------------------------------------------------------------------------|
| `production`    | commits touching non-test source (`.py`/`.md`/`.rst`) — the default       |
| `test-touching` | commits that change a python test file (approximates the training regime) |
| `all`           | every first-parent commit                                                 |

The diff fed at each step is the filtered production-code diff from the previous
*selected* commit to the current one, so changes in skipped commits still flow in.

### `branch_strategy`
How branches map onto GRU state:

| value        | behaviour                                                                                   |
|--------------|---------------------------------------------------------------------------------------------|
| `current`    | track the checked-out branch (default)                                                       |
| `main-only`  | always follow `main_branch`, regardless of what's checked out                                |
| `per-branch` | keep an independent hidden state + adapter per branch, forked from the parent at branch point |

For `per-branch`, when a new branch first appears its state is forked from a
parent branch whose cursor is an ancestor of the new tip; otherwise that branch
cold-starts. Each branch's adapter lands in `.code2lora/adapters/<branch>/`.

## Using a private / local checkpoint

```toml
[model]
checkpoint_path = "/path/to/gru_head.best.pt"
```

or via environment variables (also honoured): `CODE2LORA_CKPT`,
`CODE2LORA_CKPT_REPO`, `CODE2LORA_CKPT_FILE`.

## CLI reference

| command           | description                                              |
|-------------------|----------------------------------------------------------|
| `init`            | write `.code2lora/config.toml` (+ `--install-hook`)      |
| `install-hook`    | add a git `post-commit` hook that runs `sync`            |
| `uninstall-hook`  | remove the hook                                          |
| `sync [--force]`  | process new commits; export when due (`--force` exports now) |
| `status`          | show cursor / pending commits / adapter per branch       |
| `watch`           | run the polling watcher in the foreground                |
| `export [-o DIR]` | force-generate the adapter into a directory              |
| `generate PROMPT` | load base + adapter and generate                         |

Add `--repo PATH` before the command to target a repo other than the cwd.

## What gets written

```
.code2lora/
  config.toml          # your settings
  state.pt             # persisted GRU state + cursor per branch
  adapters/<branch>/   # adapter_model.safetensors + adapter_config.json
  .gitignore           # ignores state.pt and adapters/ by default
```

## Limitations

- Inference-only: the hypernetwork is **not** fine-tuned on your repo.
- Trained on Python repositories with test suites; other languages will run but
  are out of the trained distribution.
- `test-touching` uses a "changes a test file" heuristic, a lighter,
  incremental-friendly approximation of the trainer's "introduces a new
  assertion" selection.
- Defaults target `Qwen/Qwen2.5-Coder-1.5B`; the PEFT key layout assumes a
  Qwen2/Llama-style decoder (`self_attn.*` / `mlp.*`).

## License

Apache-2.0.
