Spaces:
Running on Zero
Running on Zero
atomwalk12 commited on
Commit ·
0dd6c2f
1
Parent(s): a9f4671
initial commit
Browse filesThis view is limited to 50 files because it contains too many changes. See raw diff
- .devcontainer/devcontainer.json +32 -0
- .devcontainer/postCreateCommand.sh +10 -0
- .gemini/config.yaml +10 -0
- .gemini/styleguide.md +144 -0
- .github/actions/setup-python-env/action.yml +41 -0
- .github/dependabot.yml +11 -0
- .github/workflows/codeql.yml +100 -0
- .github/workflows/conventional-pr.yml +17 -0
- .github/workflows/main.yml +70 -0
- .github/workflows/on-release-main.yml +102 -0
- .github/workflows/validate-codecov-config.yml +15 -0
- .gitignore +155 -0
- .pre-commit-config.yaml +26 -0
- CHANGELOG.md +389 -0
- CONTRIBUTING.md +126 -0
- Dockerfile +21 -0
- LICENSE +21 -0
- Makefile +166 -0
- README.md +150 -7
- app.py +0 -7
- codecov.yaml +9 -0
- env/.env.distillation.example +14 -0
- env/.env.grpo.example +9 -0
- env/.env.grpo.runpod.example +16 -0
- linalg_zero/__init__.py +0 -0
- linalg_zero/config/cleaning_config.yaml +42 -0
- linalg_zero/config/data.py +341 -0
- linalg_zero/config/dataset/default.yml +0 -0
- linalg_zero/config/dataset/default_debug.yml +1 -0
- linalg_zero/config/distillation/env.example.sh +13 -0
- linalg_zero/config/distillation/llamacpp_qwen3_30b_A3B_instruct.yaml +49 -0
- linalg_zero/config/distillation/llamacpp_qwen3_30b_A3B_think.yaml +49 -0
- linalg_zero/config/distillation/llamacpp_qwen3_32b_instruct.yaml +49 -0
- linalg_zero/config/distillation/qwen3_think_vllm_debug.yaml +65 -0
- linalg_zero/config/distillation/vllm_qwen3_32b.yaml +46 -0
- linalg_zero/config/distillation/vllm_qwen3_32b_debug.yaml +46 -0
- linalg_zero/config/distillation/vllm_qwen3_4b_think.yaml +52 -0
- linalg_zero/config/grpo/Qwen/Qwen2.5-3B/eval/linalgzero-grpo-110.yaml +62 -0
- linalg_zero/config/grpo/Qwen/Qwen2.5-3B/eval/linalgzero-grpo.yaml +62 -0
- linalg_zero/config/grpo/Qwen/Qwen2.5-3B/eval/linalgzero-sft-110.yaml +62 -0
- linalg_zero/config/grpo/Qwen/Qwen2.5-3B/eval/linalgzero-sft.yaml +62 -0
- linalg_zero/config/grpo/Qwen/Qwen2.5-3B/local.yaml +95 -0
- linalg_zero/config/grpo/Qwen/Qwen2.5-3B/runpod.yaml +95 -0
- linalg_zero/config/sft/accelerate/zero2.yaml +21 -0
- linalg_zero/config/sft/accelerate/zero3.yaml +22 -0
- linalg_zero/config/sft/qwen2.5-3B/instruct.yaml +78 -0
- linalg_zero/config/sft/qwen2.5-3B/lora.yaml +97 -0
- linalg_zero/config/sft/qwen2.5-3B/merged.yaml +78 -0
- linalg_zero/config/sft/qwen2.5-3B/nst.yaml +99 -0
- linalg_zero/demo/app.py +968 -0
.devcontainer/devcontainer.json
ADDED
|
@@ -0,0 +1,32 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
// For format details, see https://aka.ms/devcontainer.json. For config options, see the
|
| 2 |
+
// README at: https://github.com/devcontainers/templates/tree/main/src/python
|
| 3 |
+
{
|
| 4 |
+
"name": "linalg-zero",
|
| 5 |
+
// Or use a Dockerfile or Docker Compose file. More info: https://containers.dev/guide/dockerfile
|
| 6 |
+
"image": "mcr.microsoft.com/devcontainers/python:1-3.11-bullseye",
|
| 7 |
+
"runArgs": [
|
| 8 |
+
// avoid UID/GID remapping under rootless Podman
|
| 9 |
+
// This flag works with Podman but not with Docker:
|
| 10 |
+
// "--userns=keep-id",
|
| 11 |
+
"-v", "${localEnv:HOME}/.ssh:/home/vscode/.ssh:ro",
|
| 12 |
+
"--user", "1000:1000"
|
| 13 |
+
],
|
| 14 |
+
"features": {},
|
| 15 |
+
|
| 16 |
+
// Use 'postCreateCommand' to run commands after the container is created.
|
| 17 |
+
"postCreateCommand": "./.devcontainer/postCreateCommand.sh",
|
| 18 |
+
|
| 19 |
+
// Configure tool-specific properties.
|
| 20 |
+
"customizations": {
|
| 21 |
+
"vscode": {
|
| 22 |
+
"extensions": ["ms-python.python", "editorconfig.editorconfig"],
|
| 23 |
+
"settings": {
|
| 24 |
+
"python.testing.pytestArgs": ["tests"],
|
| 25 |
+
"python.testing.unittestEnabled": false,
|
| 26 |
+
"python.testing.pytestEnabled": true,
|
| 27 |
+
"python.defaultInterpreterPath": "/workspaces/linalg-zero/.venv/bin/python",
|
| 28 |
+
"python.testing.pytestPath": "/workspaces/linalg-zero/.venv/bin/pytest"
|
| 29 |
+
}
|
| 30 |
+
}
|
| 31 |
+
}
|
| 32 |
+
}
|
.devcontainer/postCreateCommand.sh
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
#! /usr/bin/env bash
|
| 2 |
+
|
| 3 |
+
# Install uv
|
| 4 |
+
curl -LsSf https://astral.sh/uv/install.sh | sh
|
| 5 |
+
|
| 6 |
+
# Install Dependencies
|
| 7 |
+
uv sync
|
| 8 |
+
|
| 9 |
+
# Install pre-commit hooks
|
| 10 |
+
uv run pre-commit install --install-hooks
|
.gemini/config.yaml
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
have_fun: true
|
| 2 |
+
code_review:
|
| 3 |
+
disable: false
|
| 4 |
+
comment_severity_threshold: MEDIUM
|
| 5 |
+
max_review_comments: -1
|
| 6 |
+
pull_request_opened:
|
| 7 |
+
help: false
|
| 8 |
+
summary: true
|
| 9 |
+
code_review: true
|
| 10 |
+
ignore_patterns: []
|
.gemini/styleguide.md
ADDED
|
@@ -0,0 +1,144 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Project LinAlgZero Python Style Guide
|
| 2 |
+
|
| 3 |
+
# Introduction
|
| 4 |
+
This style guide outlines the coding conventions for Python code.
|
| 5 |
+
It's based on PEP 8, but with some modifications to address specific needs and
|
| 6 |
+
preferences within our organization.
|
| 7 |
+
|
| 8 |
+
# Key Principles
|
| 9 |
+
* **Readability:** Code should be easy to understand for all team members.
|
| 10 |
+
* **Maintainability:** Code should be easy to modify and extend.
|
| 11 |
+
* **Consistency:** Adhering to a consistent style across all projects improves
|
| 12 |
+
collaboration and reduces errors.
|
| 13 |
+
* **Performance:** While readability is paramount, code should be efficient.
|
| 14 |
+
|
| 15 |
+
# Deviations from PEP 8
|
| 16 |
+
|
| 17 |
+
## Line Length
|
| 18 |
+
* **Maximum line length:** 119 characters (instead of PEP 8's 79).
|
| 19 |
+
* Modern screens allow for wider lines, improving code readability in many cases.
|
| 20 |
+
* Many common patterns in our codebase, like long strings or URLs, often exceed 79 characters.
|
| 21 |
+
|
| 22 |
+
## Indentation
|
| 23 |
+
* **Use 4 spaces per indentation level.** (PEP 8 recommendation)
|
| 24 |
+
|
| 25 |
+
## Imports
|
| 26 |
+
* **Group imports:**
|
| 27 |
+
* Standard library imports
|
| 28 |
+
* Related third party imports
|
| 29 |
+
* Local application/library specific imports
|
| 30 |
+
* **Absolute imports:** Always use absolute imports for clarity.
|
| 31 |
+
* **Import order within groups:** Sort alphabetically.
|
| 32 |
+
|
| 33 |
+
## Naming Conventions
|
| 34 |
+
|
| 35 |
+
* **Variables:** Use lowercase with underscores (snake_case): `user_name`, `total_count`
|
| 36 |
+
* **Constants:** Use uppercase with underscores: `MAX_VALUE`, `DATABASE_NAME`
|
| 37 |
+
* **Functions:** Use lowercase with underscores (snake_case): `calculate_total()`, `process_data()`
|
| 38 |
+
* **Classes:** Use CapWords (CamelCase): `UserManager`, `PaymentProcessor`
|
| 39 |
+
* **Modules:** Use lowercase with underscores (snake_case): `user_utils`, `payment_gateway`
|
| 40 |
+
|
| 41 |
+
## Docstrings
|
| 42 |
+
* **Use triple double quotes (`"""Docstring goes here."""`) for all docstrings.**
|
| 43 |
+
* **First line:** Concise summary of the object's purpose.
|
| 44 |
+
* **For complex functions/classes:** Include detailed descriptions of parameters, return values,
|
| 45 |
+
attributes, and exceptions.
|
| 46 |
+
* **Use Google style docstrings:** This helps with automated documentation generation.
|
| 47 |
+
```python
|
| 48 |
+
def my_function(param1, param2):
|
| 49 |
+
"""Single-line summary.
|
| 50 |
+
|
| 51 |
+
More detailed description, if necessary.
|
| 52 |
+
|
| 53 |
+
Args:
|
| 54 |
+
param1 (int): The first parameter.
|
| 55 |
+
param2 (str): The second parameter.
|
| 56 |
+
|
| 57 |
+
Returns:
|
| 58 |
+
bool: The return value. True for success, False otherwise.
|
| 59 |
+
|
| 60 |
+
Raises:
|
| 61 |
+
ValueError: If `param2` is invalid.
|
| 62 |
+
"""
|
| 63 |
+
# function body here
|
| 64 |
+
```
|
| 65 |
+
|
| 66 |
+
## Type Hints
|
| 67 |
+
* **Use type hints:** Type hints improve code readability and help catch errors early.
|
| 68 |
+
* **Follow PEP 484:** Use the standard type hinting syntax.
|
| 69 |
+
|
| 70 |
+
## Comments
|
| 71 |
+
* **Write clear and concise comments:** Explain the "why" behind the code, not just the "what".
|
| 72 |
+
* **Comment sparingly:** Well-written code should be self-documenting where possible.
|
| 73 |
+
* **Use complete sentences:** Start comments with a capital letter and use proper punctuation.
|
| 74 |
+
|
| 75 |
+
## Logging
|
| 76 |
+
* **Use a standard logging framework:** Project LinAlgZero uses the built-in `logging` module.
|
| 77 |
+
* **Log at appropriate levels:** DEBUG, INFO, WARNING, ERROR, CRITICAL
|
| 78 |
+
* **Provide context:** Include relevant information in log messages to aid debugging.
|
| 79 |
+
|
| 80 |
+
## Error Handling
|
| 81 |
+
* **Use specific exceptions:** Avoid using broad exceptions like `Exception`.
|
| 82 |
+
* **Handle exceptions gracefully:** Provide informative error messages and avoid crashing the program.
|
| 83 |
+
* **Use `try...except` blocks:** Isolate code that might raise exceptions.
|
| 84 |
+
|
| 85 |
+
# Tooling
|
| 86 |
+
* **Code formatter:** Ruff (ruff-format) - Enforces consistent formatting automatically.
|
| 87 |
+
* **Linter:** Ruff - Identifies potential issues and style violations.
|
| 88 |
+
|
| 89 |
+
# Example
|
| 90 |
+
```python
|
| 91 |
+
"""Module for user authentication."""
|
| 92 |
+
|
| 93 |
+
import hashlib
|
| 94 |
+
import logging
|
| 95 |
+
import os
|
| 96 |
+
|
| 97 |
+
from companyx.db import user_database
|
| 98 |
+
|
| 99 |
+
LOGGER = logging.getLogger(__name__)
|
| 100 |
+
|
| 101 |
+
def hash_password(password: str) -> str:
|
| 102 |
+
"""Hashes a password using SHA-256.
|
| 103 |
+
|
| 104 |
+
Args:
|
| 105 |
+
password (str): The password to hash.
|
| 106 |
+
|
| 107 |
+
Returns:
|
| 108 |
+
str: The hashed password.
|
| 109 |
+
"""
|
| 110 |
+
salt = os.urandom(16)
|
| 111 |
+
salted_password = salt + password.encode('utf-8')
|
| 112 |
+
hashed_password = hashlib.sha256(salted_password).hexdigest()
|
| 113 |
+
return f"{salt.hex()}:{hashed_password}"
|
| 114 |
+
|
| 115 |
+
def authenticate_user(username: str, password: str) -> bool:
|
| 116 |
+
"""Authenticates a user against the database.
|
| 117 |
+
|
| 118 |
+
Args:
|
| 119 |
+
username (str): The user's username.
|
| 120 |
+
password (str): The user's password.
|
| 121 |
+
|
| 122 |
+
Returns:
|
| 123 |
+
bool: True if the user is authenticated, False otherwise.
|
| 124 |
+
"""
|
| 125 |
+
try:
|
| 126 |
+
user = user_database.get_user(username)
|
| 127 |
+
if user is None:
|
| 128 |
+
LOGGER.warning("Authentication failed: User not found - %s", username)
|
| 129 |
+
return False
|
| 130 |
+
|
| 131 |
+
stored_hash = user.password_hash
|
| 132 |
+
salt, hashed_password = stored_hash.split(':')
|
| 133 |
+
salted_password = bytes.fromhex(salt) + password.encode('utf-8')
|
| 134 |
+
calculated_hash = hashlib.sha256(salted_password).hexdigest()
|
| 135 |
+
|
| 136 |
+
if calculated_hash == hashed_password:
|
| 137 |
+
LOGGER.info("User authenticated successfully - %s", username)
|
| 138 |
+
return True
|
| 139 |
+
else:
|
| 140 |
+
LOGGER.warning("Authentication failed: Incorrect password - %s", username)
|
| 141 |
+
return False
|
| 142 |
+
except Exception as e:
|
| 143 |
+
LOGGER.error("An error occurred during authentication: %s", e)
|
| 144 |
+
return False
|
.github/actions/setup-python-env/action.yml
ADDED
|
@@ -0,0 +1,41 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
name: "Setup Python Environment"
|
| 2 |
+
description: "Set up Python environment for the given Python version"
|
| 3 |
+
|
| 4 |
+
inputs:
|
| 5 |
+
python-version:
|
| 6 |
+
description: "Python version to use"
|
| 7 |
+
required: true
|
| 8 |
+
default: "3.12"
|
| 9 |
+
uv-version:
|
| 10 |
+
description: "uv version to use"
|
| 11 |
+
required: true
|
| 12 |
+
default: "0.6.14"
|
| 13 |
+
dependency-groups:
|
| 14 |
+
description: "Space-separated dependency groups to install (e.g. 'dev test')"
|
| 15 |
+
required: false
|
| 16 |
+
default: "dev"
|
| 17 |
+
|
| 18 |
+
runs:
|
| 19 |
+
using: "composite"
|
| 20 |
+
steps:
|
| 21 |
+
- uses: actions/setup-python@v5
|
| 22 |
+
with:
|
| 23 |
+
python-version: ${{ inputs.python-version }}
|
| 24 |
+
|
| 25 |
+
- name: Install uv
|
| 26 |
+
uses: astral-sh/setup-uv@v2
|
| 27 |
+
with:
|
| 28 |
+
version: ${{ inputs.uv-version }}
|
| 29 |
+
enable-cache: 'true'
|
| 30 |
+
cache-suffix: ${{ matrix.python-version }}
|
| 31 |
+
|
| 32 |
+
- name: Install Python dependencies
|
| 33 |
+
run: |
|
| 34 |
+
args=()
|
| 35 |
+
if [ -n "${{ inputs.dependency-groups }}" ]; then
|
| 36 |
+
for group in ${{ inputs.dependency-groups }}; do
|
| 37 |
+
args+=(--group "$group")
|
| 38 |
+
done
|
| 39 |
+
fi
|
| 40 |
+
uv sync --frozen "${args[@]}"
|
| 41 |
+
shell: bash
|
.github/dependabot.yml
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# To get started with Dependabot version updates, you'll need to specify which
|
| 2 |
+
# package ecosystems to update and where the package manifests are located.
|
| 3 |
+
# Please see the documentation for all configuration options:
|
| 4 |
+
# https://docs.github.com/code-security/dependabot/dependabot-version-updates/configuration-options-for-the-dependabot.yml-file
|
| 5 |
+
|
| 6 |
+
version: 2
|
| 7 |
+
updates:
|
| 8 |
+
- package-ecosystem: "uv" # See documentation for possible values
|
| 9 |
+
directory: "/" # Location of package manifests
|
| 10 |
+
schedule:
|
| 11 |
+
interval: "weekly"
|
.github/workflows/codeql.yml
ADDED
|
@@ -0,0 +1,100 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# For most projects, this workflow file will not need changing; you simply need
|
| 2 |
+
# to commit it to your repository.
|
| 3 |
+
#
|
| 4 |
+
# You may wish to alter this file to override the set of languages analyzed,
|
| 5 |
+
# or to provide custom queries or build logic.
|
| 6 |
+
#
|
| 7 |
+
# ******** NOTE ********
|
| 8 |
+
# We have attempted to detect the languages in your repository. Please check
|
| 9 |
+
# the `language` matrix defined below to confirm you have the correct set of
|
| 10 |
+
# supported CodeQL languages.
|
| 11 |
+
#
|
| 12 |
+
name: "CodeQL Advanced"
|
| 13 |
+
|
| 14 |
+
on:
|
| 15 |
+
push:
|
| 16 |
+
branches: [ "main" ]
|
| 17 |
+
pull_request:
|
| 18 |
+
branches: [ "main" ]
|
| 19 |
+
schedule:
|
| 20 |
+
- cron: '42 21 * * 2'
|
| 21 |
+
|
| 22 |
+
jobs:
|
| 23 |
+
analyze:
|
| 24 |
+
name: Analyze (${{ matrix.language }})
|
| 25 |
+
# Runner size impacts CodeQL analysis time. To learn more, please see:
|
| 26 |
+
# - https://gh.io/recommended-hardware-resources-for-running-codeql
|
| 27 |
+
# - https://gh.io/supported-runners-and-hardware-resources
|
| 28 |
+
# - https://gh.io/using-larger-runners (GitHub.com only)
|
| 29 |
+
# Consider using larger runners or machines with greater resources for possible analysis time improvements.
|
| 30 |
+
runs-on: ${{ (matrix.language == 'swift' && 'macos-latest') || 'ubuntu-latest' }}
|
| 31 |
+
permissions:
|
| 32 |
+
# required for all workflows
|
| 33 |
+
security-events: write
|
| 34 |
+
|
| 35 |
+
# required to fetch internal or private CodeQL packs
|
| 36 |
+
packages: read
|
| 37 |
+
|
| 38 |
+
# only required for workflows in private repositories
|
| 39 |
+
actions: read
|
| 40 |
+
contents: read
|
| 41 |
+
|
| 42 |
+
strategy:
|
| 43 |
+
fail-fast: false
|
| 44 |
+
matrix:
|
| 45 |
+
include:
|
| 46 |
+
- language: actions
|
| 47 |
+
build-mode: none
|
| 48 |
+
- language: python
|
| 49 |
+
build-mode: none
|
| 50 |
+
# CodeQL supports the following values keywords for 'language': 'actions', 'c-cpp', 'csharp', 'go', 'java-kotlin', 'javascript-typescript', 'python', 'ruby', 'swift'
|
| 51 |
+
# Use `c-cpp` to analyze code written in C, C++ or both
|
| 52 |
+
# Use 'java-kotlin' to analyze code written in Java, Kotlin or both
|
| 53 |
+
# Use 'javascript-typescript' to analyze code written in JavaScript, TypeScript or both
|
| 54 |
+
# To learn more about changing the languages that are analyzed or customizing the build mode for your analysis,
|
| 55 |
+
# see https://docs.github.com/en/code-security/code-scanning/creating-an-advanced-setup-for-code-scanning/customizing-your-advanced-setup-for-code-scanning.
|
| 56 |
+
# If you are analyzing a compiled language, you can modify the 'build-mode' for that language to customize how
|
| 57 |
+
# your codebase is analyzed, see https://docs.github.com/en/code-security/code-scanning/creating-an-advanced-setup-for-code-scanning/codeql-code-scanning-for-compiled-languages
|
| 58 |
+
steps:
|
| 59 |
+
- name: Checkout repository
|
| 60 |
+
uses: actions/checkout@v4
|
| 61 |
+
|
| 62 |
+
# Add any setup steps before running the `github/codeql-action/init` action.
|
| 63 |
+
# This includes steps like installing compilers or runtimes (`actions/setup-node`
|
| 64 |
+
# or others). This is typically only required for manual builds.
|
| 65 |
+
# - name: Setup runtime (example)
|
| 66 |
+
# uses: actions/setup-example@v1
|
| 67 |
+
|
| 68 |
+
# Initializes the CodeQL tools for scanning.
|
| 69 |
+
- name: Initialize CodeQL
|
| 70 |
+
uses: github/codeql-action/init@v3
|
| 71 |
+
with:
|
| 72 |
+
languages: ${{ matrix.language }}
|
| 73 |
+
build-mode: ${{ matrix.build-mode }}
|
| 74 |
+
# If you wish to specify custom queries, you can do so here or in a config file.
|
| 75 |
+
# By default, queries listed here will override any specified in a config file.
|
| 76 |
+
# Prefix the list here with "+" to use these queries and those in the config file.
|
| 77 |
+
|
| 78 |
+
# For more details on CodeQL's query packs, refer to: https://docs.github.com/en/code-security/code-scanning/automatically-scanning-your-code-for-vulnerabilities-and-errors/configuring-code-scanning#using-queries-in-ql-packs
|
| 79 |
+
# queries: security-extended,security-and-quality
|
| 80 |
+
|
| 81 |
+
# If the analyze step fails for one of the languages you are analyzing with
|
| 82 |
+
# "We were unable to automatically build your code", modify the matrix above
|
| 83 |
+
# to set the build mode to "manual" for that language. Then modify this step
|
| 84 |
+
# to build your code.
|
| 85 |
+
# ℹ️ Command-line programs to run using the OS shell.
|
| 86 |
+
# 📚 See https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idstepsrun
|
| 87 |
+
- if: matrix.build-mode == 'manual'
|
| 88 |
+
shell: bash
|
| 89 |
+
run: |
|
| 90 |
+
echo 'If you are using a "manual" build mode for one or more of the' \
|
| 91 |
+
'languages you are analyzing, replace this with the commands to build' \
|
| 92 |
+
'your code, for example:'
|
| 93 |
+
echo ' make bootstrap'
|
| 94 |
+
echo ' make release'
|
| 95 |
+
exit 1
|
| 96 |
+
|
| 97 |
+
- name: Perform CodeQL Analysis
|
| 98 |
+
uses: github/codeql-action/analyze@v3
|
| 99 |
+
with:
|
| 100 |
+
category: "/language:${{matrix.language}}"
|
.github/workflows/conventional-pr.yml
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
name: conventional-pr
|
| 2 |
+
on:
|
| 3 |
+
pull_request:
|
| 4 |
+
types:
|
| 5 |
+
- opened
|
| 6 |
+
- edited
|
| 7 |
+
- synchronize
|
| 8 |
+
branches:
|
| 9 |
+
- main
|
| 10 |
+
jobs:
|
| 11 |
+
lint-pr:
|
| 12 |
+
runs-on: ubuntu-latest
|
| 13 |
+
steps:
|
| 14 |
+
- uses: actions/checkout@v3
|
| 15 |
+
- uses: CondeNast/conventional-pull-request-action@v0.2.0
|
| 16 |
+
env:
|
| 17 |
+
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
.github/workflows/main.yml
ADDED
|
@@ -0,0 +1,70 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
name: Main
|
| 2 |
+
|
| 3 |
+
on:
|
| 4 |
+
push:
|
| 5 |
+
branches:
|
| 6 |
+
- main
|
| 7 |
+
pull_request:
|
| 8 |
+
types: [opened, synchronize, reopened, ready_for_review]
|
| 9 |
+
|
| 10 |
+
jobs:
|
| 11 |
+
quality:
|
| 12 |
+
runs-on: ubuntu-latest
|
| 13 |
+
steps:
|
| 14 |
+
- name: Check out
|
| 15 |
+
uses: actions/checkout@v4
|
| 16 |
+
|
| 17 |
+
- uses: actions/cache@v4
|
| 18 |
+
with:
|
| 19 |
+
path: ~/.cache/pre-commit
|
| 20 |
+
key: pre-commit-${{ hashFiles('.pre-commit-config.yaml') }}
|
| 21 |
+
|
| 22 |
+
- name: Set up the environment
|
| 23 |
+
uses: ./.github/actions/setup-python-env
|
| 24 |
+
|
| 25 |
+
- name: Run checks
|
| 26 |
+
run: make check
|
| 27 |
+
|
| 28 |
+
tests-and-type-check:
|
| 29 |
+
runs-on: ubuntu-latest
|
| 30 |
+
strategy:
|
| 31 |
+
matrix:
|
| 32 |
+
python-version: ["3.10", "3.11", "3.12"]
|
| 33 |
+
fail-fast: false
|
| 34 |
+
defaults:
|
| 35 |
+
run:
|
| 36 |
+
shell: bash
|
| 37 |
+
steps:
|
| 38 |
+
- name: Check out
|
| 39 |
+
uses: actions/checkout@v4
|
| 40 |
+
|
| 41 |
+
- name: Set up the environment
|
| 42 |
+
uses: ./.github/actions/setup-python-env
|
| 43 |
+
with:
|
| 44 |
+
python-version: ${{ matrix.python-version }}
|
| 45 |
+
dependency-groups: "dev test"
|
| 46 |
+
|
| 47 |
+
- name: Run tests
|
| 48 |
+
run: uv run python -m pytest tests --cov --cov-config=pyproject.toml --cov-report=xml
|
| 49 |
+
|
| 50 |
+
- name: Check typing
|
| 51 |
+
run: uv run mypy
|
| 52 |
+
|
| 53 |
+
|
| 54 |
+
- name: Upload coverage reports to Codecov with GitHub Action on Python 3.11
|
| 55 |
+
uses: codecov/codecov-action@v4
|
| 56 |
+
if: ${{ matrix.python-version == '3.11' }}
|
| 57 |
+
env:
|
| 58 |
+
CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }}
|
| 59 |
+
|
| 60 |
+
check-docs:
|
| 61 |
+
runs-on: ubuntu-latest
|
| 62 |
+
steps:
|
| 63 |
+
- name: Check out
|
| 64 |
+
uses: actions/checkout@v4
|
| 65 |
+
|
| 66 |
+
- name: Set up the environment
|
| 67 |
+
uses: ./.github/actions/setup-python-env
|
| 68 |
+
|
| 69 |
+
- name: Check if documentation can be built
|
| 70 |
+
run: uv run mkdocs build -s
|
.github/workflows/on-release-main.yml
ADDED
|
@@ -0,0 +1,102 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
name: release-main
|
| 2 |
+
|
| 3 |
+
on:
|
| 4 |
+
workflow_dispatch:
|
| 5 |
+
|
| 6 |
+
jobs:
|
| 7 |
+
semantic-release:
|
| 8 |
+
runs-on: ubuntu-latest
|
| 9 |
+
concurrency: release
|
| 10 |
+
permissions:
|
| 11 |
+
id-token: write
|
| 12 |
+
contents: write
|
| 13 |
+
outputs:
|
| 14 |
+
released: ${{ steps.release.outputs.released }}
|
| 15 |
+
tag: ${{ steps.release.outputs.tag }}
|
| 16 |
+
steps:
|
| 17 |
+
- uses: actions/checkout@v4
|
| 18 |
+
with:
|
| 19 |
+
ref: ${{ github.ref_name }}
|
| 20 |
+
fetch-depth: 0
|
| 21 |
+
persist-credentials: false
|
| 22 |
+
|
| 23 |
+
- name: Setup | Force release branch to be at workflow sha
|
| 24 |
+
run: |
|
| 25 |
+
git reset --hard ${{ github.sha }}
|
| 26 |
+
|
| 27 |
+
- name: Action | Python Semantic Release
|
| 28 |
+
id: release
|
| 29 |
+
uses: python-semantic-release/python-semantic-release@v9.21.1
|
| 30 |
+
with:
|
| 31 |
+
github_token: ${{ secrets.GITHUB_TOKEN }}
|
| 32 |
+
|
| 33 |
+
# The following is done through the on-release-main.yml action.
|
| 34 |
+
# Leaving this here for reference.
|
| 35 |
+
# - name: Publish | Upload package to PyPI
|
| 36 |
+
# uses: pypa/gh-action-pypi-publish@release/v1
|
| 37 |
+
# # NOTE: DO NOT wrap the conditional in ${{ }} as it will always evaluate to true.
|
| 38 |
+
# # See https://github.com/actions/runner/issues/1173
|
| 39 |
+
# if: steps.release.outputs.released == 'true'
|
| 40 |
+
|
| 41 |
+
- name: Publish | Upload to GitHub Release Assets
|
| 42 |
+
uses: python-semantic-release/publish-action@v9.21.1
|
| 43 |
+
if: steps.release.outputs.released == 'true'
|
| 44 |
+
with:
|
| 45 |
+
github_token: ${{ secrets.GITHUB_TOKEN }}
|
| 46 |
+
tag: ${{ steps.release.outputs.tag }}
|
| 47 |
+
|
| 48 |
+
|
| 49 |
+
set-version:
|
| 50 |
+
needs: [semantic-release]
|
| 51 |
+
if: needs.semantic-release.outputs.released == 'true'
|
| 52 |
+
runs-on: ubuntu-24.04
|
| 53 |
+
steps:
|
| 54 |
+
- uses: actions/checkout@v4
|
| 55 |
+
|
| 56 |
+
- name: Update project version
|
| 57 |
+
run: |
|
| 58 |
+
sed -i "s/^version = \".*\"/version = \"$RELEASE_VERSION\"/" pyproject.toml
|
| 59 |
+
env:
|
| 60 |
+
RELEASE_VERSION: ${{ needs.semantic-release.outputs.tag }}
|
| 61 |
+
|
| 62 |
+
- name: Upload updated pyproject.toml
|
| 63 |
+
uses: actions/upload-artifact@v4
|
| 64 |
+
with:
|
| 65 |
+
name: pyproject-toml
|
| 66 |
+
path: pyproject.toml
|
| 67 |
+
|
| 68 |
+
publish:
|
| 69 |
+
runs-on: ubuntu-latest
|
| 70 |
+
needs: [set-version]
|
| 71 |
+
steps:
|
| 72 |
+
- name: Check out
|
| 73 |
+
uses: actions/checkout@v4
|
| 74 |
+
|
| 75 |
+
- name: Set up the environment
|
| 76 |
+
uses: ./.github/actions/setup-python-env
|
| 77 |
+
|
| 78 |
+
- name: Download updated pyproject.toml
|
| 79 |
+
uses: actions/download-artifact@v4
|
| 80 |
+
with:
|
| 81 |
+
name: pyproject-toml
|
| 82 |
+
|
| 83 |
+
- name: Build package
|
| 84 |
+
run: uv build
|
| 85 |
+
|
| 86 |
+
- name: Publish package
|
| 87 |
+
run: uv publish
|
| 88 |
+
env:
|
| 89 |
+
UV_PUBLISH_TOKEN: ${{ secrets.PYPI_TOKEN }}
|
| 90 |
+
|
| 91 |
+
deploy-docs:
|
| 92 |
+
needs: publish
|
| 93 |
+
runs-on: ubuntu-latest
|
| 94 |
+
steps:
|
| 95 |
+
- name: Check out
|
| 96 |
+
uses: actions/checkout@v4
|
| 97 |
+
|
| 98 |
+
- name: Set up the environment
|
| 99 |
+
uses: ./.github/actions/setup-python-env
|
| 100 |
+
|
| 101 |
+
- name: Deploy documentation
|
| 102 |
+
run: uv run mkdocs gh-deploy --force
|
.github/workflows/validate-codecov-config.yml
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
name: validate-codecov-config
|
| 2 |
+
|
| 3 |
+
on:
|
| 4 |
+
pull_request:
|
| 5 |
+
paths: [codecov.yaml]
|
| 6 |
+
push:
|
| 7 |
+
branches: [main]
|
| 8 |
+
|
| 9 |
+
jobs:
|
| 10 |
+
validate-codecov-config:
|
| 11 |
+
runs-on: ubuntu-22.04
|
| 12 |
+
steps:
|
| 13 |
+
- uses: actions/checkout@v4
|
| 14 |
+
- name: Validate codecov configuration
|
| 15 |
+
run: curl -sSL --fail-with-body --data-binary @codecov.yaml https://codecov.io/validate
|
.gitignore
ADDED
|
@@ -0,0 +1,155 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
docs/source
|
| 2 |
+
|
| 3 |
+
# From https://raw.githubusercontent.com/github/gitignore/main/Python.gitignore
|
| 4 |
+
|
| 5 |
+
# Byte-compiled / optimized / DLL files
|
| 6 |
+
__pycache__/
|
| 7 |
+
*.py[cod]
|
| 8 |
+
*$py.class
|
| 9 |
+
|
| 10 |
+
# C extensions
|
| 11 |
+
*.so
|
| 12 |
+
|
| 13 |
+
# Distribution / packaging
|
| 14 |
+
.Python
|
| 15 |
+
build/
|
| 16 |
+
develop-eggs/
|
| 17 |
+
dist/
|
| 18 |
+
downloads/
|
| 19 |
+
eggs/
|
| 20 |
+
.eggs/
|
| 21 |
+
lib/
|
| 22 |
+
lib64/
|
| 23 |
+
parts/
|
| 24 |
+
sdist/
|
| 25 |
+
var/
|
| 26 |
+
wheels/
|
| 27 |
+
share/python-wheels/
|
| 28 |
+
*.egg-info/
|
| 29 |
+
.installed.cfg
|
| 30 |
+
*.egg
|
| 31 |
+
MANIFEST
|
| 32 |
+
|
| 33 |
+
# PyInstaller
|
| 34 |
+
# Usually these files are written by a python script from a template
|
| 35 |
+
# before PyInstaller builds the exe, so as to inject date/other infos into it.
|
| 36 |
+
*.manifest
|
| 37 |
+
*.spec
|
| 38 |
+
|
| 39 |
+
# Installer logs
|
| 40 |
+
pip-log.txt
|
| 41 |
+
pip-delete-this-directory.txt
|
| 42 |
+
|
| 43 |
+
# Unit test / coverage reports
|
| 44 |
+
htmlcov/
|
| 45 |
+
.tox/
|
| 46 |
+
.nox/
|
| 47 |
+
.coverage
|
| 48 |
+
.coverage.*
|
| 49 |
+
.cache
|
| 50 |
+
nosetests.xml
|
| 51 |
+
coverage.xml
|
| 52 |
+
*.cover
|
| 53 |
+
*.py,cover
|
| 54 |
+
.hypothesis/
|
| 55 |
+
.pytest_cache/
|
| 56 |
+
cover/
|
| 57 |
+
|
| 58 |
+
# Translations
|
| 59 |
+
*.mo
|
| 60 |
+
*.pot
|
| 61 |
+
|
| 62 |
+
# Django stuff:
|
| 63 |
+
*.log
|
| 64 |
+
local_settings.py
|
| 65 |
+
db.sqlite3
|
| 66 |
+
db.sqlite3-journal
|
| 67 |
+
|
| 68 |
+
# Flask stuff:
|
| 69 |
+
instance/
|
| 70 |
+
.webassets-cache
|
| 71 |
+
|
| 72 |
+
# Scrapy stuff:
|
| 73 |
+
.scrapy
|
| 74 |
+
|
| 75 |
+
# Sphinx documentation
|
| 76 |
+
docs/_build/
|
| 77 |
+
|
| 78 |
+
# PyBuilder
|
| 79 |
+
.pybuilder/
|
| 80 |
+
target/
|
| 81 |
+
|
| 82 |
+
# Jupyter Notebook
|
| 83 |
+
.ipynb_checkpoints
|
| 84 |
+
|
| 85 |
+
# IPython
|
| 86 |
+
profile_default/
|
| 87 |
+
ipython_config.py
|
| 88 |
+
|
| 89 |
+
# PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm
|
| 90 |
+
__pypackages__/
|
| 91 |
+
|
| 92 |
+
# Celery stuff
|
| 93 |
+
celerybeat-schedule
|
| 94 |
+
celerybeat.pid
|
| 95 |
+
|
| 96 |
+
# SageMath parsed files
|
| 97 |
+
*.sage.py
|
| 98 |
+
|
| 99 |
+
# Environments
|
| 100 |
+
.env
|
| 101 |
+
.venv
|
| 102 |
+
venv/
|
| 103 |
+
|
| 104 |
+
# Spyder project settings
|
| 105 |
+
.spyderproject
|
| 106 |
+
.spyproject
|
| 107 |
+
|
| 108 |
+
# Rope project settings
|
| 109 |
+
.ropeproject
|
| 110 |
+
|
| 111 |
+
# mkdocs documentation
|
| 112 |
+
/site
|
| 113 |
+
|
| 114 |
+
# mypy
|
| 115 |
+
.mypy_cache/
|
| 116 |
+
.dmypy.json
|
| 117 |
+
dmypy.json
|
| 118 |
+
|
| 119 |
+
# Pyre type checker
|
| 120 |
+
.pyre/
|
| 121 |
+
|
| 122 |
+
# pytype static type analyzer
|
| 123 |
+
.pytype/
|
| 124 |
+
|
| 125 |
+
# Cython debug symbols
|
| 126 |
+
cython_debug/
|
| 127 |
+
|
| 128 |
+
# Vscode config files
|
| 129 |
+
.vscode/
|
| 130 |
+
|
| 131 |
+
# PyCharm
|
| 132 |
+
# JetBrains specific template is maintained in a separate JetBrains.gitignore that can
|
| 133 |
+
# be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore
|
| 134 |
+
# and can be added to the global gitignore or merged into this file. For a more nuclear
|
| 135 |
+
# option (not recommended) you can uncomment the following to ignore the entire idea folder.
|
| 136 |
+
#.idea/
|
| 137 |
+
|
| 138 |
+
logs
|
| 139 |
+
models
|
| 140 |
+
notebooks
|
| 141 |
+
*DE.md
|
| 142 |
+
wandb/
|
| 143 |
+
results/
|
| 144 |
+
*_debug.py
|
| 145 |
+
*_debug.sh
|
| 146 |
+
distilabel_cache/
|
| 147 |
+
hf-cache/
|
| 148 |
+
vllm-cache/
|
| 149 |
+
uv-cache/
|
| 150 |
+
unsloth_compiled_cache/
|
| 151 |
+
.*o
|
| 152 |
+
outputs
|
| 153 |
+
500
|
| 154 |
+
distillation-cache
|
| 155 |
+
_unsloth_temporary_saved_buffers
|
.pre-commit-config.yaml
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
repos:
|
| 2 |
+
- repo: https://github.com/pre-commit/pre-commit-hooks
|
| 3 |
+
rev: "v5.0.0"
|
| 4 |
+
hooks:
|
| 5 |
+
- id: check-case-conflict
|
| 6 |
+
- id: check-merge-conflict
|
| 7 |
+
- id: check-toml
|
| 8 |
+
- id: check-yaml
|
| 9 |
+
- id: check-json
|
| 10 |
+
exclude: ^.devcontainer/devcontainer.json
|
| 11 |
+
- id: pretty-format-json
|
| 12 |
+
exclude: ^.devcontainer/devcontainer.json
|
| 13 |
+
args: [--autofix, --no-sort-keys]
|
| 14 |
+
- id: end-of-file-fixer
|
| 15 |
+
exclude: ^linalg_zero/distillation/vllm/.*\.jinja$
|
| 16 |
+
- id: trailing-whitespace
|
| 17 |
+
exclude: ^linalg_zero/distillation/vllm/.*\.jinja$
|
| 18 |
+
|
| 19 |
+
- repo: https://github.com/astral-sh/ruff-pre-commit
|
| 20 |
+
rev: "v0.11.5"
|
| 21 |
+
hooks:
|
| 22 |
+
- id: ruff
|
| 23 |
+
args: [--exit-non-zero-on-fix]
|
| 24 |
+
exclude: ^linalg_zero/distillation/vllm/.*\.jinja$
|
| 25 |
+
- id: ruff-format
|
| 26 |
+
exclude: ^linalg_zero/distillation/vllm/.*\.jinja$
|
CHANGELOG.md
ADDED
|
@@ -0,0 +1,389 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# CHANGELOG
|
| 2 |
+
|
| 3 |
+
|
| 4 |
+
## v1.0.0 (2026-02-17)
|
| 5 |
+
|
| 6 |
+
### Bug Fixes
|
| 7 |
+
|
| 8 |
+
- **generate**: Remove ambiguities in problem generation ([`5339d04`](https://github.com/atomwalk12/linalg-zero/commit/5339d04bd7f1a8253e744a209f47179f67a47394))
|
| 9 |
+
- **distillation**: Remove vllm chat templates ([`57be920`](https://github.com/atomwalk12/linalg-zero/commit/57be920fdb2ca9524593b67b735dfee8716affc0))
|
| 10 |
+
- **grpo**: Adjust launch script parameters ([`a40be0e`](https://github.com/atomwalk12/linalg-zero/commit/a40be0ebcf73657a4d0c695ba933d379a31eef2b))
|
| 11 |
+
- **grpo**: Log statistics ([`b983abd`](https://github.com/atomwalk12/linalg-zero/commit/b983abd1760b26b62c2e101ac76e1e3290359ec9))
|
| 12 |
+
- **sft**: Adjust optional accuracy callback ([`16b7d32`](https://github.com/atomwalk12/linalg-zero/commit/16b7d322f5a50fd830c1ea5acd51e3ca20702454))
|
| 13 |
+
|
| 14 |
+
### Documentation
|
| 15 |
+
|
| 16 |
+
- **config**: Add sample config files ([`62290aa`](https://github.com/atomwalk12/linalg-zero/commit/62290aa84d011ef52078272d241204518a78cf16))
|
| 17 |
+
- **report**: Finalize project ([`ffbfcb2`](https://github.com/atomwalk12/linalg-zero/commit/ffbfcb24a0cc54bcd888b4b7c19148e75ffce4f7))
|
| 18 |
+
|
| 19 |
+
### Features
|
| 20 |
+
|
| 21 |
+
- **distillation**: Add data generation configs ([`6aab283`](https://github.com/atomwalk12/linalg-zero/commit/6aab283a343c6273bfdc274e7397dff76bb3fa5c))
|
| 22 |
+
- **distillation**: Add validation script ([`8344dba`](https://github.com/atomwalk12/linalg-zero/commit/8344dbad775768a7a74ffd83dec56587514e1c49))
|
| 23 |
+
- **distillation**: Improve multi-turn generation with progress tracking ([`f58eadf`](https://github.com/atomwalk12/linalg-zero/commit/f58eadf0083f135f2fee2405b43eca1c2705d84e))
|
| 24 |
+
- **grpo**: Add environment abstraction layer with base classes ([`edc9615`](https://github.com/atomwalk12/linalg-zero/commit/edc9615163385df75ad18aa15f918e534c45f7fa))
|
| 25 |
+
- **grpo**: Add linear algebra environment with reward computation ([`4e46e7c`](https://github.com/atomwalk12/linalg-zero/commit/4e46e7c9e0040ee34e5fca060f652272f0c6ed1f))
|
| 26 |
+
- **grpo**: Add linear algebra tools for matrix operations ([`d2f08b1`](https://github.com/atomwalk12/linalg-zero/commit/d2f08b1231c2c0f493bbea1f3f6718cb6829c0fa))
|
| 27 |
+
- **grpo**: Add reward model and RL training utilities ([`aba4bae`](https://github.com/atomwalk12/linalg-zero/commit/aba4baefdbbe9c21c01ed82d2a062f788062f9d6))
|
| 28 |
+
- **grpo**: Add task selection and training infrastructure ([`d82f8c6`](https://github.com/atomwalk12/linalg-zero/commit/d82f8c69506056c227825741ae7098eabbcb2067))
|
| 29 |
+
- **grpo**: Add tool calling agent implementation ([`b95ec50`](https://github.com/atomwalk12/linalg-zero/commit/b95ec50ce7956f897a1bde16481633cb2395c77b))
|
| 30 |
+
- **grpo**: Add training and evaluation scripts ([`38b99fd`](https://github.com/atomwalk12/linalg-zero/commit/38b99fd20ecf08d0fa7ce185b6094b1d782d90d9))
|
| 31 |
+
- **grpo**: Add utility modules for training infrastructure ([`7362699`](https://github.com/atomwalk12/linalg-zero/commit/73626995bdcfd365b40fcd6813eca5ee99f2b8c0))
|
| 32 |
+
- **grpo**: Add yaml config files ([`bcb5b68`](https://github.com/atomwalk12/linalg-zero/commit/bcb5b68ef026110a8fcee14da23e5bf4736c8251))
|
| 33 |
+
- **sft**: Add model evaluation and dataset preparation scripts ([`5e8ee4c`](https://github.com/atomwalk12/linalg-zero/commit/5e8ee4c9d8fdf90e825a8a907216dadf78e8103b))
|
| 34 |
+
- **sft**: Add yaml training configs ([`65ff234`](https://github.com/atomwalk12/linalg-zero/commit/65ff2348dc592d790bef130ecf593ce157ebdaf3))
|
| 35 |
+
- **sft**: Adjust tool evaluation callback ([`c7f2c9d`](https://github.com/atomwalk12/linalg-zero/commit/c7f2c9daf2b8506e3687bd92d1d90d6599c99aa7))
|
| 36 |
+
- **sft**: Improve tool calling accuracy callback with weave logging ([`823b263`](https://github.com/atomwalk12/linalg-zero/commit/823b263a447470a30993c809264dbb20494d1331))
|
| 37 |
+
- **sft**: Refactor diagnostics and callbacks and improve evaluation logging ([`10ae836`](https://github.com/atomwalk12/linalg-zero/commit/10ae836eb0c7af9b7756bc0690c4461f2eaf3c8d))
|
| 38 |
+
- **system_prompts**: Add SFT system prompt and improve tool usage guidelines ([`061d21a`](https://github.com/atomwalk12/linalg-zero/commit/061d21aa6de9dc5e0f5ff3ac97a8f71be7a83c2e))
|
| 39 |
+
|
| 40 |
+
### Refactoring
|
| 41 |
+
|
| 42 |
+
- Reorganize training entry points ([`5a65164`](https://github.com/atomwalk12/linalg-zero/commit/5a65164ceb460701ba946c745ddf5e8a1631b73e))
|
| 43 |
+
- **grpo**: Improve XML parser API and validation logic ([`fa80cba`](https://github.com/atomwalk12/linalg-zero/commit/fa80cba680bd6af515bc66a66b69a255b4e3f3d1))
|
| 44 |
+
|
| 45 |
+
|
| 46 |
+
## v0.3.0 (2025-09-29)
|
| 47 |
+
|
| 48 |
+
### Bug Fixes
|
| 49 |
+
|
| 50 |
+
- Normalize grpo dataset schema
|
| 51 |
+
([`8590141`](https://github.com/atomwalk12/linalg-zero/commit/8590141149580635669ffcb40b3ad89c09849772))
|
| 52 |
+
|
| 53 |
+
- **context**: Modify entropy budget validation precision
|
| 54 |
+
([`b27d237`](https://github.com/atomwalk12/linalg-zero/commit/b27d2376c8399c491c52ded7bfbf0b838824d306))
|
| 55 |
+
|
| 56 |
+
- **distillation**: Align parsed_messages size with the conversations size
|
| 57 |
+
([`6a0a6bd`](https://github.com/atomwalk12/linalg-zero/commit/6a0a6bd173a48de5036700379c75da4ea51eb6a6))
|
| 58 |
+
|
| 59 |
+
- this ensures simpler reasoning since the two arrays contain similar data - fix small problems
|
| 60 |
+
around malformed messages, which are not silently skipped
|
| 61 |
+
|
| 62 |
+
- **distillation**: Solve accumulation problem with tool-use statistics
|
| 63 |
+
([`523f2ec`](https://github.com/atomwalk12/linalg-zero/commit/523f2ecf0156b805bcb1b2d9aa14852de00e5acd))
|
| 64 |
+
|
| 65 |
+
- **entropy**: Allow data generation using varying precision levels
|
| 66 |
+
([`08bbddd`](https://github.com/atomwalk12/linalg-zero/commit/08bbddd6bf699bfa0327381cc24da303afea9dd9))
|
| 67 |
+
|
| 68 |
+
- **generation**: Add difficulty rating based number of tool calls performed to reach a solution
|
| 69 |
+
([`aa2bace`](https://github.com/atomwalk12/linalg-zero/commit/aa2bacea495661d2cbf23117bd751f079e1e3069))
|
| 70 |
+
|
| 71 |
+
- **generation**: Adjust matrices format and introduce better organization for the difficulty levels
|
| 72 |
+
([`00bb08f`](https://github.com/atomwalk12/linalg-zero/commit/00bb08f44a4ac4a6bda4559a7ada28bc73084811))
|
| 73 |
+
|
| 74 |
+
- **generation**: Fix circular dependency by moving the library types to a separate file
|
| 75 |
+
([`33ee32a`](https://github.com/atomwalk12/linalg-zero/commit/33ee32a3e2c26faa7da170b3adba812c1f6661ce))
|
| 76 |
+
|
| 77 |
+
- **generator**: Use Dirichlet distribution for entropy allocation
|
| 78 |
+
([`0a16a52`](https://github.com/atomwalk12/linalg-zero/commit/0a16a52590aa038e3d2780e6378be275bd5e8b1b))
|
| 79 |
+
|
| 80 |
+
### Documentation
|
| 81 |
+
|
| 82 |
+
- Improve generated statistics on tool execution
|
| 83 |
+
([`7c62398`](https://github.com/atomwalk12/linalg-zero/commit/7c62398a065a2c746dd95ed5b2bd21314b2bab29))
|
| 84 |
+
|
| 85 |
+
### Features
|
| 86 |
+
|
| 87 |
+
- Enhance distillation pipeline with new model configurations and diagnostics support
|
| 88 |
+
([`0559239`](https://github.com/atomwalk12/linalg-zero/commit/05592398c9be27b9c364643ddc5b88cb19f4cdfe))
|
| 89 |
+
|
| 90 |
+
- Update distillation configurations
|
| 91 |
+
([`b43e149`](https://github.com/atomwalk12/linalg-zero/commit/b43e149a411f3f4587d76fbdd67d135cac91896c))
|
| 92 |
+
|
| 93 |
+
- **base_generator**: Add ability to redirect arbitrarily matrix component across composition
|
| 94 |
+
contexts
|
| 95 |
+
([`1c57a61`](https://github.com/atomwalk12/linalg-zero/commit/1c57a61ca13327ec2bde9be7539cb5e331452f9f))
|
| 96 |
+
|
| 97 |
+
- **distillation**: Add base script to run the distillation pipeline
|
| 98 |
+
([`2189bb9`](https://github.com/atomwalk12/linalg-zero/commit/2189bb9303c0a2bb84331a81e200d9dae19724bd))
|
| 99 |
+
|
| 100 |
+
- simplifies the previous approach to use only 1 task for multi-turn conversations
|
| 101 |
+
|
| 102 |
+
- **generation**: Add entropy allocation using dirichlet distribution
|
| 103 |
+
([`03d4bcb`](https://github.com/atomwalk12/linalg-zero/commit/03d4bcb88a8ad7f3c2cb9b6e89da3f30e71e816d))
|
| 104 |
+
|
| 105 |
+
- **generation**: Add matrix vector multiplication generator
|
| 106 |
+
([`a1e0ff1`](https://github.com/atomwalk12/linalg-zero/commit/a1e0ff1c2f44a48aef44d53ec68ffa27f3494788))
|
| 107 |
+
|
| 108 |
+
- **generation**: Add result verification step involving sympy-primitive conversion
|
| 109 |
+
([`07f8d51`](https://github.com/atomwalk12/linalg-zero/commit/07f8d517e886e46821fc6c8fa09a4bba00ab6e78))
|
| 110 |
+
|
| 111 |
+
- **generation**: Generate a fixed number of examples per class
|
| 112 |
+
([`4eb251a`](https://github.com/atomwalk12/linalg-zero/commit/4eb251ac933e708520a46e22ae5f6e9dd47cb113))
|
| 113 |
+
|
| 114 |
+
- **generator**: Add composition infrastructure to allow mixture of problems
|
| 115 |
+
([`d08820b`](https://github.com/atomwalk12/linalg-zero/commit/d08820bfa258f85cb5d533c9ccac5bb26940bcce))
|
| 116 |
+
|
| 117 |
+
- **generator**: Add constrained matrix generation for composed components
|
| 118 |
+
([`76faaf1`](https://github.com/atomwalk12/linalg-zero/commit/76faaf10984ff94b911252147c77c9a41a633512))
|
| 119 |
+
|
| 120 |
+
- **generator**: Add difficulty data-class for managing difficulty parameters
|
| 121 |
+
([`33eae33`](https://github.com/atomwalk12/linalg-zero/commit/33eae3355afb9255f5252eb9c661fecec612130d))
|
| 122 |
+
|
| 123 |
+
- **generator**: Add frobenius norm generator atomic operation
|
| 124 |
+
([`c3c53b7`](https://github.com/atomwalk12/linalg-zero/commit/c3c53b7e6f036f99c8ae142dd0aad25c7113c1b4))
|
| 125 |
+
|
| 126 |
+
- refactor to differentiate between independent and dependent components - add tests for composing
|
| 127 |
+
linear-system -> matrix multiplication -> frobenius norm
|
| 128 |
+
|
| 129 |
+
- **generator**: Add frobenius norm solver
|
| 130 |
+
([`7e346b3`](https://github.com/atomwalk12/linalg-zero/commit/7e346b36aad034a14d0a1ac71f6a2f25115a783d))
|
| 131 |
+
|
| 132 |
+
- **generator**: Add generator for solving linear systems
|
| 133 |
+
([`37faf45`](https://github.com/atomwalk12/linalg-zero/commit/37faf45a86b1c0068d19a1391174c1618c7ca77b))
|
| 134 |
+
|
| 135 |
+
- **generator**: Add integer/rational number generators using on controllable complexity
|
| 136 |
+
([`731100f`](https://github.com/atomwalk12/linalg-zero/commit/731100f609c6591a1fe2297ff0c64294195035d2))
|
| 137 |
+
|
| 138 |
+
- **generator**: Add matrix cofactor generator and related components
|
| 139 |
+
([`347e8b3`](https://github.com/atomwalk12/linalg-zero/commit/347e8b3388ae2ed5858a3b78f1fadb2ec15a44ba))
|
| 140 |
+
|
| 141 |
+
- **generator**: Add matrix inverse generator and related components
|
| 142 |
+
([`0154fef`](https://github.com/atomwalk12/linalg-zero/commit/0154fef50dfaa6a31dea0eeb78abd51144eca211))
|
| 143 |
+
|
| 144 |
+
- **generator**: Add matrix trace generator and related functionality
|
| 145 |
+
([`6cee5cd`](https://github.com/atomwalk12/linalg-zero/commit/6cee5cdc31b82b0606f1b3655e3bd592831e2514))
|
| 146 |
+
|
| 147 |
+
- **generator**: Add matrix transpose generator
|
| 148 |
+
([`32eae5c`](https://github.com/atomwalk12/linalg-zero/commit/32eae5cacb8b7472da3a3db7c3e5d890883b7df6))
|
| 149 |
+
|
| 150 |
+
- **generator**: Add new problem solver for finding matrix determinant
|
| 151 |
+
([`c700f05`](https://github.com/atomwalk12/linalg-zero/commit/c700f0537f49b75e59793fbe26aa861d1a36598f))
|
| 152 |
+
|
| 153 |
+
- **generator**: Add rank generator
|
| 154 |
+
([`6fdedf4`](https://github.com/atomwalk12/linalg-zero/commit/6fdedf41ec9437bc3500595fe21c36ca5dc603de))
|
| 155 |
+
|
| 156 |
+
- **generator**: Add SymPy problem generation base class with context management and template
|
| 157 |
+
handling
|
| 158 |
+
([`c94e860`](https://github.com/atomwalk12/linalg-zero/commit/c94e860ad06ff8298a6fcc1233a3de04521187d3))
|
| 159 |
+
|
| 160 |
+
- **generator**: Add template engine to help with diverse problem generation
|
| 161 |
+
([`d52b4eb`](https://github.com/atomwalk12/linalg-zero/commit/d52b4ebe9844c8ad808feef50fb0cfa64b020060))
|
| 162 |
+
|
| 163 |
+
- **generator**: Add validation and wrappers for generating dependent components
|
| 164 |
+
([`d557dfe`](https://github.com/atomwalk12/linalg-zero/commit/d557dfefbeb2c421d3e9f13bf11612972680622e))
|
| 165 |
+
|
| 166 |
+
- **generator**: Allow dependencies between components to be referenced across problems
|
| 167 |
+
([`ebf0d4a`](https://github.com/atomwalk12/linalg-zero/commit/ebf0d4adc3209be869a7bc88bf51aa2206ee8c7d))
|
| 168 |
+
|
| 169 |
+
- **generator**: Improve dataset generation with optimized registry and added constraints to integer
|
| 170 |
+
generation
|
| 171 |
+
([`6352e35`](https://github.com/atomwalk12/linalg-zero/commit/6352e35b4550eb65778cdbaa82c8f6f3536d704f))
|
| 172 |
+
|
| 173 |
+
- **generator**: Improve factory registration API
|
| 174 |
+
([`5a13a11`](https://github.com/atomwalk12/linalg-zero/commit/5a13a1144405f9add7b0a9e271871d53448b89e1))
|
| 175 |
+
|
| 176 |
+
- **generator**: Separate matrix-matrix multiplication from matrix-vector multiplication generators
|
| 177 |
+
([`b07e5bb`](https://github.com/atomwalk12/linalg-zero/commit/b07e5bb23a8d709ff46f6bae88380747ec2f7c59))
|
| 178 |
+
|
| 179 |
+
- **generator**: Simplify component logic by transferring the responsibility to the base class for
|
| 180 |
+
question/answer generation and verification
|
| 181 |
+
([`8f880f5`](https://github.com/atomwalk12/linalg-zero/commit/8f880f5d81232fe2361fac3669d1155adff0d754))
|
| 182 |
+
|
| 183 |
+
- **grpo**: Add GRPO configuration files and reward functions
|
| 184 |
+
([`04557c4`](https://github.com/atomwalk12/linalg-zero/commit/04557c46114c56fb4ad4b7cd91cbf1a3403a763e))
|
| 185 |
+
|
| 186 |
+
- Added GRPO training scripts and configuration files. - Introduced new linear algebra functions and
|
| 187 |
+
tools for GRPO. - Implemented dataset preparation and processing for GRPO training. - Added
|
| 188 |
+
support for multi-turn interactions in training. - Created a comprehensive debug dataset for
|
| 189 |
+
testing and validation.
|
| 190 |
+
|
| 191 |
+
- **multi-turn**: Implement multi-turn generation with tool use and verification
|
| 192 |
+
([`85f9314`](https://github.com/atomwalk12/linalg-zero/commit/85f93149fc0c45fd00ec9f2d806460b051f68979))
|
| 193 |
+
|
| 194 |
+
- **templates**: Adjust the templates to ensure a consistent format
|
| 195 |
+
([`7cde039`](https://github.com/atomwalk12/linalg-zero/commit/7cde039a9dacaaf417c5e57443ef50290f52db16))
|
| 196 |
+
|
| 197 |
+
- **verification**: Add checks to ensure generated data is valid
|
| 198 |
+
([`af961f8`](https://github.com/atomwalk12/linalg-zero/commit/af961f83090714757b7fe53f889835201c39d02f))
|
| 199 |
+
|
| 200 |
+
### Refactoring
|
| 201 |
+
|
| 202 |
+
- Add enum for available problem types
|
| 203 |
+
([`ca9bc90`](https://github.com/atomwalk12/linalg-zero/commit/ca9bc903188b9b6ba33d718548b5f24300e58eaa))
|
| 204 |
+
|
| 205 |
+
- Auto entropy allocation in base class based on difficulty
|
| 206 |
+
([`6f56296`](https://github.com/atomwalk12/linalg-zero/commit/6f56296e69e39224017446808ffb56d71a981d4b))
|
| 207 |
+
|
| 208 |
+
- Centralise using a mixin class the template generation method to ensure consistent generation
|
| 209 |
+
across all components
|
| 210 |
+
([`0b6b3c7`](https://github.com/atomwalk12/linalg-zero/commit/0b6b3c727674fbf6ab319c0196f26a99684789ba))
|
| 211 |
+
|
| 212 |
+
- Customise question layout
|
| 213 |
+
([`d527db0`](https://github.com/atomwalk12/linalg-zero/commit/d527db0befed2da79a2cde57d0d894a05ebb13af))
|
| 214 |
+
|
| 215 |
+
- Minor tweaks
|
| 216 |
+
([`d72ec79`](https://github.com/atomwalk12/linalg-zero/commit/d72ec79e23bf9049262ac05dd1207423f8a9aa35))
|
| 217 |
+
|
| 218 |
+
- Simplify tool accuracy callback
|
| 219 |
+
([`8305221`](https://github.com/atomwalk12/linalg-zero/commit/8305221c2759d9b9df45c2e8c31baf2355b24e15))
|
| 220 |
+
|
| 221 |
+
- **composition**: Remove unnecessary code
|
| 222 |
+
([`50f0258`](https://github.com/atomwalk12/linalg-zero/commit/50f02586d080b4edde21014d09d345d49ef5b959))
|
| 223 |
+
|
| 224 |
+
- **generator**: Add GenerationConstraints for matrix generation constraints
|
| 225 |
+
([`a5f763f`](https://github.com/atomwalk12/linalg-zero/commit/a5f763f0f077345edb400b496f0e148ccd7b5942))
|
| 226 |
+
|
| 227 |
+
- **generator**: Encapsulate generation parameters in the config
|
| 228 |
+
([`b89cabb`](https://github.com/atomwalk12/linalg-zero/commit/b89cabbc62c04fa44041cd5d5c56c1703410af8f))
|
| 229 |
+
|
| 230 |
+
- **generator**: Improve type safety and update function signatures and imports
|
| 231 |
+
([`16cd9ea`](https://github.com/atomwalk12/linalg-zero/commit/16cd9eade56b3f44476af1ea3da0c820c71e6ba3))
|
| 232 |
+
|
| 233 |
+
- **generator**: Improve type safety and update function signatures and imports
|
| 234 |
+
([`39f080e`](https://github.com/atomwalk12/linalg-zero/commit/39f080e4809ed96ed3b72f7a668d2893bdd1d1ef))
|
| 235 |
+
|
| 236 |
+
- **generator**: Sample entropy in a centralized place within the context
|
| 237 |
+
([`d920fcf`](https://github.com/atomwalk12/linalg-zero/commit/d920fcfb1c81241c14eb3118e28df411fa87671a))
|
| 238 |
+
|
| 239 |
+
- **generator**: Unify problem types and improve entropy management
|
| 240 |
+
([`fc309c2`](https://github.com/atomwalk12/linalg-zero/commit/fc309c247ac07d0ed05de3227a275c408db6bfea))
|
| 241 |
+
|
| 242 |
+
- **generator**: Update matrix operations to use centralized library calls
|
| 243 |
+
([`bdd537b`](https://github.com/atomwalk12/linalg-zero/commit/bdd537be0b628b7b8f7f5da5528f0aaed00fa7ab))
|
| 244 |
+
|
| 245 |
+
|
| 246 |
+
## v0.2.0 (2025-08-03)
|
| 247 |
+
|
| 248 |
+
### Bug Fixes
|
| 249 |
+
|
| 250 |
+
- Add logging utilities for files and stdout
|
| 251 |
+
([`d2692e6`](https://github.com/atomwalk12/linalg-zero/commit/d2692e68bbedabb5d110582737b3d995e26d64a2))
|
| 252 |
+
|
| 253 |
+
- Be granular about exceptions being thrown
|
| 254 |
+
([`4c88f9f`](https://github.com/atomwalk12/linalg-zero/commit/4c88f9fc8a14effb11b49ce90fb323122e0c4916))
|
| 255 |
+
|
| 256 |
+
Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com>
|
| 257 |
+
|
| 258 |
+
- Ensure an exception is raised if the subprocess returns a non-zero exit code
|
| 259 |
+
([`4695ab9`](https://github.com/atomwalk12/linalg-zero/commit/4695ab9c9afa4b56ec4167d99777b6b230d01ac7))
|
| 260 |
+
|
| 261 |
+
Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com>
|
| 262 |
+
|
| 263 |
+
- Finetune the number of Llama-cpp GPU layers offloaded to the GPU
|
| 264 |
+
([`a37fc13`](https://github.com/atomwalk12/linalg-zero/commit/a37fc130ab9b04be0ecccfe1278c6e1fe68d3350))
|
| 265 |
+
|
| 266 |
+
- **dependencies**: Pin llama-cpp-python version to 0.3.13), update distilabel dependencies and add
|
| 267 |
+
lock file
|
| 268 |
+
([`ef18d53`](https://github.com/atomwalk12/linalg-zero/commit/ef18d53a9fda569c6148ab8130557ce689d7c7ba))
|
| 269 |
+
|
| 270 |
+
- **distillation**: Add script to push debugging dataset to huggingface
|
| 271 |
+
([`6d6c91a`](https://github.com/atomwalk12/linalg-zero/commit/6d6c91ac275aaaee1d145bbb2d99296da823c617))
|
| 272 |
+
|
| 273 |
+
- **inference**: Add hf_pretrained_model_name_or_path and remove redundant installation in launch
|
| 274 |
+
script
|
| 275 |
+
([`75be580`](https://github.com/atomwalk12/linalg-zero/commit/75be58003bf3fd321deb185d117c7fc43ba11a57))
|
| 276 |
+
|
| 277 |
+
### Build System
|
| 278 |
+
|
| 279 |
+
- Remove python 3.9 support
|
| 280 |
+
([`e7421e6`](https://github.com/atomwalk12/linalg-zero/commit/e7421e611029b15f21ac9dcef2c4e14d97e99f9b))
|
| 281 |
+
|
| 282 |
+
### Features
|
| 283 |
+
|
| 284 |
+
- Add configuration parameters, tasks for running distributed training and pin dependencies
|
| 285 |
+
([`180fa5f`](https://github.com/atomwalk12/linalg-zero/commit/180fa5f4ccabf0f45d5c02371b80d0774a9cace5))
|
| 286 |
+
|
| 287 |
+
- Add dataset generator utility
|
| 288 |
+
([`279b0bd`](https://github.com/atomwalk12/linalg-zero/commit/279b0bdcf867b586c3b201afe425da1e93f2bfea))
|
| 289 |
+
|
| 290 |
+
- Add workflow to generate a new dataset
|
| 291 |
+
([`e0ba35d`](https://github.com/atomwalk12/linalg-zero/commit/e0ba35d7792309627dcca34293d11cb758ac3def))
|
| 292 |
+
|
| 293 |
+
- Implement question generation factories for arithmetic and linear algebra
|
| 294 |
+
([`f20fb2c`](https://github.com/atomwalk12/linalg-zero/commit/f20fb2c97d825d059eb6612d1f7cd4504c41f74c))
|
| 295 |
+
|
| 296 |
+
- make use of a global registry to keep track of the various problem definitions
|
| 297 |
+
|
| 298 |
+
- **distillation**: Add centralised control for launching the inference server
|
| 299 |
+
([`23a4078`](https://github.com/atomwalk12/linalg-zero/commit/23a4078dc59d1706989850f32d67786db32191ec))
|
| 300 |
+
|
| 301 |
+
- **distillation**: Add filter to easily track and discard incorrect results
|
| 302 |
+
([`d991534`](https://github.com/atomwalk12/linalg-zero/commit/d991534a1f6eee0c691520f7d0a53bbdd0fbb341))
|
| 303 |
+
|
| 304 |
+
- **distillation**: Add generation pipeline
|
| 305 |
+
([`d18317f`](https://github.com/atomwalk12/linalg-zero/commit/d18317f32e6e03dbb04e124560c58c731af57ba9))
|
| 306 |
+
|
| 307 |
+
- **distillation**: Add local script for llama.cpp inference server
|
| 308 |
+
([`bf1c3e1`](https://github.com/atomwalk12/linalg-zero/commit/bf1c3e18ff1ff5cf39b47b8df3319898b2f41c3a))
|
| 309 |
+
|
| 310 |
+
- **distillation**: Add planning and tool selection
|
| 311 |
+
([`6b9e3e2`](https://github.com/atomwalk12/linalg-zero/commit/6b9e3e2e952267ce9cd5796bcb0d448a48f98a3e))
|
| 312 |
+
|
| 313 |
+
- **distillation**: Add result synthesiser
|
| 314 |
+
([`017a47c`](https://github.com/atomwalk12/linalg-zero/commit/017a47ca3204fb0a27c21532ba1e9fd480dae7a6))
|
| 315 |
+
|
| 316 |
+
- **distillation**: Add the argilla components for simpler result inspection
|
| 317 |
+
([`20b7649`](https://github.com/atomwalk12/linalg-zero/commit/20b7649f88621cd5d5fea11624ce4e5fde8441de))
|
| 318 |
+
|
| 319 |
+
- **distillation**: Add the planner component
|
| 320 |
+
([`ed5175a`](https://github.com/atomwalk12/linalg-zero/commit/ed5175aca518ed6d593ae863e00f6e84a02f6f4e))
|
| 321 |
+
|
| 322 |
+
- **distillation**: Add verl dependency for GRPO
|
| 323 |
+
([`9d7ccb6`](https://github.com/atomwalk12/linalg-zero/commit/9d7ccb65c8488760e566cda5aabb345ef41a78f6))
|
| 324 |
+
|
| 325 |
+
- **distillation**: Code execution component
|
| 326 |
+
([`24072e7`](https://github.com/atomwalk12/linalg-zero/commit/24072e75338875335d1efd226df6b72339f0c3c4))
|
| 327 |
+
|
| 328 |
+
- **distillation**: Customise the chat generation pipeline to preserve input/output results
|
| 329 |
+
([`9973a10`](https://github.com/atomwalk12/linalg-zero/commit/9973a1009b1400a3e0cc0e0562b1464ffbd291b8))
|
| 330 |
+
|
| 331 |
+
- **distillation**: Demonstrate the tool selection component and update planner to use
|
| 332 |
+
ChatGeneration
|
| 333 |
+
([`8fe4e0a`](https://github.com/atomwalk12/linalg-zero/commit/8fe4e0a840dfa665b80a89b2a62e3f07fb5806d5))
|
| 334 |
+
|
| 335 |
+
- **distillation**: Implement function calling pipeline using Llama-cpp
|
| 336 |
+
([`f8776ce`](https://github.com/atomwalk12/linalg-zero/commit/f8776cedf82002f1c08a18f9252a929d70991dd1))
|
| 337 |
+
|
| 338 |
+
- **distillation**: Improve launch script to download models from the hf-hub and tune configuration
|
| 339 |
+
parameters
|
| 340 |
+
([`6fc9503`](https://github.com/atomwalk12/linalg-zero/commit/6fc950382725f00268b4b9b671c90abed1f3ab02))
|
| 341 |
+
|
| 342 |
+
- **distillation**: Integrate all related components to generate new data (completed pipeline)
|
| 343 |
+
([`c8532a4`](https://github.com/atomwalk12/linalg-zero/commit/c8532a4d5a6da3bfb7ce82e6de10f81f033ab826))
|
| 344 |
+
|
| 345 |
+
- **distillation**: Integrate math-verify for formal evaluation of the output
|
| 346 |
+
([`a2c3757`](https://github.com/atomwalk12/linalg-zero/commit/a2c3757e216d462ed9915a179e517382c68639bd))
|
| 347 |
+
|
| 348 |
+
- **sft**: Add additional callbacks (i.e. evaluation, push revision to hub, early stopping)
|
| 349 |
+
([`90e7f35`](https://github.com/atomwalk12/linalg-zero/commit/90e7f3532e0ca502f0edda571e5b33889cb41b89))
|
| 350 |
+
|
| 351 |
+
- **sft**: Complete evaluation with the ability to resume training, log results via wandb, create
|
| 352 |
+
model cards and save to the huggingface hub
|
| 353 |
+
([`a7d86a3`](https://github.com/atomwalk12/linalg-zero/commit/a7d86a3e5c59ab527e9d3ebc5779f4d35065b2e7))
|
| 354 |
+
|
| 355 |
+
### Testing
|
| 356 |
+
|
| 357 |
+
- Add dataset configuration file
|
| 358 |
+
([`5372677`](https://github.com/atomwalk12/linalg-zero/commit/5372677debfcb91b17c0bbc5af665bd19872c3ba))
|
| 359 |
+
|
| 360 |
+
- Check default registry configuration
|
| 361 |
+
([`47f190a`](https://github.com/atomwalk12/linalg-zero/commit/47f190a94e045650b3c1cffee3d4888d26b16164))
|
| 362 |
+
|
| 363 |
+
|
| 364 |
+
## v0.1.0 (2025-07-08)
|
| 365 |
+
|
| 366 |
+
### Bug Fixes
|
| 367 |
+
|
| 368 |
+
- Add container fix for Docker
|
| 369 |
+
([`bf3884b`](https://github.com/atomwalk12/linalg-zero/commit/bf3884b10d03f6dfa253944c2497297bc91d32d2))
|
| 370 |
+
|
| 371 |
+
- Improve release pipeline to include semantic releases
|
| 372 |
+
([`888738c`](https://github.com/atomwalk12/linalg-zero/commit/888738c4b914c65e001025ca3ba4473c1712f235))
|
| 373 |
+
|
| 374 |
+
### Features
|
| 375 |
+
|
| 376 |
+
- Add additional tasks
|
| 377 |
+
([`c036f31`](https://github.com/atomwalk12/linalg-zero/commit/c036f31ba63af43b205e7a73464935f98f602773))
|
| 378 |
+
|
| 379 |
+
- Add codeql support
|
| 380 |
+
([`9b337c6`](https://github.com/atomwalk12/linalg-zero/commit/9b337c6eb7892943e2e6e1d004ec8611f868e0bd))
|
| 381 |
+
|
| 382 |
+
- Add gemini style guide
|
| 383 |
+
([`0f65ed1`](https://github.com/atomwalk12/linalg-zero/commit/0f65ed1847f72f900cf3d37e0766e1d06269c451))
|
| 384 |
+
|
| 385 |
+
- Set up semantic release
|
| 386 |
+
([`a97ecd3`](https://github.com/atomwalk12/linalg-zero/commit/a97ecd319d69c348b1a3a760c00ad6678a7b7339))
|
| 387 |
+
|
| 388 |
+
- Update mkdocs theme
|
| 389 |
+
([`8dd0a40`](https://github.com/atomwalk12/linalg-zero/commit/8dd0a40dd627c64ce868d557b8c889c7c6a24d0d))
|
CONTRIBUTING.md
ADDED
|
@@ -0,0 +1,126 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Contributing to `linalg-zero`
|
| 2 |
+
|
| 3 |
+
Contributions are welcome, and they are greatly appreciated!
|
| 4 |
+
Every little bit helps, and credit will always be given.
|
| 5 |
+
|
| 6 |
+
You can contribute in many ways:
|
| 7 |
+
|
| 8 |
+
# Types of Contributions
|
| 9 |
+
|
| 10 |
+
## Report Bugs
|
| 11 |
+
|
| 12 |
+
Report bugs at https://github.com/atomwalk12/linalg-zero/issues
|
| 13 |
+
|
| 14 |
+
If you are reporting a bug, please include:
|
| 15 |
+
|
| 16 |
+
- Your operating system name and version.
|
| 17 |
+
- Any details about your local setup that might be helpful in troubleshooting.
|
| 18 |
+
- Detailed steps to reproduce the bug.
|
| 19 |
+
|
| 20 |
+
## Fix Bugs
|
| 21 |
+
|
| 22 |
+
Look through the GitHub issues for bugs.
|
| 23 |
+
Anything tagged with "bug" and "help wanted" is open to whoever wants to implement a fix for it.
|
| 24 |
+
|
| 25 |
+
## Implement Features
|
| 26 |
+
|
| 27 |
+
Look through the GitHub issues for features.
|
| 28 |
+
Anything tagged with "enhancement" and "help wanted" is open to whoever wants to implement it.
|
| 29 |
+
|
| 30 |
+
## Write Documentation
|
| 31 |
+
|
| 32 |
+
linalg-zero could always use more documentation, whether as part of the official docs, in docstrings, or even on the web in blog posts, articles, and such.
|
| 33 |
+
|
| 34 |
+
## Submit Feedback
|
| 35 |
+
|
| 36 |
+
The best way to send feedback is to file an issue at https://github.com/atomwalk12/linalg-zero/issues.
|
| 37 |
+
|
| 38 |
+
If you are proposing a new feature:
|
| 39 |
+
|
| 40 |
+
- Explain in detail how it would work.
|
| 41 |
+
- Keep the scope as narrow as possible, to make it easier to implement.
|
| 42 |
+
- Remember that this is a volunteer-driven project, and that contributions
|
| 43 |
+
are welcome :)
|
| 44 |
+
|
| 45 |
+
# Get Started!
|
| 46 |
+
|
| 47 |
+
Ready to contribute? Here's how to set up `linalg-zero` for local development.
|
| 48 |
+
Please note this documentation assumes you already have `uv` and `Git` installed and ready to go.
|
| 49 |
+
|
| 50 |
+
1. Fork the `linalg-zero` repo on GitHub.
|
| 51 |
+
|
| 52 |
+
2. Clone your fork locally:
|
| 53 |
+
|
| 54 |
+
```bash
|
| 55 |
+
cd <directory_in_which_repo_should_be_created>
|
| 56 |
+
git clone git@github.com:YOUR_NAME/linalg-zero.git
|
| 57 |
+
```
|
| 58 |
+
|
| 59 |
+
3. Now we need to install the environment. Navigate into the directory
|
| 60 |
+
|
| 61 |
+
```bash
|
| 62 |
+
cd linalg-zero
|
| 63 |
+
```
|
| 64 |
+
|
| 65 |
+
Then, install and activate the environment with:
|
| 66 |
+
|
| 67 |
+
```bash
|
| 68 |
+
uv sync --group dev --group test
|
| 69 |
+
```
|
| 70 |
+
|
| 71 |
+
4. Install pre-commit to run linters/formatters at commit time:
|
| 72 |
+
|
| 73 |
+
```bash
|
| 74 |
+
uv run pre-commit install
|
| 75 |
+
```
|
| 76 |
+
|
| 77 |
+
5. Create a branch for local development:
|
| 78 |
+
|
| 79 |
+
```bash
|
| 80 |
+
git checkout -b name-of-your-bugfix-or-feature
|
| 81 |
+
```
|
| 82 |
+
|
| 83 |
+
Now you can make your changes locally.
|
| 84 |
+
|
| 85 |
+
6. Don't forget to add test cases for your added functionality to the `tests` directory.
|
| 86 |
+
|
| 87 |
+
7. When you're done making changes, check that your changes pass the formatting tests.
|
| 88 |
+
|
| 89 |
+
```bash
|
| 90 |
+
make check
|
| 91 |
+
```
|
| 92 |
+
|
| 93 |
+
Now, validate that all unit tests are passing:
|
| 94 |
+
|
| 95 |
+
```bash
|
| 96 |
+
make test
|
| 97 |
+
```
|
| 98 |
+
|
| 99 |
+
9. Before raising a pull request you should also run tox.
|
| 100 |
+
This will run the tests across different versions of Python:
|
| 101 |
+
|
| 102 |
+
```bash
|
| 103 |
+
tox
|
| 104 |
+
```
|
| 105 |
+
|
| 106 |
+
This requires you to have multiple versions of python installed.
|
| 107 |
+
This step is also triggered in the CI/CD pipeline, so you could also choose to skip this step locally.
|
| 108 |
+
|
| 109 |
+
10. Commit your changes and push your branch to GitHub:
|
| 110 |
+
|
| 111 |
+
```bash
|
| 112 |
+
git add .
|
| 113 |
+
git commit -m "Your detailed description of your changes."
|
| 114 |
+
git push origin name-of-your-bugfix-or-feature
|
| 115 |
+
```
|
| 116 |
+
|
| 117 |
+
11. Submit a pull request through the GitHub website.
|
| 118 |
+
|
| 119 |
+
# Pull Request Guidelines
|
| 120 |
+
|
| 121 |
+
Before you submit a pull request, check that it meets these guidelines:
|
| 122 |
+
|
| 123 |
+
1. The pull request should include tests.
|
| 124 |
+
|
| 125 |
+
2. If the pull request adds functionality, the docs should be updated.
|
| 126 |
+
Put your new functionality into a function with a docstring, and add the feature to the list in `README.md`.
|
Dockerfile
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Install uv
|
| 2 |
+
FROM python:3.12-slim
|
| 3 |
+
COPY --from=ghcr.io/astral-sh/uv:latest /uv /bin/uv
|
| 4 |
+
|
| 5 |
+
# Change the working directory to the `app` directory
|
| 6 |
+
WORKDIR /app
|
| 7 |
+
|
| 8 |
+
# Copy the lockfile and `pyproject.toml` into the image
|
| 9 |
+
COPY uv.lock /app/uv.lock
|
| 10 |
+
COPY pyproject.toml /app/pyproject.toml
|
| 11 |
+
|
| 12 |
+
# Install dependencies
|
| 13 |
+
RUN uv sync --frozen --no-install-project
|
| 14 |
+
|
| 15 |
+
# Copy the project into the image
|
| 16 |
+
COPY . /app
|
| 17 |
+
|
| 18 |
+
# Sync the project
|
| 19 |
+
RUN uv sync --frozen
|
| 20 |
+
|
| 21 |
+
CMD [ "python", "linalg_zero/generate.py" ]
|
LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
MIT License
|
| 2 |
+
|
| 3 |
+
Copyright (c) 2025 Razvan Florian Vasile
|
| 4 |
+
|
| 5 |
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
| 6 |
+
of this software and associated documentation files (the "Software"), to deal
|
| 7 |
+
in the Software without restriction, including without limitation the rights
|
| 8 |
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
| 9 |
+
copies of the Software, and to permit persons to whom the Software is
|
| 10 |
+
furnished to do so, subject to the following conditions:
|
| 11 |
+
|
| 12 |
+
The above copyright notice and this permission notice shall be included in all
|
| 13 |
+
copies or substantial portions of the Software.
|
| 14 |
+
|
| 15 |
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
| 16 |
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
| 17 |
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
| 18 |
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
| 19 |
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
| 20 |
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
| 21 |
+
SOFTWARE.
|
Makefile
ADDED
|
@@ -0,0 +1,166 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
## NOTE: the llama-cpp server is used to startup the inference server using `make distillation-server`
|
| 2 |
+
# Fixing the llama-cpp server version to 0.3.13 as the upstream repository gets updated frequently
|
| 3 |
+
# leading to incompatibility issues. If bumping the version don't forget to update pyproject.toml.
|
| 4 |
+
.PHONY: install-data-gen
|
| 5 |
+
install-data-gen: ## Install the virtual environment and install the pre-commit hooks.
|
| 6 |
+
@echo "🚀 Creating virtual environment using uv"
|
| 7 |
+
@uv sync --locked --group data-gen
|
| 8 |
+
@uv pip install setuptools flash-attn==2.7.3 --no-build-isolation
|
| 9 |
+
@uv run pre-commit install
|
| 10 |
+
|
| 11 |
+
.PHONY: install-sft
|
| 12 |
+
install-sft: ## Install the virtual environment and install the pre-commit hooks.
|
| 13 |
+
@echo "🚀 Creating virtual environment using uv"
|
| 14 |
+
@uv sync --locked --group sft
|
| 15 |
+
@uv pip install --upgrade setuptools ninja
|
| 16 |
+
@uv pip install --upgrade --torch-backend cu128 "xformers==0.0.33.post1"
|
| 17 |
+
@TORCH_CUDA_ARCH_LIST=8.9 MAX_JOBS=3 uv pip install --upgrade --no-build-isolation "flash-attn==2.8.3"
|
| 18 |
+
@uv run pre-commit install
|
| 19 |
+
|
| 20 |
+
.PHONY: install-grpo
|
| 21 |
+
install-grpo: ## Install the virtual environment and install the pre-commit hooks.
|
| 22 |
+
@echo "🚀 Creating virtual environment using uv"
|
| 23 |
+
@uv sync --locked --group grpo
|
| 24 |
+
@uv run pre-commit install
|
| 25 |
+
|
| 26 |
+
.PHONY: setup-dev
|
| 27 |
+
setup-dev: ## Setup the development environment
|
| 28 |
+
@echo "🚀 Setting up development environment"
|
| 29 |
+
@uv run linalg_zero/distillation/scripts/push_debug_dataset.py --dataset-name atomwalk12/linalg-debug --private
|
| 30 |
+
|
| 31 |
+
.PHONY: check
|
| 32 |
+
check: ## Run code quality tools.
|
| 33 |
+
@echo "🚀 Checking lock file consistency with 'pyproject.toml'"
|
| 34 |
+
@uv lock --locked
|
| 35 |
+
@echo "🚀 Linting code: Running pre-commit"
|
| 36 |
+
ifeq ($(CI),true)
|
| 37 |
+
@echo "🔍 CI detected: Running ruff in check mode"
|
| 38 |
+
@uv run ruff check .
|
| 39 |
+
@uv run ruff format --check .
|
| 40 |
+
@SKIP=ruff,ruff-format uv run pre-commit run -a
|
| 41 |
+
else
|
| 42 |
+
@uv run pre-commit run -a
|
| 43 |
+
endif
|
| 44 |
+
@echo "🚀 Static type checking: Running mypy"
|
| 45 |
+
@uv run mypy
|
| 46 |
+
@echo "🚀 Checking for obsolete dependencies: Running deptry"
|
| 47 |
+
@uv run deptry .
|
| 48 |
+
|
| 49 |
+
.PHONY: test
|
| 50 |
+
test: ## Test the code with pytest
|
| 51 |
+
@echo "🚀 Testing code: Running pytest"
|
| 52 |
+
@uv run python -m pytest --cov --cov-config=pyproject.toml --cov-report=xml
|
| 53 |
+
|
| 54 |
+
.PHONY: coverage-site
|
| 55 |
+
coverage-site: ## Generate coverage report in HTML format
|
| 56 |
+
@echo "🚀 Generating coverage report in HTML format"
|
| 57 |
+
@uv run coverage html
|
| 58 |
+
|
| 59 |
+
.PHONY: build
|
| 60 |
+
build: clean-build ## Build wheel file
|
| 61 |
+
@echo "🚀 Creating wheel file"
|
| 62 |
+
@uvx --from build pyproject-build --installer uv
|
| 63 |
+
|
| 64 |
+
.PHONY: clean-build
|
| 65 |
+
clean-build: ## Clean build artifacts
|
| 66 |
+
@echo "🚀 Removing build artifacts"
|
| 67 |
+
@uv run python -c "import shutil; import os; shutil.rmtree('dist') if os.path.exists('dist') else None"
|
| 68 |
+
|
| 69 |
+
.PHONY: publish
|
| 70 |
+
publish: ## Publish a release to PyPI.
|
| 71 |
+
@echo "🚀 Publishing."
|
| 72 |
+
@uvx twine upload --repository-url https://upload.pypi.org/legacy/ dist/*
|
| 73 |
+
|
| 74 |
+
.PHONY: build-and-publish
|
| 75 |
+
build-and-publish: build publish ## Build and publish.
|
| 76 |
+
|
| 77 |
+
.PHONY: docs-test
|
| 78 |
+
docs-test: ## Test if documentation can be built without warnings or errors
|
| 79 |
+
@echo "🚀 Testing documentation build"
|
| 80 |
+
@uv run mkdocs build -s
|
| 81 |
+
|
| 82 |
+
.PHONY: docs
|
| 83 |
+
docs: ## Build and serve the documentation
|
| 84 |
+
@echo "🚀 Building and serving documentation"
|
| 85 |
+
@uv run mkdocs serve
|
| 86 |
+
|
| 87 |
+
.PHONY: semantic-release
|
| 88 |
+
semantic-release: ## Test semantic release
|
| 89 |
+
@echo "🚀 Testing semantic release"
|
| 90 |
+
@uv run semantic-release -vv --noop version --print
|
| 91 |
+
|
| 92 |
+
.PHONY: gh-deploy
|
| 93 |
+
gh-deploy: ## Deploy the documentation to GitHub Pages
|
| 94 |
+
@echo "🚀 Deploying documentation to GitHub Pages"
|
| 95 |
+
@uv run mkdocs gh-deploy --force
|
| 96 |
+
|
| 97 |
+
LLAMACPP_CONFIG=linalg_zero/config/distillation/llamacpp_qwen3_32b_instruct.yaml
|
| 98 |
+
VLLM_CONFIG=linalg_zero/config/distillation/vllm_qwen3_32b.yaml
|
| 99 |
+
|
| 100 |
+
.PHONY: distillation-llamacpp
|
| 101 |
+
distillation-llamacpp: ## Start the llama.cpp server
|
| 102 |
+
@echo "🚀 Starting llama.cpp server"
|
| 103 |
+
@INFERENCE_BACKEND=llamacpp uv run python linalg_zero/distillation/launch_server.py --config $(LLAMACPP_CONFIG)
|
| 104 |
+
|
| 105 |
+
.PHONY: distillation-vllm
|
| 106 |
+
distillation-vllm: ## Start the vLLM server
|
| 107 |
+
@echo "🚀 Starting vLLM server"
|
| 108 |
+
@. ./env.sh && uv run python linalg_zero/distillation/launch_server.py --config ${VLLM_CONFIG}
|
| 109 |
+
|
| 110 |
+
.PHONY: distillation
|
| 111 |
+
distillation: ## Run the distillation pipeline using the vllm config
|
| 112 |
+
@echo "🚀 Running distillation pipeline"
|
| 113 |
+
@. ./env.sh && uv run python linalg_zero/distillation.py --config ${VLLM_CONFIG}
|
| 114 |
+
|
| 115 |
+
.PHONY: distillation-vllm-local
|
| 116 |
+
distillation-vllm-local: ## Start the vLLM server
|
| 117 |
+
@echo "🚀 Starting vLLM server"
|
| 118 |
+
@export USING_VLLM=true INFERENCE_BACKEND=vllm && uv run python linalg_zero/distillation/launch_server.py --config linalg_zero/config/distillation/vllm_qwen3_4b_think.yaml
|
| 119 |
+
|
| 120 |
+
|
| 121 |
+
.PHONY: distillation-local
|
| 122 |
+
distillation-debug: ## Start the vLLM server
|
| 123 |
+
@echo "🚀 Starting vLLM server"
|
| 124 |
+
@export USING_VLLM=true && uv run python linalg_zero/distillation.py --config linalg_zero/config/distillation/vllm_qwen3_4b_think.yaml
|
| 125 |
+
|
| 126 |
+
|
| 127 |
+
# SFT Training Commands
|
| 128 |
+
SFT_CONFIG=linalg_zero/config/sft/sft_debug_config.yaml
|
| 129 |
+
# SFT_CONFIG=linalg_zero/config/sft/sft_config.yaml
|
| 130 |
+
ACCELERATE_CONFIG=linalg_zero/config/sft/accelerate/zero3.yaml
|
| 131 |
+
|
| 132 |
+
.PHONY: sft-debug
|
| 133 |
+
sft-debug: ## Run SFT training on single GPU
|
| 134 |
+
@echo "🚀 Running SFT training on single GPU"
|
| 135 |
+
@uv run python linalg_zero/sft.py --config $(SFT_CONFIG)
|
| 136 |
+
|
| 137 |
+
|
| 138 |
+
.PHONY: sft-distributed
|
| 139 |
+
sft-distributed: ## Run SFT training with distributed setup using DeepSpeed ZeroStage 3
|
| 140 |
+
@echo "🚀 Running distributed SFT training with DeepSpeed"
|
| 141 |
+
@uv run accelerate launch --config_file=$(ACCELERATE_CONFIG) linalg_zero/sft.py --config $(SFT_CONFIG)
|
| 142 |
+
|
| 143 |
+
.PHONY: prepare-grpo-dataset
|
| 144 |
+
prepare-grpo-dataset: ## Prepare the GRPO dataset
|
| 145 |
+
@echo "🚀 Creating GRPO dataset"
|
| 146 |
+
@uv run linalg_zero/grpo/process_dataset.py
|
| 147 |
+
|
| 148 |
+
.PHONY: generate-optimised-config
|
| 149 |
+
generate-optimised-config: ## Generate the optimised config
|
| 150 |
+
@echo "🚀 Generating optimised config"
|
| 151 |
+
@uv run linalg_zero/generator/analysis/analyse.py
|
| 152 |
+
|
| 153 |
+
.PHONY: run-training
|
| 154 |
+
run-training: ## Run the training pipeline
|
| 155 |
+
@echo "🚀 Running training pipeline"
|
| 156 |
+
@$(MAKE) setup-dev
|
| 157 |
+
@$(MAKE) prepare-grpo-dataset
|
| 158 |
+
@echo "🚀 Training pipeline completed"
|
| 159 |
+
|
| 160 |
+
|
| 161 |
+
.PHONY: help
|
| 162 |
+
help:
|
| 163 |
+
@uv run python -c "import re; \
|
| 164 |
+
[[print(f'\033[36m{m[0]:<20}\033[0m {m[1]}') for m in re.findall(r'^([a-zA-Z_-]+):.*?## (.*)$$', open(makefile).read(), re.M)] for makefile in ('$(MAKEFILE_LIST)').strip().split()]"
|
| 165 |
+
|
| 166 |
+
.DEFAULT_GOAL := help
|
README.md
CHANGED
|
@@ -1,12 +1,155 @@
|
|
| 1 |
---
|
| 2 |
-
title: Linalg
|
| 3 |
-
emoji:
|
| 4 |
-
colorFrom:
|
| 5 |
-
colorTo:
|
| 6 |
sdk: gradio
|
| 7 |
-
sdk_version: 6.9.0
|
| 8 |
-
|
|
|
|
| 9 |
pinned: false
|
| 10 |
---
|
| 11 |
|
| 12 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
---
|
| 2 |
+
title: Linalg-Zero
|
| 3 |
+
emoji: 🧠
|
| 4 |
+
colorFrom: indigo
|
| 5 |
+
colorTo: purple
|
| 6 |
sdk: gradio
|
| 7 |
+
sdk_version: "6.9.0"
|
| 8 |
+
python_version: "3.12.12"
|
| 9 |
+
app_file: linalg_zero/demo/app.py
|
| 10 |
pinned: false
|
| 11 |
---
|
| 12 |
|
| 13 |
+
[](https://img.shields.io/github/v/release/atomwalk12/linalg-zero)
|
| 14 |
+
[](https://github.com/atomwalk12/linalg-zero/actions/workflows/main.yml?query=branch%3Amain)
|
| 15 |
+
[](LICENSE)
|
| 16 |
+
|
| 17 |
+
# Linalg-Zero
|
| 18 |
+
|
| 19 |
+
## Overview
|
| 20 |
+
|
| 21 |
+
<details>
|
| 22 |
+
<summary>Table of Contents</summary>
|
| 23 |
+
<ol>
|
| 24 |
+
<li><a href="#overview">Overview</a></li>
|
| 25 |
+
<li><a href="#main-phases">Main Phases</a></li>
|
| 26 |
+
<li><a href="#installation">Installation</a></li>
|
| 27 |
+
<li><a href="#quickstart">Quickstart</a></li>
|
| 28 |
+
<li><a href="#results">Results</a></li>
|
| 29 |
+
<li><a href="#artifacts">Artifacts</a></li>
|
| 30 |
+
<li><a href="#reproducibility">Reproducibility</a></li>
|
| 31 |
+
<li><a href="#acknowledgements">Acknowledgements</a></li>
|
| 32 |
+
</ol>
|
| 33 |
+
</details>
|
| 34 |
+
|
| 35 |
+
This repository offers tools for generating a linear algebra problem dataset and training an open-source base model (i.e. Qwen2.5-3B), aiming to explore planning and tool use using SFT and RL, distinct from Deepseek-R1's primary emphasis on reasoning.
|
| 36 |
+
|
| 37 |
+
The project is simple by design and mostly consists of:
|
| 38 |
+
|
| 39 |
+
- `linalg_zero/`: contains the scripts to train models as well as generate synthetic data:
|
| 40 |
+
- `generate.py`: generates the linear algebra dataset and splits.
|
| 41 |
+
- `distillation.py`: runs the distillation pipeline to create multi-turn tool-use data.
|
| 42 |
+
- `sft_train.py`: performs a simple SFT of a model on a dataset.
|
| 43 |
+
- `grpo_train.py`: trains a model with GRPO on a given dataset.
|
| 44 |
+
- `Makefile`: contains easy-to-run commands for the dataset and training workflows using previous scripts.
|
| 45 |
+
|
| 46 |
+
## Main Phases
|
| 47 |
+
|
| 48 |
+
We use the DeepSeek-R1 [tech report](https://github.com/deepseek-ai/DeepSeek-R1) as a loose guide, but the project phases are:
|
| 49 |
+
|
| 50 |
+
* Step 1: generate a linear algebra dataset with controlled difficulty and tool-call metadata.
|
| 51 |
+
* Step 2: distill multi-turn tool-use data from a teacher model.
|
| 52 |
+
* Step 3: SFT the base model on the dataset to teach the tool-calling format.
|
| 53 |
+
* Step 4: GRPO fine-tune on the tool-use tasks, using a curriculum.
|
| 54 |
+
|
| 55 |
+
|
| 56 |
+
## Installation
|
| 57 |
+
|
| 58 |
+
We use `uv` as the dependency management tool.
|
| 59 |
+
First, to install `uv`, follow the [UV Installation Guide](https://docs.astral.sh/uv/getting-started/installation/).
|
| 60 |
+
|
| 61 |
+
To run the experiments install the dependencies using:
|
| 62 |
+
|
| 63 |
+
* For generation/distillation: `make install-data-gen`
|
| 64 |
+
* For SFT: `make install-sft`
|
| 65 |
+
* For RL: `make install-grpo`
|
| 66 |
+
|
| 67 |
+
Next, log into your Hugging Face and Weights and Biases accounts as follows:
|
| 68 |
+
|
| 69 |
+
```shell
|
| 70 |
+
huggingface-cli login
|
| 71 |
+
wandb login
|
| 72 |
+
```
|
| 73 |
+
|
| 74 |
+
## Quickstart
|
| 75 |
+
|
| 76 |
+
After installing dependencies above, run the commands below. For modifications, see the config files.
|
| 77 |
+
|
| 78 |
+
```shell
|
| 79 |
+
# Phase 1: Generate dataset
|
| 80 |
+
uv run python linalg_zero/generate.py --dataset_name atomwalk12/linalgzero --push_dataset
|
| 81 |
+
|
| 82 |
+
# Phase 2: Distillation (setup once)
|
| 83 |
+
cp linalg_zero/config/distillation/env.example.sh env.sh
|
| 84 |
+
# Edit env.sh to set HF_TOKEN and ARGILLA_API_KEY.
|
| 85 |
+
source env.sh
|
| 86 |
+
|
| 87 |
+
# Terminal A
|
| 88 |
+
uv run python linalg_zero/distillation/launch_server.py --config linalg_zero/config/distillation/vllm_qwen3_32b.yaml
|
| 89 |
+
|
| 90 |
+
# Terminal B (new terminal; source env.sh again)
|
| 91 |
+
source env.sh
|
| 92 |
+
uv run python linalg_zero/distillation.py --config linalg_zero/config/distillation/vllm_qwen3_32b.yaml
|
| 93 |
+
|
| 94 |
+
# Phase 3: SFT
|
| 95 |
+
uv run python linalg_zero/sft_train.py --config linalg_zero/config/sft/qwen2.5-3B/lora.yaml
|
| 96 |
+
|
| 97 |
+
# Phase 4: GRPO
|
| 98 |
+
uv run python linalg_zero/grpo_train.py --config-name runpod.yaml
|
| 99 |
+
```
|
| 100 |
+
|
| 101 |
+
Training requires the dataset to follow the strict OpenAI tool-calling format (see [this link](https://huggingface.co/docs/trl/en/dataset_formats#tool-calling)). We provide scripts to prepare and validate the data accordingly:
|
| 102 |
+
|
| 103 |
+
- `linalg_zero/`
|
| 104 |
+
- `sft/scripts/prepare_dataset.py`: prepares the SFT dataset.
|
| 105 |
+
- `grpo/scripts/prepare_dataset.py`: prepares and validates the GRPO dataset.
|
| 106 |
+
|
| 107 |
+
## Results
|
| 108 |
+
|
| 109 |
+
We provide a recipe to encourage planning and tool-use capabilities in the [Qwen2.5-3B](https://huggingface.co/Qwen/Qwen2.5-3B) model, starting from a pre-trained (not instruction-tuned) base model.
|
| 110 |
+
|
| 111 |
+
This yields models like [Linalg-Zero-SFT](https://huggingface.co/atomwalk12/LinalgZero-SFT) and [Linalg-Zero-GRPO](https://huggingface.co/atomwalk12/LinalgZero-GRPO), with the following downstream performance on the test set:
|
| 112 |
+
|
| 113 |
+
|
| 114 |
+
| Metric | LinAlgZero-SFT | LinAlgZero-GRPO |
|
| 115 |
+
|--------------------|----------------|-----------------|
|
| 116 |
+
| Optimal Trajectory | 89.87% | 90.26% |
|
| 117 |
+
| Correctness | 91.86% | 92.63% |
|
| 118 |
+
| Format Validity | 96.15% | 96.66% |
|
| 119 |
+
| Tool Success | 100.00% | 100.00% |
|
| 120 |
+
|
| 121 |
+
### Artifacts
|
| 122 |
+
|
| 123 |
+
| Artifact | Link |
|
| 124 |
+
|---|---|
|
| 125 |
+
| SFT checkpoint | [atomwalk12/LinalgZero-SFT](https://huggingface.co/atomwalk12/LinalgZero-SFT) |
|
| 126 |
+
| GRPO checkpoint | [atomwalk12/LinAlgZero-GRPO](https://huggingface.co/atomwalk12/LinAlgZero-GRPO) |
|
| 127 |
+
| Base dataset | [atomwalk12/linalgzero](https://huggingface.co/datasets/atomwalk12/linalgzero) |
|
| 128 |
+
| Distilled dataset (clean) | [atomwalk12/linalgzero-distilled-clean](https://huggingface.co/datasets/atomwalk12/linalgzero-distilled-clean) |
|
| 129 |
+
| SFT dataset | [atomwalk12/linalgzero-sft](https://huggingface.co/datasets/atomwalk12/linalgzero-sft) |
|
| 130 |
+
| GRPO dataset | [atomwalk12/linalgzero-grpo](https://huggingface.co/datasets/atomwalk12/linalgzero-grpo) |
|
| 131 |
+
|
| 132 |
+
## Reproducibility
|
| 133 |
+
- **Distillation:** H100 80GB on [Runpod](https://www.runpod.io/) with [Qwen/Qwen3-32B-FP8](https://huggingface.co/Qwen/Qwen3-32B-FP8); 15 hours at $2.39/hr (~$25).
|
| 134 |
+
- **SFT:** Local 24GB RTX 4090 with [Qwen/Qwen2.5-3B](https://huggingface.co/Qwen/Qwen2.5-3B).
|
| 135 |
+
- **GRPO:** RTX 6000 Ada on [Runpod](https://www.runpod.io/), improving on the SFT baseline; 57 hours at $0.77/hr (~$50).
|
| 136 |
+
- **Total:** ~$75 using a mix of cloud GPUs and local training.
|
| 137 |
+
|
| 138 |
+
## Acknowledgements
|
| 139 |
+
- We base our distillation pipeline on [distilabel](https://github.com/argilla-io/distilabel).
|
| 140 |
+
- We base the RL experiment on [ART](https://deepwiki.com/OpenPipe/ART).
|
| 141 |
+
- We use Qwen2.5 series base model [Qwen2.5](https://github.com/QwenLM/Qwen2.5).
|
| 142 |
+
|
| 143 |
+
## Citation
|
| 144 |
+
|
| 145 |
+
If you find this project is useful in your own work, please consider citing as follows:
|
| 146 |
+
|
| 147 |
+
```bibtex
|
| 148 |
+
@misc{linalg-zero,
|
| 149 |
+
title = {Linalg-Zero: Distilling Neurosymbolic Reasoning for Linear Algebra in Small Language Models},
|
| 150 |
+
url = {https://github.com/atomwalk12/linalg-zero},
|
| 151 |
+
author = {{Razvan F. Vasile}},
|
| 152 |
+
month = {March},
|
| 153 |
+
year = {2026}
|
| 154 |
+
}
|
| 155 |
+
```
|
app.py
DELETED
|
@@ -1,7 +0,0 @@
|
|
| 1 |
-
import gradio as gr
|
| 2 |
-
|
| 3 |
-
def greet(name):
|
| 4 |
-
return "Hello " + name + "!!"
|
| 5 |
-
|
| 6 |
-
demo = gr.Interface(fn=greet, inputs="text", outputs="text")
|
| 7 |
-
demo.launch()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
codecov.yaml
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
coverage:
|
| 2 |
+
range: 70..100
|
| 3 |
+
round: down
|
| 4 |
+
precision: 1
|
| 5 |
+
status:
|
| 6 |
+
project:
|
| 7 |
+
default:
|
| 8 |
+
target: 90%
|
| 9 |
+
threshold: 0.5%
|
env/.env.distillation.example
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
#!/bin/bash
|
| 2 |
+
|
| 3 |
+
# Distillation
|
| 4 |
+
export HF_HOME=/workspace/linalg-zero/hf-cache
|
| 5 |
+
export HUGGINGFACE_HUB_CACHE=/workspace/linalg-zero/hf-cache
|
| 6 |
+
export VLLM_WORKDIR=/workspace/linalg-zero/vllm-cache
|
| 7 |
+
export UV_CACHE_DIR=/workspace/linalg-zero/uv-cache
|
| 8 |
+
export XDG_CACHE_HOME=/workspace/linalg-zero/.cache
|
| 9 |
+
export PIP_CACHE_DIR=/workspace/linalg-zero/pip-cache
|
| 10 |
+
export USING_VLLM=true
|
| 11 |
+
export INFERENCE_BACKEND=vllm
|
| 12 |
+
export ARGILLA_API_URL=https://atomwalk12-linalgzero-distilled.hf.space
|
| 13 |
+
export ARGILLA_API_KEY=<...>
|
| 14 |
+
export HF_TOKEN=<...>
|
env/.env.grpo.example
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
OPENPIPE_API_KEY=<insert-key>
|
| 2 |
+
WANDB_API_KEY=<insert-key>
|
| 3 |
+
VLLM_API_KEY="my-secret-key"
|
| 4 |
+
ACCELERATE_MIXED_PRECISION=bf16
|
| 5 |
+
VLLM_NO_USAGE_STATS=1
|
| 6 |
+
|
| 7 |
+
HF_HUB_NAMESPACE=<...>
|
| 8 |
+
HF_TOKEN=<...>
|
| 9 |
+
HF_REPO_PRIVATE=1
|
env/.env.grpo.runpod.example
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
export HF_HOME=/workspace/linalg-zero/hf-cache
|
| 2 |
+
export HUGGINGFACE_HUB_CACHE=/workspace/linalg-zero/hf-cache
|
| 3 |
+
export VLLM_WORKDIR=/workspace/linalg-zero/vllm-cache
|
| 4 |
+
export UV_CACHE_DIR=/workspace/linalg-zero/uv-cache
|
| 5 |
+
export XDG_CACHE_HOME=/workspace/linalg-zero/.cache
|
| 6 |
+
export PIP_CACHE_DIR=/workspace/linalg-zero/pip-cache
|
| 7 |
+
|
| 8 |
+
export OPENPIPE_API_KEY=<...>
|
| 9 |
+
export WANDB_API_KEY=<...>
|
| 10 |
+
export VLLM_API_KEY="my-secret-key"
|
| 11 |
+
export ACCELERATE_MIXED_PRECISION=bf16
|
| 12 |
+
export VLLM_NO_USAGE_STATS=1
|
| 13 |
+
|
| 14 |
+
export HF_HUB_NAMESPACE=<...>
|
| 15 |
+
export HF_TOKEN=<...>
|
| 16 |
+
export HF_REPO_PRIVATE=1
|
linalg_zero/__init__.py
ADDED
|
File without changes
|
linalg_zero/config/cleaning_config.yaml
ADDED
|
@@ -0,0 +1,42 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Initial dirty: 49 entries
|
| 2 |
+
|
| 3 |
+
##############
|
| 4 |
+
# Tool calls #
|
| 5 |
+
##############
|
| 6 |
+
120:
|
| 7 |
+
to_remove: [4, 5]
|
| 8 |
+
remove_reason:
|
| 9 |
+
"4,5": "exact msg/tool call with 2,3"
|
| 10 |
+
# Note that when counting these fields, they start from 0.
|
| 11 |
+
initial_msg_count: 9
|
| 12 |
+
expected_final_msg_count: 7
|
| 13 |
+
|
| 14 |
+
|
| 15 |
+
419:
|
| 16 |
+
to_remove: [8, 9]
|
| 17 |
+
remove_reason:
|
| 18 |
+
"8,9": "exact msg/tool call with 6,7"
|
| 19 |
+
initial_msg_count: 11
|
| 20 |
+
expected_final_msg_count: 9
|
| 21 |
+
|
| 22 |
+
|
| 23 |
+
1203:
|
| 24 |
+
to_remove: [4,5]
|
| 25 |
+
remove_reason:
|
| 26 |
+
"4,5": "exact msg/tool call with 2,3"
|
| 27 |
+
initial_msg_count: 7
|
| 28 |
+
expected_final_msg_count: 5
|
| 29 |
+
|
| 30 |
+
################
|
| 31 |
+
# Final answer #
|
| 32 |
+
################
|
| 33 |
+
1052:
|
| 34 |
+
to_remove: [4,5]
|
| 35 |
+
remove_reason:
|
| 36 |
+
"4,5": "exact msg/tool call with 2,3"
|
| 37 |
+
to_replace:
|
| 38 |
+
6:
|
| 39 |
+
"content":
|
| 40 |
+
"think": "<think>This tool call completes the task. Now, I can provide the final answer.</think>"
|
| 41 |
+
initial_msg_count: 7
|
| 42 |
+
expected_final_msg_count: 5
|
linalg_zero/config/data.py
ADDED
|
@@ -0,0 +1,341 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from dataclasses import dataclass, field
|
| 2 |
+
|
| 3 |
+
from trl.scripts.utils import ScriptArguments as ScriptArgs
|
| 4 |
+
from trl.trainer.model_config import ModelConfig
|
| 5 |
+
|
| 6 |
+
|
| 7 |
+
@dataclass
|
| 8 |
+
class ScriptArguments(ScriptArgs):
|
| 9 |
+
"""
|
| 10 |
+
Extended version of ScriptArguments with support for dataset mixtures.
|
| 11 |
+
"""
|
| 12 |
+
|
| 13 |
+
dataset_name: str | None = field(
|
| 14 |
+
default=None, metadata={"help": "Training dataset name. Contains chain-of-thought solutions."}
|
| 15 |
+
)
|
| 16 |
+
eval_dataset_config: str | None = field(default=None, metadata={"help": "Evaluation dataset config."})
|
| 17 |
+
take_n: int | None = field(default=None, metadata={"help": "Number of examples to take from the dataset."})
|
| 18 |
+
|
| 19 |
+
|
| 20 |
+
@dataclass
|
| 21 |
+
class SFTModelConfig(ModelConfig):
|
| 22 |
+
enforce_eager: bool | None = field(default=None, metadata={"help": "Whether to enforce eager execution."})
|
| 23 |
+
|
| 24 |
+
|
| 25 |
+
@dataclass
|
| 26 |
+
class SFTRunConfig:
|
| 27 |
+
add_special_tokens: bool = field(
|
| 28 |
+
metadata={"help": "Whether to add special tokens to the model."},
|
| 29 |
+
)
|
| 30 |
+
|
| 31 |
+
early_stopping_patience: int = field(
|
| 32 |
+
default=3, metadata={"help": "The number of epochs to wait before early stopping."}
|
| 33 |
+
)
|
| 34 |
+
|
| 35 |
+
early_stopping_threshold: float = field(
|
| 36 |
+
default=0.0, metadata={"help": "Minimum improvement required to reset patience counter."}
|
| 37 |
+
)
|
| 38 |
+
|
| 39 |
+
benchmarks: list[str] = field(
|
| 40 |
+
default_factory=lambda: [],
|
| 41 |
+
metadata={"help": "The benchmarks to run after training."},
|
| 42 |
+
)
|
| 43 |
+
callbacks: list[str] = field(
|
| 44 |
+
default_factory=lambda: [],
|
| 45 |
+
metadata={"help": "The callbacks to run during training."},
|
| 46 |
+
)
|
| 47 |
+
chat_template: str | None = field(default=None, metadata={"help": "The chat template to use."})
|
| 48 |
+
system_prompt: str | None = field(
|
| 49 |
+
default=None,
|
| 50 |
+
metadata={"help": "The optional system prompt to use for benchmarking."},
|
| 51 |
+
)
|
| 52 |
+
hub_model_revision: str | None = field(
|
| 53 |
+
default="main",
|
| 54 |
+
metadata={"help": "The Hub model branch to push the model to."},
|
| 55 |
+
)
|
| 56 |
+
overwrite_hub_revision: bool = field(default=False, metadata={"help": "Whether to overwrite the Hub revision."})
|
| 57 |
+
push_to_hub_revision: bool = field(default=False, metadata={"help": "Whether to push to a Hub revision/branch."})
|
| 58 |
+
wandb_entity: str | None = field(
|
| 59 |
+
default=None,
|
| 60 |
+
metadata={"help": ("The entity to store runs under.")},
|
| 61 |
+
)
|
| 62 |
+
wandb_project: str | None = field(
|
| 63 |
+
default=None,
|
| 64 |
+
metadata={"help": ("The project to store runs under.")},
|
| 65 |
+
)
|
| 66 |
+
wandb_run_group: str | None = field(
|
| 67 |
+
default=None,
|
| 68 |
+
metadata={"help": ("The group to store runs under.")},
|
| 69 |
+
)
|
| 70 |
+
wandb_run_id: str | None = field(default=None, metadata={"help": {"The wandb run id."}})
|
| 71 |
+
|
| 72 |
+
eval_max_new_tokens: int | None = field(
|
| 73 |
+
default=None,
|
| 74 |
+
metadata={"help": "Max new tokens for evaluation callbacks (does not affect training)."},
|
| 75 |
+
)
|
| 76 |
+
|
| 77 |
+
max_seq_length: int | None = field(
|
| 78 |
+
default=None,
|
| 79 |
+
metadata={"help": "Max sequence length for evaluation callbacks (does not affect training)."},
|
| 80 |
+
)
|
| 81 |
+
|
| 82 |
+
gpu_memory_utilization: float | None = field(
|
| 83 |
+
default=0.95, metadata={"help": "Fraction of GPU memory to be used by vLLM (0-1)"}
|
| 84 |
+
)
|
| 85 |
+
|
| 86 |
+
# Evaluation sampling
|
| 87 |
+
max_eval_samples: int | None = field(
|
| 88 |
+
default=None,
|
| 89 |
+
metadata={"help": "Maximum number of eval samples for periodic evaluations. None/-1 for full dataset."},
|
| 90 |
+
)
|
| 91 |
+
final_eval_max_samples: int | None = field(
|
| 92 |
+
default=None,
|
| 93 |
+
metadata={"help": "Maximum number of eval samples for periodic evaluations. None/-1 for full dataset."},
|
| 94 |
+
)
|
| 95 |
+
|
| 96 |
+
|
| 97 |
+
@dataclass
|
| 98 |
+
class DatasetGenerationConfig:
|
| 99 |
+
"""
|
| 100 |
+
Data class that stores the dataset generation parameters.
|
| 101 |
+
|
| 102 |
+
Args:
|
| 103 |
+
dataset_name (str): The name of the dataset to generate.
|
| 104 |
+
"""
|
| 105 |
+
|
| 106 |
+
dataset_name: str | None = field(
|
| 107 |
+
metadata={"help": "Should be the name used to store the dataset on the Hugging Face Hub."},
|
| 108 |
+
)
|
| 109 |
+
|
| 110 |
+
|
| 111 |
+
@dataclass
|
| 112 |
+
class LlamaCppServerConfig:
|
| 113 |
+
"""
|
| 114 |
+
Data class that stores LlamaCPP server parameters with llama_cpp_ prefix.
|
| 115 |
+
"""
|
| 116 |
+
|
| 117 |
+
def __post_init__(self) -> None:
|
| 118 |
+
pass
|
| 119 |
+
|
| 120 |
+
# Server parameters
|
| 121 |
+
host: str = field(
|
| 122 |
+
metadata={"help": "Host address to bind to"},
|
| 123 |
+
)
|
| 124 |
+
port: int = field(
|
| 125 |
+
metadata={"help": "Port to listen on"},
|
| 126 |
+
)
|
| 127 |
+
n_ctx: int = field(
|
| 128 |
+
metadata={"help": "Context size"},
|
| 129 |
+
)
|
| 130 |
+
split_mode: int = field(
|
| 131 |
+
metadata={"help": "Split mode (0=none, 1=layer, 2=row)"},
|
| 132 |
+
)
|
| 133 |
+
|
| 134 |
+
# Model parameters
|
| 135 |
+
n_gpu_layers: int = field(
|
| 136 |
+
metadata={"help": "Number of GPU layers to offload"},
|
| 137 |
+
)
|
| 138 |
+
|
| 139 |
+
model: str = field(
|
| 140 |
+
metadata={"help": "Model URL to download (GGUF format)"},
|
| 141 |
+
)
|
| 142 |
+
|
| 143 |
+
hf_pretrained_model_name_or_path: str | None = field(
|
| 144 |
+
default=None,
|
| 145 |
+
metadata={"help": "Huggingface repository ID to ensure that the correct tokenizer is used."},
|
| 146 |
+
)
|
| 147 |
+
|
| 148 |
+
hf_model_repo_id: str | None = field(
|
| 149 |
+
default=None,
|
| 150 |
+
metadata={"help": "Path to the repository where the model is stored."},
|
| 151 |
+
)
|
| 152 |
+
|
| 153 |
+
|
| 154 |
+
@dataclass
|
| 155 |
+
class VllmServerConfig:
|
| 156 |
+
"""
|
| 157 |
+
Data class that stores vLLM server parameters with vllm_ prefix.
|
| 158 |
+
"""
|
| 159 |
+
|
| 160 |
+
# Model parameters
|
| 161 |
+
model: str = field(
|
| 162 |
+
metadata={"help": "Model name (HuggingFace format)"},
|
| 163 |
+
)
|
| 164 |
+
|
| 165 |
+
# Server parameters
|
| 166 |
+
host: str = field(
|
| 167 |
+
metadata={"help": "Host address to bind to"},
|
| 168 |
+
)
|
| 169 |
+
port: int = field(
|
| 170 |
+
metadata={"help": "Port to listen on"},
|
| 171 |
+
)
|
| 172 |
+
enable_auto_tool_choice: bool = field(
|
| 173 |
+
metadata={"help": "Enable automatic tool choice"},
|
| 174 |
+
)
|
| 175 |
+
tool_call_parser: str = field(
|
| 176 |
+
metadata={"help": "Tool call parser to use"},
|
| 177 |
+
)
|
| 178 |
+
chat_template: str | None = field(
|
| 179 |
+
default=None,
|
| 180 |
+
metadata={"help": "Chat template to use"},
|
| 181 |
+
)
|
| 182 |
+
quantization: str | None = field(
|
| 183 |
+
default=None,
|
| 184 |
+
metadata={"help": "Quantization to use"},
|
| 185 |
+
)
|
| 186 |
+
api_key: str = field(
|
| 187 |
+
default="not-used",
|
| 188 |
+
metadata={"help": "API key for authentication (use 'not-used' for local development)"},
|
| 189 |
+
)
|
| 190 |
+
|
| 191 |
+
# Memory / performance tuning parameters
|
| 192 |
+
dtype: str | None = field(
|
| 193 |
+
default=None,
|
| 194 |
+
metadata={"help": "Computation dtype for model weights and activations (e.g., float16)"},
|
| 195 |
+
)
|
| 196 |
+
kv_cache_dtype: str | None = field(
|
| 197 |
+
default=None,
|
| 198 |
+
metadata={"help": "KV cache dtype (auto, fp8, fp8_e4m3, fp8_e5m2)"},
|
| 199 |
+
)
|
| 200 |
+
max_model_len: int | None = field(
|
| 201 |
+
default=None,
|
| 202 |
+
metadata={"help": "Maximum model context length (tokens)"},
|
| 203 |
+
)
|
| 204 |
+
max_num_seqs: int | None = field(
|
| 205 |
+
default=None,
|
| 206 |
+
metadata={"help": "Maximum number of concurrent sequences"},
|
| 207 |
+
)
|
| 208 |
+
gpu_memory_utilization: float | None = field(
|
| 209 |
+
default=None,
|
| 210 |
+
metadata={"help": "Fraction of GPU memory to be used by vLLM (0-1)"},
|
| 211 |
+
)
|
| 212 |
+
enforce_eager: bool | None = field(
|
| 213 |
+
default=None,
|
| 214 |
+
metadata={"help": "Disable CUDA graphs to reduce memory usage"},
|
| 215 |
+
)
|
| 216 |
+
swap_space: int | None = field(
|
| 217 |
+
default=None,
|
| 218 |
+
metadata={"help": "CPU swap space in GB per GPU for paging KV cache"},
|
| 219 |
+
)
|
| 220 |
+
max_num_batched_tokens: int | None = field(
|
| 221 |
+
default=None,
|
| 222 |
+
metadata={"help": "Limit number of tokens processed per batch (prefill)"},
|
| 223 |
+
)
|
| 224 |
+
tensor_parallel_size: int | None = field(
|
| 225 |
+
default=None,
|
| 226 |
+
metadata={"help": "Tensor parallelism degree"},
|
| 227 |
+
)
|
| 228 |
+
enable_chunked_prefill: bool | None = field(
|
| 229 |
+
default=None,
|
| 230 |
+
metadata={"help": "Enable chunked prefill to reduce peak prefill memory"},
|
| 231 |
+
)
|
| 232 |
+
|
| 233 |
+
# Model parameters
|
| 234 |
+
reasoning_parser: str | None = field(
|
| 235 |
+
default=None,
|
| 236 |
+
metadata={"help": "Reasoning parser to use"},
|
| 237 |
+
)
|
| 238 |
+
|
| 239 |
+
|
| 240 |
+
@dataclass
|
| 241 |
+
class DistillationConfig:
|
| 242 |
+
"""
|
| 243 |
+
Data class that stores the distillation pipeline parameters.
|
| 244 |
+
"""
|
| 245 |
+
|
| 246 |
+
# Dataset parameters
|
| 247 |
+
dataset_name: str | None = field(
|
| 248 |
+
metadata={"help": "HuggingFace dataset to load"},
|
| 249 |
+
)
|
| 250 |
+
|
| 251 |
+
# Prompt parameters
|
| 252 |
+
prompt_column: str = field(
|
| 253 |
+
metadata={"help": "Column name for prompt data"},
|
| 254 |
+
)
|
| 255 |
+
prompt_template: str = field(
|
| 256 |
+
metadata={"help": "Template string for formatting prompts"},
|
| 257 |
+
)
|
| 258 |
+
|
| 259 |
+
# Generation parameters (non-defaults first)
|
| 260 |
+
model_type: str | None = field(metadata={"help": "Model type for generation"})
|
| 261 |
+
enable_reasoning: bool = field(metadata={"help": "Whether to enable thinking"})
|
| 262 |
+
max_new_tokens: int = field(
|
| 263 |
+
metadata={"help": "Maximum number of new tokens to generate"},
|
| 264 |
+
)
|
| 265 |
+
num_generations: int = field(
|
| 266 |
+
metadata={"help": "Number of generations per problem"},
|
| 267 |
+
)
|
| 268 |
+
|
| 269 |
+
# Processing parameters
|
| 270 |
+
input_batch_size: int = field(
|
| 271 |
+
metadata={"help": "Batch size for input processing"},
|
| 272 |
+
)
|
| 273 |
+
use_cache: bool = field(
|
| 274 |
+
metadata={"help": "Whether to use cache for the pipeline. This can enable error recovery."},
|
| 275 |
+
)
|
| 276 |
+
|
| 277 |
+
timeout: int = field(
|
| 278 |
+
metadata={"help": "Request timeout in seconds"},
|
| 279 |
+
)
|
| 280 |
+
retries: int = field(
|
| 281 |
+
metadata={"help": "Number of retries for failed requests"},
|
| 282 |
+
)
|
| 283 |
+
|
| 284 |
+
# Output parameters
|
| 285 |
+
hf_output_dataset: str | None = field(
|
| 286 |
+
metadata={"help": "HuggingFace repo to push results to"},
|
| 287 |
+
)
|
| 288 |
+
argilla_output_dataset: str | None = field(
|
| 289 |
+
metadata={"help": "Argilla dataset to push results to. This is used for manual annotation."},
|
| 290 |
+
)
|
| 291 |
+
private: bool = field(
|
| 292 |
+
metadata={"help": "Whether to make the output dataset private when pushing to HF Hub"},
|
| 293 |
+
)
|
| 294 |
+
|
| 295 |
+
# Generation parameters
|
| 296 |
+
n_turns: int = field(
|
| 297 |
+
metadata={"help": "Number of turns to generate"},
|
| 298 |
+
)
|
| 299 |
+
min_successful_completions: int = field(
|
| 300 |
+
default=-1,
|
| 301 |
+
metadata={"help": "Minimum number of successful completions to generate"},
|
| 302 |
+
)
|
| 303 |
+
strip_think_prefix: bool = field(
|
| 304 |
+
default=True,
|
| 305 |
+
metadata={"help": "Whether to strip the think prefix from the conversation. This is needed for Qwen3 models."},
|
| 306 |
+
)
|
| 307 |
+
# Optional stopping sequences (must come after non-default fields)
|
| 308 |
+
stop: list[str] | None = field(
|
| 309 |
+
default=None,
|
| 310 |
+
metadata={"help": "Stop sequences for generation (each string is a stop token)"},
|
| 311 |
+
)
|
| 312 |
+
|
| 313 |
+
debug_mode: bool = field(
|
| 314 |
+
default=False,
|
| 315 |
+
metadata={"help": "Whether to do evaluation"},
|
| 316 |
+
)
|
| 317 |
+
|
| 318 |
+
take_n: int | None = field(
|
| 319 |
+
default=None,
|
| 320 |
+
metadata={"help": "Number of examples to take from the dataset."},
|
| 321 |
+
)
|
| 322 |
+
|
| 323 |
+
structured_output: bool = field(
|
| 324 |
+
default=False,
|
| 325 |
+
metadata={"help": "Whether to use structured output"},
|
| 326 |
+
)
|
| 327 |
+
|
| 328 |
+
deterministic: bool = field(
|
| 329 |
+
default=True,
|
| 330 |
+
metadata={"help": "Make generation deterministic (temperature=0, top_p=1)"},
|
| 331 |
+
)
|
| 332 |
+
|
| 333 |
+
client_replicas: int | None = field(
|
| 334 |
+
default=None,
|
| 335 |
+
metadata={"help": "Number of client replicas for parallel processing"},
|
| 336 |
+
)
|
| 337 |
+
|
| 338 |
+
dataset_config: str | None = field(
|
| 339 |
+
default=None,
|
| 340 |
+
metadata={"help": "Dataset config to use"},
|
| 341 |
+
)
|
linalg_zero/config/dataset/default.yml
ADDED
|
File without changes
|
linalg_zero/config/dataset/default_debug.yml
ADDED
|
@@ -0,0 +1 @@
|
|
|
|
|
|
|
| 1 |
+
dataset_name: "linalg_zero"
|
linalg_zero/config/distillation/env.example.sh
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
#!/bin/bash
|
| 2 |
+
|
| 3 |
+
export HF_HOME=/workspace/linalg-zero/hf-cache
|
| 4 |
+
export HUGGINGFACE_HUB_CACHE=/workspace/linalg-zero/hf-cache
|
| 5 |
+
export VLLM_WORKDIR=/workspace/linalg-zero/vllm-cache
|
| 6 |
+
export UV_CACHE_DIR=/workspace/linalg-zero/uv-cache
|
| 7 |
+
export XDG_CACHE_HOME=/workspace/linalg-zero/.cache
|
| 8 |
+
export PIP_CACHE_DIR=/workspace/linalg-zero/pip-cache
|
| 9 |
+
export USING_VLLM=true
|
| 10 |
+
export INFERENCE_BACKEND=vllm
|
| 11 |
+
export ARGILLA_API_URL=https://atomwalk12-linalgzero-distilled.hf.space
|
| 12 |
+
export ARGILLA_API_KEY=<token>
|
| 13 |
+
export HF_TOKEN=<token>
|
linalg_zero/config/distillation/llamacpp_qwen3_30b_A3B_instruct.yaml
ADDED
|
@@ -0,0 +1,49 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
######################
|
| 2 |
+
# DistillationConfig #
|
| 3 |
+
######################
|
| 4 |
+
|
| 5 |
+
# Dataset parameters
|
| 6 |
+
dataset_name: "atomwalk12/linalgzero"
|
| 7 |
+
debug_mode: True
|
| 8 |
+
take_n: 20
|
| 9 |
+
n_turns: 6
|
| 10 |
+
|
| 11 |
+
# Prompt parameters
|
| 12 |
+
prompt_column: "prompt"
|
| 13 |
+
prompt_template: "{{ instruction }}"
|
| 14 |
+
|
| 15 |
+
# Generation parameters
|
| 16 |
+
model_type: "default"
|
| 17 |
+
deterministic: true
|
| 18 |
+
max_new_tokens: 8192
|
| 19 |
+
num_generations: 1
|
| 20 |
+
|
| 21 |
+
# Processing parameters
|
| 22 |
+
input_batch_size: 5
|
| 23 |
+
use_cache: true
|
| 24 |
+
client_replicas: 1
|
| 25 |
+
timeout: 600
|
| 26 |
+
retries: 2
|
| 27 |
+
|
| 28 |
+
# Output parameters
|
| 29 |
+
hf_output_dataset: "atomwalk12/linalgzero-distilled-debug"
|
| 30 |
+
argilla_output_dataset: "atomwalk12/linalgzero-distilled-debug"
|
| 31 |
+
private: false
|
| 32 |
+
|
| 33 |
+
########################
|
| 34 |
+
# LlamaCppServerConfig #
|
| 35 |
+
########################
|
| 36 |
+
|
| 37 |
+
# Server parameters
|
| 38 |
+
host: "localhost"
|
| 39 |
+
port: 8000
|
| 40 |
+
|
| 41 |
+
# Model
|
| 42 |
+
model: "Qwen3-30B-A3B-Instruct-2507-UD-Q5_K_XL.gguf"
|
| 43 |
+
hf_model_repo_id: "unsloth/Qwen3-30B-A3B-Instruct-2507-GGUF"
|
| 44 |
+
hf_pretrained_model_name_or_path: "Qwen/Qwen3-32B"
|
| 45 |
+
enable_reasoning: true
|
| 46 |
+
|
| 47 |
+
n_gpu_layers: 45
|
| 48 |
+
n_ctx: 8192
|
| 49 |
+
split_mode: 2
|
linalg_zero/config/distillation/llamacpp_qwen3_30b_A3B_think.yaml
ADDED
|
@@ -0,0 +1,49 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
######################
|
| 2 |
+
# DistillationConfig #
|
| 3 |
+
######################
|
| 4 |
+
|
| 5 |
+
# Dataset parameters
|
| 6 |
+
dataset_name: "atomwalk12/linalgzero"
|
| 7 |
+
debug_mode: True
|
| 8 |
+
take_n: 20
|
| 9 |
+
n_turns: 6
|
| 10 |
+
|
| 11 |
+
# Prompt parameters
|
| 12 |
+
prompt_column: "prompt"
|
| 13 |
+
prompt_template: "{{ instruction }}"
|
| 14 |
+
|
| 15 |
+
# Generation parameters
|
| 16 |
+
model_type: "default"
|
| 17 |
+
deterministic: true
|
| 18 |
+
max_new_tokens: 8192
|
| 19 |
+
num_generations: 1
|
| 20 |
+
|
| 21 |
+
# Processing parameters
|
| 22 |
+
input_batch_size: 5
|
| 23 |
+
use_cache: true
|
| 24 |
+
client_replicas: 1
|
| 25 |
+
timeout: 600
|
| 26 |
+
retries: 2
|
| 27 |
+
|
| 28 |
+
# Output parameters
|
| 29 |
+
hf_output_dataset: "atomwalk12/linalgzero-distilled-debug"
|
| 30 |
+
argilla_output_dataset: "atomwalk12/linalgzero-distilled-debug"
|
| 31 |
+
private: false
|
| 32 |
+
|
| 33 |
+
########################
|
| 34 |
+
# LlamaCppServerConfig #
|
| 35 |
+
########################
|
| 36 |
+
|
| 37 |
+
# Server parameters
|
| 38 |
+
host: "localhost"
|
| 39 |
+
port: 8000
|
| 40 |
+
|
| 41 |
+
# Model
|
| 42 |
+
model: "Qwen3-30B-A3B-Thinking-2507-UD-Q5_K_XL.gguf"
|
| 43 |
+
hf_model_repo_id: "unsloth/Qwen3-30B-A3B-Thinking-2507-GGUF"
|
| 44 |
+
hf_pretrained_model_name_or_path: "Qwen/Qwen3-32B"
|
| 45 |
+
enable_reasoning: true
|
| 46 |
+
|
| 47 |
+
n_gpu_layers: 45
|
| 48 |
+
n_ctx: 8192
|
| 49 |
+
split_mode: 2
|
linalg_zero/config/distillation/llamacpp_qwen3_32b_instruct.yaml
ADDED
|
@@ -0,0 +1,49 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
######################
|
| 2 |
+
# DistillationConfig #
|
| 3 |
+
######################
|
| 4 |
+
|
| 5 |
+
# Dataset parameters
|
| 6 |
+
dataset_name: "atomwalk12/linalgzero"
|
| 7 |
+
debug_mode: False
|
| 8 |
+
take_n: 20
|
| 9 |
+
n_turns: 6
|
| 10 |
+
|
| 11 |
+
# Prompt parameters
|
| 12 |
+
prompt_column: "prompt"
|
| 13 |
+
prompt_template: "{{ instruction }}"
|
| 14 |
+
|
| 15 |
+
# Generation parameters
|
| 16 |
+
model_type: "default"
|
| 17 |
+
deterministic: true
|
| 18 |
+
max_new_tokens: 8192
|
| 19 |
+
num_generations: 1
|
| 20 |
+
|
| 21 |
+
# Processing parameters
|
| 22 |
+
input_batch_size: 7
|
| 23 |
+
use_cache: true
|
| 24 |
+
client_replicas: 1
|
| 25 |
+
timeout: 600
|
| 26 |
+
retries: 2
|
| 27 |
+
|
| 28 |
+
# Output parameters
|
| 29 |
+
hf_output_dataset: "atomwalk12/linalgzero-distilled-debug"
|
| 30 |
+
argilla_output_dataset: "atomwalk12/linalgzero-distilled-debug"
|
| 31 |
+
private: false
|
| 32 |
+
|
| 33 |
+
########################
|
| 34 |
+
# LlamaCppServerConfig #
|
| 35 |
+
########################
|
| 36 |
+
|
| 37 |
+
# Server parameters
|
| 38 |
+
host: "localhost"
|
| 39 |
+
port: 8000
|
| 40 |
+
|
| 41 |
+
# Model
|
| 42 |
+
model: "Qwen3-32B-Q4_K_M.gguf"
|
| 43 |
+
hf_model_repo_id: "unsloth/Qwen3-32B-GGUF"
|
| 44 |
+
hf_pretrained_model_name_or_path: "Qwen/Qwen3-32B"
|
| 45 |
+
enable_reasoning: true
|
| 46 |
+
|
| 47 |
+
n_gpu_layers: 60
|
| 48 |
+
n_ctx: 8192
|
| 49 |
+
split_mode: 2
|
linalg_zero/config/distillation/qwen3_think_vllm_debug.yaml
ADDED
|
@@ -0,0 +1,65 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Distillation Configuration for vLLM inference
|
| 2 |
+
|
| 3 |
+
# Dataset parameters
|
| 4 |
+
dataset_name: "atomwalk12/linalgzero"
|
| 5 |
+
# dataset_name: "atomwalk12/linalg-distilled-debug"
|
| 6 |
+
debug_mode: True
|
| 7 |
+
dataset_config: null
|
| 8 |
+
take_n: 20
|
| 9 |
+
|
| 10 |
+
# Prompt parameters
|
| 11 |
+
prompt_column: "prompt"
|
| 12 |
+
prompt_template: "{{ instruction }}"
|
| 13 |
+
|
| 14 |
+
# Generation parameters
|
| 15 |
+
model_type: "default"
|
| 16 |
+
deterministic: true
|
| 17 |
+
max_new_tokens: 8192 # 32768
|
| 18 |
+
num_generations: 1
|
| 19 |
+
|
| 20 |
+
# Processing parameters
|
| 21 |
+
input_batch_size: 3
|
| 22 |
+
use_cache: true
|
| 23 |
+
client_replicas: 1
|
| 24 |
+
timeout: 600
|
| 25 |
+
retries: 2
|
| 26 |
+
|
| 27 |
+
# Output parameters
|
| 28 |
+
hf_output_dataset: "atomwalk12/linalgzero-distilled-debug"
|
| 29 |
+
argilla_output_dataset: "atomwalk12/linalgzero-distilled-debug"
|
| 30 |
+
private: false
|
| 31 |
+
|
| 32 |
+
# Server parameters
|
| 33 |
+
# To inspect the various model/chat template combinations:
|
| 34 |
+
# https://github.com/vllm-project/vllm/blob/main/docs/features/tool_calling.md
|
| 35 |
+
|
| 36 |
+
model: "Eslzzyl/Qwen3-4B-Thinking-2507-AWQ"
|
| 37 |
+
tool_call_parser: "hermes"
|
| 38 |
+
reasoning_parser: "qwen3" # "deepseek_r1"
|
| 39 |
+
|
| 40 |
+
#model: "lurker18/Llama_3.1_8B_Instruct_AWQ_4bit"
|
| 41 |
+
#tool_call_parser: "llama3_json"
|
| 42 |
+
#chat_template: "linalg_zero/distillation/vllm/tool_chat_template_llama3.1_json.jinja"
|
| 43 |
+
|
| 44 |
+
#model: "solidrust/Mistral-7B-Instruct-v0.3-AWQ"
|
| 45 |
+
#tool_call_parser: "mistral"
|
| 46 |
+
#chat_template: "linalg_zero/distillation/vllm/tool_chat_template_mistral_parallel.jinja"
|
| 47 |
+
|
| 48 |
+
enable_auto_tool_choice: true
|
| 49 |
+
quantization: "awq"
|
| 50 |
+
host: "localhost"
|
| 51 |
+
port: 8000
|
| 52 |
+
|
| 53 |
+
# Memory / performance tuning for 24GB GPU
|
| 54 |
+
dtype: "float16"
|
| 55 |
+
kv_cache_dtype: "fp8"
|
| 56 |
+
max_num_seqs: 1
|
| 57 |
+
gpu_memory_utilization: 0.85
|
| 58 |
+
enforce_eager: true
|
| 59 |
+
swap_space: 4
|
| 60 |
+
max_num_batched_tokens: 2048
|
| 61 |
+
max_model_len: 32768 # 131072
|
| 62 |
+
enable_chunked_prefill: true
|
| 63 |
+
|
| 64 |
+
# Distillation parameters
|
| 65 |
+
n_turns: 6
|
linalg_zero/config/distillation/vllm_qwen3_32b.yaml
ADDED
|
@@ -0,0 +1,46 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Dataset parameters
|
| 2 |
+
dataset_name: "atomwalk12/linalgzero"
|
| 3 |
+
debug_mode: True
|
| 4 |
+
dataset_config: null
|
| 5 |
+
n_turns: 8
|
| 6 |
+
min_successful_completions: -1
|
| 7 |
+
|
| 8 |
+
# Prompt parameters
|
| 9 |
+
prompt_column: "prompt"
|
| 10 |
+
prompt_template: "{{ instruction }}"
|
| 11 |
+
|
| 12 |
+
# Generation parameters
|
| 13 |
+
model_type: "default"
|
| 14 |
+
deterministic: false
|
| 15 |
+
max_new_tokens: 4096
|
| 16 |
+
num_generations: 1
|
| 17 |
+
enable_reasoning: true
|
| 18 |
+
|
| 19 |
+
# Processing parameters
|
| 20 |
+
input_batch_size: 24
|
| 21 |
+
use_cache: true
|
| 22 |
+
timeout: 600
|
| 23 |
+
retries: 5
|
| 24 |
+
|
| 25 |
+
# Output parameters
|
| 26 |
+
hf_output_dataset: "atomwalk12/linalgzero-distilled"
|
| 27 |
+
argilla_output_dataset: "atomwalk12/linalgzero-distilled"
|
| 28 |
+
private: false
|
| 29 |
+
|
| 30 |
+
# Server parameters
|
| 31 |
+
model: "Qwen/Qwen3-32B-FP8"
|
| 32 |
+
tool_call_parser: "hermes"
|
| 33 |
+
reasoning_parser: "qwen3"
|
| 34 |
+
|
| 35 |
+
enable_auto_tool_choice: true
|
| 36 |
+
host: "localhost"
|
| 37 |
+
port: 8000
|
| 38 |
+
|
| 39 |
+
dtype: "bfloat16"
|
| 40 |
+
kv_cache_dtype: "auto"
|
| 41 |
+
max_num_seqs: 24
|
| 42 |
+
max_model_len: 24576
|
| 43 |
+
gpu_memory_utilization: 0.93
|
| 44 |
+
enable_chunked_prefill: true
|
| 45 |
+
swap_space: 8
|
| 46 |
+
max_num_batched_tokens: 1024
|
linalg_zero/config/distillation/vllm_qwen3_32b_debug.yaml
ADDED
|
@@ -0,0 +1,46 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Dataset parameters
|
| 2 |
+
dataset_name: "atomwalk12/linalgzero"
|
| 3 |
+
debug_mode: False
|
| 4 |
+
dataset_config: null
|
| 5 |
+
n_turns: 8
|
| 6 |
+
take_n: 500
|
| 7 |
+
|
| 8 |
+
# Prompt parameters
|
| 9 |
+
prompt_column: "prompt"
|
| 10 |
+
prompt_template: "{{ instruction }}"
|
| 11 |
+
|
| 12 |
+
# Generation parameters
|
| 13 |
+
model_type: "default"
|
| 14 |
+
deterministic: false
|
| 15 |
+
max_new_tokens: 4096
|
| 16 |
+
num_generations: 1
|
| 17 |
+
enable_reasoning: true
|
| 18 |
+
|
| 19 |
+
# Processing parameters
|
| 20 |
+
input_batch_size: 24
|
| 21 |
+
use_cache: true
|
| 22 |
+
timeout: 600
|
| 23 |
+
retries: 5
|
| 24 |
+
|
| 25 |
+
# Output parameters
|
| 26 |
+
hf_output_dataset: "atomwalk12/linalgzero-distilled-500"
|
| 27 |
+
argilla_output_dataset: "atomwalk12/linalgzero-distilled-500"
|
| 28 |
+
private: false
|
| 29 |
+
|
| 30 |
+
# Server parameters
|
| 31 |
+
model: "Qwen/Qwen3-32B-FP8"
|
| 32 |
+
tool_call_parser: "hermes"
|
| 33 |
+
reasoning_parser: "qwen3"
|
| 34 |
+
|
| 35 |
+
enable_auto_tool_choice: true
|
| 36 |
+
host: "localhost"
|
| 37 |
+
port: 8000
|
| 38 |
+
|
| 39 |
+
dtype: "bfloat16"
|
| 40 |
+
kv_cache_dtype: "auto"
|
| 41 |
+
max_num_seqs: 24
|
| 42 |
+
max_model_len: 24576
|
| 43 |
+
gpu_memory_utilization: 0.93
|
| 44 |
+
enable_chunked_prefill: true
|
| 45 |
+
swap_space: 8
|
| 46 |
+
max_num_batched_tokens: 1024
|
linalg_zero/config/distillation/vllm_qwen3_4b_think.yaml
ADDED
|
@@ -0,0 +1,52 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Dataset parameters
|
| 2 |
+
dataset_name: "atomwalk12/linalgzero"
|
| 3 |
+
debug_mode: true
|
| 4 |
+
dataset_config: null
|
| 5 |
+
n_turns: 6
|
| 6 |
+
take_n: 7
|
| 7 |
+
min_successful_completions: 8200
|
| 8 |
+
|
| 9 |
+
# Prompt parameters
|
| 10 |
+
prompt_column: "prompt"
|
| 11 |
+
prompt_template: "{{ instruction }}"
|
| 12 |
+
|
| 13 |
+
# Generation parameters
|
| 14 |
+
model_type: "default"
|
| 15 |
+
deterministic: false
|
| 16 |
+
max_new_tokens: 2048
|
| 17 |
+
num_generations: 1
|
| 18 |
+
enable_reasoning: true
|
| 19 |
+
strip_think_prefix: false
|
| 20 |
+
|
| 21 |
+
# Processing parameters
|
| 22 |
+
input_batch_size: 3
|
| 23 |
+
use_cache: true
|
| 24 |
+
client_replicas: 1
|
| 25 |
+
timeout: 600
|
| 26 |
+
retries: 2
|
| 27 |
+
|
| 28 |
+
# Output parameters
|
| 29 |
+
hf_output_dataset: "atomwalk12/linalgzero-distilled-debug"
|
| 30 |
+
argilla_output_dataset: "atomwalk12/linalgzero-distilled-debug"
|
| 31 |
+
private: false
|
| 32 |
+
|
| 33 |
+
# Server parameters
|
| 34 |
+
model: "Eslzzyl/Qwen3-4B-Thinking-2507-AWQ"
|
| 35 |
+
tool_call_parser: "hermes"
|
| 36 |
+
reasoning_parser: "qwen3"
|
| 37 |
+
|
| 38 |
+
enable_auto_tool_choice: true
|
| 39 |
+
quantization: "awq"
|
| 40 |
+
host: "localhost"
|
| 41 |
+
port: 8000
|
| 42 |
+
|
| 43 |
+
# Memory / performance tuning for 24GB GPU
|
| 44 |
+
dtype: "float16"
|
| 45 |
+
kv_cache_dtype: "auto"
|
| 46 |
+
max_num_seqs: 1
|
| 47 |
+
gpu_memory_utilization: 0.75
|
| 48 |
+
enforce_eager: true
|
| 49 |
+
swap_space: 4
|
| 50 |
+
max_num_batched_tokens: 2048
|
| 51 |
+
max_model_len: 8096
|
| 52 |
+
enable_chunked_prefill: true
|
linalg_zero/config/grpo/Qwen/Qwen2.5-3B/eval/linalgzero-grpo-110.yaml
ADDED
|
@@ -0,0 +1,62 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
init:
|
| 2 |
+
max_lora_rank: 32
|
| 3 |
+
dtype: bfloat16
|
| 4 |
+
load_in_4bit: false
|
| 5 |
+
load_in_8bit: false
|
| 6 |
+
max_seq_length: 6144
|
| 7 |
+
|
| 8 |
+
training:
|
| 9 |
+
trajectories_per_group: 4
|
| 10 |
+
groups_per_step: 8
|
| 11 |
+
learning_rate: 2e-6
|
| 12 |
+
beta: 0.03
|
| 13 |
+
eval_steps: 100
|
| 14 |
+
num_epochs: 2
|
| 15 |
+
train_mode: sync_rl
|
| 16 |
+
chat_template: "{%- if tools %}\n {{- '<|im_start|>system\\n' }}\n {%- if messages[0]['role'] == 'system' %}\n {{- messages[0]['content'] }}\n {%- else %}\n {{- 'You are a helpful assistant.' }}\n {%- endif %}\n {{- \"\\n\\n# Tools\\n\\nYou may call one or more functions to assist with the user query.\\n\\nYou are provided with function signatures within <tools></tools> XML tags:\\n<tools>\" }}\n {%- for tool in tools %}\n {{- \"\\n\" }}\n {{- tool | tojson }}\n {%- endfor %}\n {{- \"\\n</tools>\\n\\nFor each function call, return a json object with function name and arguments within <tool_call></tool_call> XML tags:\\n<tool_call>\\n{\\\"name\\\": <function-name>, \\\"arguments\\\": <args-json-object>}\\n</tool_call><|im_end|>\\n\" }}\n{%- else %}\n {%- if messages[0]['role'] == 'system' %}\n {{- '<|im_start|>system\\n' + messages[0]['content'] + '<|im_end|>\\n' }}\n {%- else %}\n {{- '<|im_start|>system\\nYou are a helpful assistant.<|im_end|>\\n' }}\n {%- endif %}\n{%- endif %}\n{%- for message in messages %}\n {%- if (message.role == \"user\") or (message.role == \"system\" and not loop.first) or (message.role == \"assistant\" and not message.tool_calls) %}\n {{- '<|im_start|>' + message.role + '\\n' + message.content + '<|im_end|>' + '\\n' }}\n {%- elif message.role == \"assistant\" %}\n {{- '<|im_start|>' + message.role }}\n {%- if message.content %}\n {{- '\\n' + message.content }}\n {%- endif %}\n {%- for tool_call in message.tool_calls %}\n {%- if tool_call.function is defined %}\n {%- set tool_call = tool_call.function %}\n {%- endif %}\n {{- '\\n<tool_call>\\n{\"name\": \"' }}\n {{- tool_call.name }}\n {{- '\", \"arguments\": ' }}\n {{- tool_call.arguments | tojson }}\n {{- '}\\n</tool_call>' }}\n {%- endfor %}\n {{- '<|im_end|>\\n' }}\n {%- elif message.role == \"tool\" %}\n {%- if (loop.index0 == 0) or (messages[loop.index0 - 1].role != \"tool\") %}\n {{- '<|im_start|>user' }}\n {%- endif %}\n {{- '\\n<tool_response>\\n' }}\n {{- message.content }}\n {{- '\\n</tool_response>' }}\n {%- if loop.last or (messages[loop.index0 + 1].role != \"tool\") %}\n {{- '<|im_end|>\\n' }}\n {%- endif %}\n {%- endif %}\n{%- endfor %}\n{%- if add_generation_prompt %}\n {{- '<|im_start|>assistant\\n' }}\n{%- endif %}\n"
|
| 17 |
+
|
| 18 |
+
run:
|
| 19 |
+
project_id: 001-linalgzero-sft-110
|
| 20 |
+
project: linalgzero-eval
|
| 21 |
+
base_model: "results/LinalgZero-SFT-110-checkpoint-300/checkpoint-300"
|
| 22 |
+
model: "results/LinalgZero-SFT-110-checkpoint-300/checkpoint-300"
|
| 23 |
+
dataset_path: "atomwalk12/linalgzero-grpo"
|
| 24 |
+
env: linear_algebra
|
| 25 |
+
model_provider: hosted_vllm
|
| 26 |
+
user_model_provider: openai
|
| 27 |
+
user_strategy: mathematician
|
| 28 |
+
user_model: gpt-4o
|
| 29 |
+
agent_strategy: tool-calling-rl
|
| 30 |
+
temperature: 0.7
|
| 31 |
+
top_p: 0.8
|
| 32 |
+
repetition_penalty: 1.0
|
| 33 |
+
task_split: test
|
| 34 |
+
log_dir: rl_results
|
| 35 |
+
skip_eval: False
|
| 36 |
+
in_process: False
|
| 37 |
+
max_assistant_turns: 5
|
| 38 |
+
max_completion_tokens: 800
|
| 39 |
+
skip_special_tokens: False
|
| 40 |
+
stop: [
|
| 41 |
+
"<tool_response>",
|
| 42 |
+
"</tool_response>",
|
| 43 |
+
"User:",
|
| 44 |
+
]
|
| 45 |
+
seed: 42
|
| 46 |
+
|
| 47 |
+
trainer:
|
| 48 |
+
max_prompt_length: 5000
|
| 49 |
+
max_completion_length: 800
|
| 50 |
+
report_to: ["wandb"]
|
| 51 |
+
seed: 42
|
| 52 |
+
data_seed: 42
|
| 53 |
+
|
| 54 |
+
engine:
|
| 55 |
+
gpu_memory_utilization: 0.6
|
| 56 |
+
max_lora_rank: 32
|
| 57 |
+
enable_sleep_mode: true
|
| 58 |
+
max_model_len: 6144
|
| 59 |
+
dtype: 'bfloat16'
|
| 60 |
+
quantization: null
|
| 61 |
+
load_format: 'auto'
|
| 62 |
+
seed: 42
|
linalg_zero/config/grpo/Qwen/Qwen2.5-3B/eval/linalgzero-grpo.yaml
ADDED
|
@@ -0,0 +1,62 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
init:
|
| 2 |
+
max_lora_rank: 32
|
| 3 |
+
dtype: bfloat16
|
| 4 |
+
load_in_4bit: false
|
| 5 |
+
load_in_8bit: false
|
| 6 |
+
max_seq_length: 10240
|
| 7 |
+
|
| 8 |
+
training:
|
| 9 |
+
trajectories_per_group: 4
|
| 10 |
+
groups_per_step: 8
|
| 11 |
+
learning_rate: 2e-6
|
| 12 |
+
beta: 0.03
|
| 13 |
+
eval_steps: 100
|
| 14 |
+
num_epochs: 2
|
| 15 |
+
train_mode: sync_rl
|
| 16 |
+
chat_template: "{%- if tools %}\n {{- '<|im_start|>system\\n' }}\n {%- if messages[0]['role'] == 'system' %}\n {{- messages[0]['content'] }}\n {%- else %}\n {{- 'You are a helpful assistant.' }}\n {%- endif %}\n {{- \"\\n\\n# Tools\\n\\nYou may call one or more functions to assist with the user query.\\n\\nYou are provided with function signatures within <tools></tools> XML tags:\\n<tools>\" }}\n {%- for tool in tools %}\n {{- \"\\n\" }}\n {{- tool | tojson }}\n {%- endfor %}\n {{- \"\\n</tools>\\n\\nFor each function call, return a json object with function name and arguments within <tool_call></tool_call> XML tags:\\n<tool_call>\\n{\\\"name\\\": <function-name>, \\\"arguments\\\": <args-json-object>}\\n</tool_call><|im_end|>\\n\" }}\n{%- else %}\n {%- if messages[0]['role'] == 'system' %}\n {{- '<|im_start|>system\\n' + messages[0]['content'] + '<|im_end|>\\n' }}\n {%- else %}\n {{- '<|im_start|>system\\nYou are a helpful assistant.<|im_end|>\\n' }}\n {%- endif %}\n{%- endif %}\n{%- for message in messages %}\n {%- if (message.role == \"user\") or (message.role == \"system\" and not loop.first) or (message.role == \"assistant\" and not message.tool_calls) %}\n {{- '<|im_start|>' + message.role + '\\n' + message.content + '<|im_end|>' + '\\n' }}\n {%- elif message.role == \"assistant\" %}\n {{- '<|im_start|>' + message.role }}\n {%- if message.content %}\n {{- '\\n' + message.content }}\n {%- endif %}\n {%- for tool_call in message.tool_calls %}\n {%- if tool_call.function is defined %}\n {%- set tool_call = tool_call.function %}\n {%- endif %}\n {{- '\\n<tool_call>\\n{\"name\": \"' }}\n {{- tool_call.name }}\n {{- '\", \"arguments\": ' }}\n {{- tool_call.arguments | tojson }}\n {{- '}\\n</tool_call>' }}\n {%- endfor %}\n {{- '<|im_end|>\\n' }}\n {%- elif message.role == \"tool\" %}\n {%- if (loop.index0 == 0) or (messages[loop.index0 - 1].role != \"tool\") %}\n {{- '<|im_start|>user' }}\n {%- endif %}\n {{- '\\n<tool_response>\\n' }}\n {{- message.content }}\n {{- '\\n</tool_response>' }}\n {%- if loop.last or (messages[loop.index0 + 1].role != \"tool\") %}\n {{- '<|im_end|>\\n' }}\n {%- endif %}\n {%- endif %}\n{%- endfor %}\n{%- if add_generation_prompt %}\n {{- '<|im_start|>assistant\\n' }}\n{%- endif %}\n"
|
| 17 |
+
|
| 18 |
+
run:
|
| 19 |
+
project_id: LinAlgZero-GRPO-001
|
| 20 |
+
project: linalgzero-eval
|
| 21 |
+
base_model: "atomwalk12/LinAlgZero-GRPO"
|
| 22 |
+
model: "atomwalk12/LinAlgZero-GRPO"
|
| 23 |
+
dataset_path: "atomwalk12/linalgzero-grpo"
|
| 24 |
+
env: linear_algebra
|
| 25 |
+
model_provider: hosted_vllm
|
| 26 |
+
user_model_provider: openai
|
| 27 |
+
user_strategy: mathematician
|
| 28 |
+
user_model: gpt-4o
|
| 29 |
+
agent_strategy: tool-calling-rl
|
| 30 |
+
temperature: 0.0
|
| 31 |
+
top_p: null
|
| 32 |
+
repetition_penalty: 1.0
|
| 33 |
+
task_split: test
|
| 34 |
+
log_dir: rl_results
|
| 35 |
+
skip_eval: False
|
| 36 |
+
in_process: False
|
| 37 |
+
max_assistant_turns: 5
|
| 38 |
+
max_completion_tokens: 1536
|
| 39 |
+
skip_special_tokens: False
|
| 40 |
+
stop: [
|
| 41 |
+
"<tool_response>",
|
| 42 |
+
"</tool_response>",
|
| 43 |
+
"User:",
|
| 44 |
+
]
|
| 45 |
+
seed: 42
|
| 46 |
+
|
| 47 |
+
trainer:
|
| 48 |
+
max_prompt_length: 8192
|
| 49 |
+
max_completion_length: 1536
|
| 50 |
+
report_to: ["wandb"]
|
| 51 |
+
seed: 42
|
| 52 |
+
data_seed: 42
|
| 53 |
+
|
| 54 |
+
engine:
|
| 55 |
+
gpu_memory_utilization: 0.6
|
| 56 |
+
max_lora_rank: 32
|
| 57 |
+
enable_sleep_mode: true
|
| 58 |
+
max_model_len: 10240
|
| 59 |
+
dtype: 'bfloat16'
|
| 60 |
+
quantization: null
|
| 61 |
+
load_format: 'auto'
|
| 62 |
+
seed: 42
|
linalg_zero/config/grpo/Qwen/Qwen2.5-3B/eval/linalgzero-sft-110.yaml
ADDED
|
@@ -0,0 +1,62 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
init:
|
| 2 |
+
max_lora_rank: 32
|
| 3 |
+
dtype: bfloat16
|
| 4 |
+
load_in_4bit: false
|
| 5 |
+
load_in_8bit: false
|
| 6 |
+
max_seq_length: 6144
|
| 7 |
+
|
| 8 |
+
training:
|
| 9 |
+
trajectories_per_group: 4
|
| 10 |
+
groups_per_step: 8
|
| 11 |
+
learning_rate: 2e-6
|
| 12 |
+
beta: 0.03
|
| 13 |
+
eval_steps: 100
|
| 14 |
+
num_epochs: 2
|
| 15 |
+
train_mode: sync_rl
|
| 16 |
+
chat_template: "{%- if tools %}\n {{- '<|im_start|>system\\n' }}\n {%- if messages[0]['role'] == 'system' %}\n {{- messages[0]['content'] }}\n {%- else %}\n {{- 'You are a helpful assistant.' }}\n {%- endif %}\n {{- \"\\n\\n# Tools\\n\\nYou may call one or more functions to assist with the user query.\\n\\nYou are provided with function signatures within <tools></tools> XML tags:\\n<tools>\" }}\n {%- for tool in tools %}\n {{- \"\\n\" }}\n {{- tool | tojson }}\n {%- endfor %}\n {{- \"\\n</tools>\\n\\nFor each function call, return a json object with function name and arguments within <tool_call></tool_call> XML tags:\\n<tool_call>\\n{\\\"name\\\": <function-name>, \\\"arguments\\\": <args-json-object>}\\n</tool_call><|im_end|>\\n\" }}\n{%- else %}\n {%- if messages[0]['role'] == 'system' %}\n {{- '<|im_start|>system\\n' + messages[0]['content'] + '<|im_end|>\\n' }}\n {%- else %}\n {{- '<|im_start|>system\\nYou are a helpful assistant.<|im_end|>\\n' }}\n {%- endif %}\n{%- endif %}\n{%- for message in messages %}\n {%- if (message.role == \"user\") or (message.role == \"system\" and not loop.first) or (message.role == \"assistant\" and not message.tool_calls) %}\n {{- '<|im_start|>' + message.role + '\\n' + message.content + '<|im_end|>' + '\\n' }}\n {%- elif message.role == \"assistant\" %}\n {{- '<|im_start|>' + message.role }}\n {%- if message.content %}\n {{- '\\n' + message.content }}\n {%- endif %}\n {%- for tool_call in message.tool_calls %}\n {%- if tool_call.function is defined %}\n {%- set tool_call = tool_call.function %}\n {%- endif %}\n {{- '\\n<tool_call>\\n{\"name\": \"' }}\n {{- tool_call.name }}\n {{- '\", \"arguments\": ' }}\n {{- tool_call.arguments | tojson }}\n {{- '}\\n</tool_call>' }}\n {%- endfor %}\n {{- '<|im_end|>\\n' }}\n {%- elif message.role == \"tool\" %}\n {%- if (loop.index0 == 0) or (messages[loop.index0 - 1].role != \"tool\") %}\n {{- '<|im_start|>user' }}\n {%- endif %}\n {{- '\\n<tool_response>\\n' }}\n {{- message.content }}\n {{- '\\n</tool_response>' }}\n {%- if loop.last or (messages[loop.index0 + 1].role != \"tool\") %}\n {{- '<|im_end|>\\n' }}\n {%- endif %}\n {%- endif %}\n{%- endfor %}\n{%- if add_generation_prompt %}\n {{- '<|im_start|>assistant\\n' }}\n{%- endif %}\n"
|
| 17 |
+
|
| 18 |
+
run:
|
| 19 |
+
project_id: 001-linalgzero-sft-110
|
| 20 |
+
project: linalgzero-eval
|
| 21 |
+
base_model: "results/LinalgZero-SFT-110-checkpoint-300/checkpoint-300"
|
| 22 |
+
model: "results/LinalgZero-SFT-110-checkpoint-300/checkpoint-300"
|
| 23 |
+
dataset_path: "atomwalk12/linalgzero-grpo"
|
| 24 |
+
env: linear_algebra
|
| 25 |
+
model_provider: hosted_vllm
|
| 26 |
+
user_model_provider: openai
|
| 27 |
+
user_strategy: mathematician
|
| 28 |
+
user_model: gpt-4o
|
| 29 |
+
agent_strategy: tool-calling-rl
|
| 30 |
+
temperature: 0.7
|
| 31 |
+
top_p: 0.8
|
| 32 |
+
repetition_penalty: 1.0
|
| 33 |
+
task_split: test
|
| 34 |
+
log_dir: rl_results
|
| 35 |
+
skip_eval: False
|
| 36 |
+
in_process: False
|
| 37 |
+
max_assistant_turns: 5
|
| 38 |
+
max_completion_tokens: 800
|
| 39 |
+
skip_special_tokens: False
|
| 40 |
+
stop: [
|
| 41 |
+
"<tool_response>",
|
| 42 |
+
"</tool_response>",
|
| 43 |
+
"User:",
|
| 44 |
+
]
|
| 45 |
+
seed: 42
|
| 46 |
+
|
| 47 |
+
trainer:
|
| 48 |
+
max_prompt_length: 5000
|
| 49 |
+
max_completion_length: 800
|
| 50 |
+
report_to: ["wandb"]
|
| 51 |
+
seed: 42
|
| 52 |
+
data_seed: 42
|
| 53 |
+
|
| 54 |
+
engine:
|
| 55 |
+
gpu_memory_utilization: 0.6
|
| 56 |
+
max_lora_rank: 32
|
| 57 |
+
enable_sleep_mode: true
|
| 58 |
+
max_model_len: 6144
|
| 59 |
+
dtype: 'bfloat16'
|
| 60 |
+
quantization: null
|
| 61 |
+
load_format: 'auto'
|
| 62 |
+
seed: 42
|
linalg_zero/config/grpo/Qwen/Qwen2.5-3B/eval/linalgzero-sft.yaml
ADDED
|
@@ -0,0 +1,62 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
init:
|
| 2 |
+
max_lora_rank: 32
|
| 3 |
+
dtype: bfloat16
|
| 4 |
+
load_in_4bit: false
|
| 5 |
+
load_in_8bit: false
|
| 6 |
+
max_seq_length: 6144
|
| 7 |
+
|
| 8 |
+
training:
|
| 9 |
+
trajectories_per_group: 4
|
| 10 |
+
groups_per_step: 8
|
| 11 |
+
learning_rate: 2e-6
|
| 12 |
+
beta: 0.03
|
| 13 |
+
eval_steps: 100
|
| 14 |
+
num_epochs: 2
|
| 15 |
+
train_mode: sync_rl
|
| 16 |
+
chat_template: "{%- if tools %}\n {{- '<|im_start|>system\\n' }}\n {%- if messages[0]['role'] == 'system' %}\n {{- messages[0]['content'] }}\n {%- else %}\n {{- 'You are a helpful assistant.' }}\n {%- endif %}\n {{- \"\\n\\n# Tools\\n\\nYou may call one or more functions to assist with the user query.\\n\\nYou are provided with function signatures within <tools></tools> XML tags:\\n<tools>\" }}\n {%- for tool in tools %}\n {{- \"\\n\" }}\n {{- tool | tojson }}\n {%- endfor %}\n {{- \"\\n</tools>\\n\\nFor each function call, return a json object with function name and arguments within <tool_call></tool_call> XML tags:\\n<tool_call>\\n{\\\"name\\\": <function-name>, \\\"arguments\\\": <args-json-object>}\\n</tool_call><|im_end|>\\n\" }}\n{%- else %}\n {%- if messages[0]['role'] == 'system' %}\n {{- '<|im_start|>system\\n' + messages[0]['content'] + '<|im_end|>\\n' }}\n {%- else %}\n {{- '<|im_start|>system\\nYou are a helpful assistant.<|im_end|>\\n' }}\n {%- endif %}\n{%- endif %}\n{%- for message in messages %}\n {%- if (message.role == \"user\") or (message.role == \"system\" and not loop.first) or (message.role == \"assistant\" and not message.tool_calls) %}\n {{- '<|im_start|>' + message.role + '\\n' + message.content + '<|im_end|>' + '\\n' }}\n {%- elif message.role == \"assistant\" %}\n {{- '<|im_start|>' + message.role }}\n {%- if message.content %}\n {{- '\\n' + message.content }}\n {%- endif %}\n {%- for tool_call in message.tool_calls %}\n {%- if tool_call.function is defined %}\n {%- set tool_call = tool_call.function %}\n {%- endif %}\n {{- '\\n<tool_call>\\n{\"name\": \"' }}\n {{- tool_call.name }}\n {{- '\", \"arguments\": ' }}\n {{- tool_call.arguments | tojson }}\n {{- '}\\n</tool_call>' }}\n {%- endfor %}\n {{- '<|im_end|>\\n' }}\n {%- elif message.role == \"tool\" %}\n {%- if (loop.index0 == 0) or (messages[loop.index0 - 1].role != \"tool\") %}\n {{- '<|im_start|>user' }}\n {%- endif %}\n {{- '\\n<tool_response>\\n' }}\n {{- message.content }}\n {{- '\\n</tool_response>' }}\n {%- if loop.last or (messages[loop.index0 + 1].role != \"tool\") %}\n {{- '<|im_end|>\\n' }}\n {%- endif %}\n {%- endif %}\n{%- endfor %}\n{%- if add_generation_prompt %}\n {{- '<|im_start|>assistant\\n' }}\n{%- endif %}\n"
|
| 17 |
+
|
| 18 |
+
run:
|
| 19 |
+
project_id: 004-linalgzero-sft
|
| 20 |
+
project: linalgzero-eval
|
| 21 |
+
base_model: "atomwalk12/LinAlgZero-SFT"
|
| 22 |
+
model: "atomwalk12/LinAlgZero-SFT"
|
| 23 |
+
dataset_path: "atomwalk12/linalgzero-grpo"
|
| 24 |
+
env: linear_algebra
|
| 25 |
+
model_provider: hosted_vllm
|
| 26 |
+
user_model_provider: openai
|
| 27 |
+
user_strategy: mathematician
|
| 28 |
+
user_model: gpt-4o
|
| 29 |
+
agent_strategy: tool-calling-rl
|
| 30 |
+
temperature: 0.0
|
| 31 |
+
top_p: null
|
| 32 |
+
repetition_penalty: 1.0
|
| 33 |
+
task_split: test
|
| 34 |
+
log_dir: rl_results
|
| 35 |
+
skip_eval: False
|
| 36 |
+
in_process: False
|
| 37 |
+
max_assistant_turns: 5
|
| 38 |
+
max_completion_tokens: 800
|
| 39 |
+
skip_special_tokens: False
|
| 40 |
+
stop: [
|
| 41 |
+
"<tool_response>",
|
| 42 |
+
"</tool_response>",
|
| 43 |
+
"User:",
|
| 44 |
+
]
|
| 45 |
+
seed: 42
|
| 46 |
+
|
| 47 |
+
trainer:
|
| 48 |
+
max_prompt_length: 5000
|
| 49 |
+
max_completion_length: 800
|
| 50 |
+
report_to: ["wandb"]
|
| 51 |
+
seed: 42
|
| 52 |
+
data_seed: 42
|
| 53 |
+
|
| 54 |
+
engine:
|
| 55 |
+
gpu_memory_utilization: 0.6
|
| 56 |
+
max_lora_rank: 32
|
| 57 |
+
enable_sleep_mode: true
|
| 58 |
+
max_model_len: 6144
|
| 59 |
+
dtype: 'bfloat16'
|
| 60 |
+
quantization: null
|
| 61 |
+
load_format: 'auto'
|
| 62 |
+
seed: 42
|
linalg_zero/config/grpo/Qwen/Qwen2.5-3B/local.yaml
ADDED
|
@@ -0,0 +1,95 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
defaults:
|
| 2 |
+
- /training@training: training_schema
|
| 3 |
+
- /run@run: run_schema
|
| 4 |
+
- _self_
|
| 5 |
+
|
| 6 |
+
|
| 7 |
+
init:
|
| 8 |
+
max_lora_rank: 32
|
| 9 |
+
dtype: bfloat16
|
| 10 |
+
load_in_4bit: false
|
| 11 |
+
load_in_8bit: false
|
| 12 |
+
max_seq_length: 10240
|
| 13 |
+
|
| 14 |
+
training:
|
| 15 |
+
eval_retries: 3
|
| 16 |
+
trajectories_per_group: 8
|
| 17 |
+
groups_per_step: 16
|
| 18 |
+
learning_rate: 1e-6
|
| 19 |
+
beta: 0.0
|
| 20 |
+
eval_steps: 100
|
| 21 |
+
num_epochs: 3
|
| 22 |
+
train_mode: sync_rl
|
| 23 |
+
importance_sampling_level: sequence
|
| 24 |
+
scale_rewards: True
|
| 25 |
+
epsilon: 3e-4
|
| 26 |
+
epsilon_high: 4e-4
|
| 27 |
+
chat_template: "{%- if tools %}\n {{- '<|im_start|>system\\n' }}\n {%- if messages[0]['role'] == 'system' %}\n {{- messages[0]['content'] }}\n {%- else %}\n {{- 'You are a helpful assistant.' }}\n {%- endif %}\n {{- \"\\n\\n# Tools\\n\\nYou may call one or more functions to assist with the user query.\\n\\nYou are provided with function signatures within <tools></tools> XML tags:\\n<tools>\" }}\n {%- for tool in tools %}\n {{- \"\\n\" }}\n {{- tool | tojson }}\n {%- endfor %}\n {{- \"\\n</tools>\\n\\nFor each function call, return a json object with function name and arguments within <tool_call></tool_call> XML tags:\\n<tool_call>\\n{\\\"name\\\": <function-name>, \\\"arguments\\\": <args-json-object>}\\n</tool_call><|im_end|>\\n\" }}\n{%- else %}\n {%- if messages[0]['role'] == 'system' %}\n {{- '<|im_start|>system\\n' + messages[0]['content'] + '<|im_end|>\\n' }}\n {%- else %}\n {{- '<|im_start|>system\\nYou are a helpful assistant.<|im_end|>\\n' }}\n {%- endif %}\n{%- endif %}\n{%- for message in messages %}\n {%- if (message.role == \"user\") or (message.role == \"system\" and not loop.first) or (message.role == \"assistant\" and not message.tool_calls) %}\n {{- '<|im_start|>' + message.role + '\\n' + message.content + '<|im_end|>' + '\\n' }}\n {%- elif message.role == \"assistant\" %}\n {{- '<|im_start|>' + message.role }}\n {%- if message.content %}\n {{- '\\n' + message.content }}\n {%- endif %}\n {%- for tool_call in message.tool_calls %}\n {%- if tool_call.function is defined %}\n {%- set tool_call = tool_call.function %}\n {%- endif %}\n {{- '\\n<tool_call>\\n{\"name\": \"' }}\n {{- tool_call.name }}\n {{- '\", \"arguments\": ' }}\n {{- tool_call.arguments | tojson }}\n {{- '}\\n</tool_call>' }}\n {%- endfor %}\n {{- '<|im_end|>\\n' }}\n {%- elif message.role == \"tool\" %}\n {%- if (loop.index0 == 0) or (messages[loop.index0 - 1].role != \"tool\") %}\n {{- '<|im_start|>user' }}\n {%- endif %}\n {{- '\\n<tool_response>\\n' }}\n {{- message.content }}\n {{- '\\n</tool_response>' }}\n {%- if loop.last or (messages[loop.index0 + 1].role != \"tool\") %}\n {{- '<|im_end|>\\n' }}\n {%- endif %}\n {%- endif %}\n{%- endfor %}\n{%- if add_generation_prompt %}\n {{- '<|im_start|>assistant\\n' }}\n{%- endif %}\n"
|
| 28 |
+
|
| 29 |
+
run:
|
| 30 |
+
project_id: test
|
| 31 |
+
project: linalgzero-temp
|
| 32 |
+
base_model: "atomwalk12/LinalgZero-SFT"
|
| 33 |
+
model: "atomwalk12/LinalgZero-SFT"
|
| 34 |
+
dataset_path: "atomwalk12/linalgzero-grpo"
|
| 35 |
+
env: linear_algebra
|
| 36 |
+
curriculum:
|
| 37 |
+
enabled: true
|
| 38 |
+
metric: tool_calls
|
| 39 |
+
sampling: mixture
|
| 40 |
+
initial_max_tool_calls: 1
|
| 41 |
+
final_max_tool_calls: 3
|
| 42 |
+
mixture_sigma: 0.5
|
| 43 |
+
mixture_min_prob_easiest: 0.1
|
| 44 |
+
fraction_at_start: 1.0
|
| 45 |
+
fraction_at_end: 1.0
|
| 46 |
+
min_total_tasks: 64
|
| 47 |
+
model_provider: hosted_vllm
|
| 48 |
+
user_strategy: mathematician
|
| 49 |
+
agent_strategy: tool-calling-rl
|
| 50 |
+
temperature: 1.0
|
| 51 |
+
top_p: 1.0
|
| 52 |
+
repetition_penalty: 1.0
|
| 53 |
+
task_split: test
|
| 54 |
+
log_dir: rl_results
|
| 55 |
+
skip_eval: False
|
| 56 |
+
in_process: False
|
| 57 |
+
max_assistant_turns: 5
|
| 58 |
+
max_completion_tokens: 1536
|
| 59 |
+
skip_special_tokens: False
|
| 60 |
+
stop: [
|
| 61 |
+
"<tool_response>",
|
| 62 |
+
"</tool_response>",
|
| 63 |
+
"User:",
|
| 64 |
+
]
|
| 65 |
+
seed: 42
|
| 66 |
+
|
| 67 |
+
peft:
|
| 68 |
+
r: 32
|
| 69 |
+
lora_alpha: 64
|
| 70 |
+
use_gradient_checkpointing: "unsloth"
|
| 71 |
+
target_modules: ["q_proj", "k_proj", "v_proj", "o_proj", "gate_proj", "up_proj", "down_proj"]
|
| 72 |
+
random_state: 42
|
| 73 |
+
|
| 74 |
+
trainer:
|
| 75 |
+
learning_rate: 1e-6
|
| 76 |
+
per_device_train_batch_size: 8
|
| 77 |
+
gradient_accumulation_steps: 1 # if != 1, deadlock occurs
|
| 78 |
+
num_generations: 2
|
| 79 |
+
max_prompt_length: 8192
|
| 80 |
+
max_completion_length: 1536
|
| 81 |
+
report_to: ["wandb"]
|
| 82 |
+
save_total_limit: 10
|
| 83 |
+
seed: 42
|
| 84 |
+
data_seed: 42
|
| 85 |
+
max_grad_norm: 1.0
|
| 86 |
+
|
| 87 |
+
engine:
|
| 88 |
+
gpu_memory_utilization: 0.33
|
| 89 |
+
max_lora_rank: 32
|
| 90 |
+
enable_sleep_mode: true
|
| 91 |
+
max_model_len: 10240
|
| 92 |
+
dtype: 'bfloat16'
|
| 93 |
+
quantization: null
|
| 94 |
+
load_format: 'auto'
|
| 95 |
+
seed: 42
|
linalg_zero/config/grpo/Qwen/Qwen2.5-3B/runpod.yaml
ADDED
|
@@ -0,0 +1,95 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
defaults:
|
| 2 |
+
- /training@training: training_schema
|
| 3 |
+
- /run@run: run_schema
|
| 4 |
+
- _self_
|
| 5 |
+
|
| 6 |
+
|
| 7 |
+
init:
|
| 8 |
+
max_lora_rank: 32
|
| 9 |
+
dtype: bfloat16
|
| 10 |
+
load_in_4bit: false
|
| 11 |
+
load_in_8bit: false
|
| 12 |
+
max_seq_length: 10240
|
| 13 |
+
|
| 14 |
+
training:
|
| 15 |
+
eval_retries: 3
|
| 16 |
+
trajectories_per_group: 8
|
| 17 |
+
groups_per_step: 16
|
| 18 |
+
learning_rate: 1e-6
|
| 19 |
+
beta: 0.0
|
| 20 |
+
eval_steps: 100
|
| 21 |
+
num_epochs: 3
|
| 22 |
+
train_mode: sync_rl
|
| 23 |
+
importance_sampling_level: sequence
|
| 24 |
+
scale_rewards: True
|
| 25 |
+
epsilon: 3e-4
|
| 26 |
+
epsilon_high: 4e-4
|
| 27 |
+
chat_template: "{%- if tools %}\n {{- '<|im_start|>system\\n' }}\n {%- if messages[0]['role'] == 'system' %}\n {{- messages[0]['content'] }}\n {%- else %}\n {{- 'You are a helpful assistant.' }}\n {%- endif %}\n {{- \"\\n\\n# Tools\\n\\nYou may call one or more functions to assist with the user query.\\n\\nYou are provided with function signatures within <tools></tools> XML tags:\\n<tools>\" }}\n {%- for tool in tools %}\n {{- \"\\n\" }}\n {{- tool | tojson }}\n {%- endfor %}\n {{- \"\\n</tools>\\n\\nFor each function call, return a json object with function name and arguments within <tool_call></tool_call> XML tags:\\n<tool_call>\\n{\\\"name\\\": <function-name>, \\\"arguments\\\": <args-json-object>}\\n</tool_call><|im_end|>\\n\" }}\n{%- else %}\n {%- if messages[0]['role'] == 'system' %}\n {{- '<|im_start|>system\\n' + messages[0]['content'] + '<|im_end|>\\n' }}\n {%- else %}\n {{- '<|im_start|>system\\nYou are a helpful assistant.<|im_end|>\\n' }}\n {%- endif %}\n{%- endif %}\n{%- for message in messages %}\n {%- if (message.role == \"user\") or (message.role == \"system\" and not loop.first) or (message.role == \"assistant\" and not message.tool_calls) %}\n {{- '<|im_start|>' + message.role + '\\n' + message.content + '<|im_end|>' + '\\n' }}\n {%- elif message.role == \"assistant\" %}\n {{- '<|im_start|>' + message.role }}\n {%- if message.content %}\n {{- '\\n' + message.content }}\n {%- endif %}\n {%- for tool_call in message.tool_calls %}\n {%- if tool_call.function is defined %}\n {%- set tool_call = tool_call.function %}\n {%- endif %}\n {{- '\\n<tool_call>\\n{\"name\": \"' }}\n {{- tool_call.name }}\n {{- '\", \"arguments\": ' }}\n {{- tool_call.arguments | tojson }}\n {{- '}\\n</tool_call>' }}\n {%- endfor %}\n {{- '<|im_end|>\\n' }}\n {%- elif message.role == \"tool\" %}\n {%- if (loop.index0 == 0) or (messages[loop.index0 - 1].role != \"tool\") %}\n {{- '<|im_start|>user' }}\n {%- endif %}\n {{- '\\n<tool_response>\\n' }}\n {{- message.content }}\n {{- '\\n</tool_response>' }}\n {%- if loop.last or (messages[loop.index0 + 1].role != \"tool\") %}\n {{- '<|im_end|>\\n' }}\n {%- endif %}\n {%- endif %}\n{%- endfor %}\n{%- if add_generation_prompt %}\n {{- '<|im_start|>assistant\\n' }}\n{%- endif %}\n"
|
| 28 |
+
|
| 29 |
+
run:
|
| 30 |
+
project_id: linalgzero-3b-grpo-runpod
|
| 31 |
+
project: linalgzero-grpo
|
| 32 |
+
base_model: "atomwalk12/LinalgZero-SFT"
|
| 33 |
+
model: "atomwalk12/LinalgZero-SFT"
|
| 34 |
+
dataset_path: "atomwalk12/linalgzero-grpo"
|
| 35 |
+
env: linear_algebra
|
| 36 |
+
curriculum:
|
| 37 |
+
enabled: true
|
| 38 |
+
metric: tool_calls
|
| 39 |
+
sampling: mixture
|
| 40 |
+
initial_max_tool_calls: 1
|
| 41 |
+
final_max_tool_calls: 3
|
| 42 |
+
mixture_sigma: 0.5
|
| 43 |
+
mixture_min_prob_easiest: 0.1
|
| 44 |
+
fraction_at_start: 1.0
|
| 45 |
+
fraction_at_end: 1.0
|
| 46 |
+
min_total_tasks: 64
|
| 47 |
+
model_provider: hosted_vllm
|
| 48 |
+
user_strategy: mathematician
|
| 49 |
+
agent_strategy: tool-calling-rl
|
| 50 |
+
temperature: 1.0
|
| 51 |
+
top_p: 1.0
|
| 52 |
+
repetition_penalty: 1.0
|
| 53 |
+
task_split: test
|
| 54 |
+
log_dir: rl_results
|
| 55 |
+
skip_eval: False
|
| 56 |
+
in_process: False
|
| 57 |
+
max_assistant_turns: 5
|
| 58 |
+
max_completion_tokens: 1536
|
| 59 |
+
skip_special_tokens: False
|
| 60 |
+
stop: [
|
| 61 |
+
"<tool_response>",
|
| 62 |
+
"</tool_response>",
|
| 63 |
+
"User:",
|
| 64 |
+
]
|
| 65 |
+
seed: 42
|
| 66 |
+
|
| 67 |
+
peft:
|
| 68 |
+
r: 32
|
| 69 |
+
lora_alpha: 64
|
| 70 |
+
use_gradient_checkpointing: "unsloth"
|
| 71 |
+
target_modules: ["q_proj", "k_proj", "v_proj", "o_proj", "gate_proj", "up_proj", "down_proj"]
|
| 72 |
+
random_state: 42
|
| 73 |
+
|
| 74 |
+
trainer:
|
| 75 |
+
learning_rate: 1e-6
|
| 76 |
+
per_device_train_batch_size: 8
|
| 77 |
+
gradient_accumulation_steps: 1 # if != 1, deadlock occurs
|
| 78 |
+
num_generations: 2
|
| 79 |
+
max_prompt_length: 8192
|
| 80 |
+
max_completion_length: 1536
|
| 81 |
+
report_to: ["wandb"]
|
| 82 |
+
save_total_limit: 10
|
| 83 |
+
seed: 42
|
| 84 |
+
data_seed: 42
|
| 85 |
+
max_grad_norm: 1.0
|
| 86 |
+
|
| 87 |
+
engine:
|
| 88 |
+
gpu_memory_utilization: 0.9
|
| 89 |
+
max_lora_rank: 32
|
| 90 |
+
enable_sleep_mode: true
|
| 91 |
+
max_model_len: 10240
|
| 92 |
+
dtype: 'bfloat16'
|
| 93 |
+
quantization: null
|
| 94 |
+
load_format: 'auto'
|
| 95 |
+
seed: 42
|
linalg_zero/config/sft/accelerate/zero2.yaml
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
compute_environment: LOCAL_MACHINE
|
| 2 |
+
debug: false
|
| 3 |
+
deepspeed_config:
|
| 4 |
+
deepspeed_multinode_launcher: standard
|
| 5 |
+
offload_optimizer_device: none
|
| 6 |
+
offload_param_device: none
|
| 7 |
+
zero3_init_flag: false
|
| 8 |
+
zero_stage: 2
|
| 9 |
+
distributed_type: DEEPSPEED
|
| 10 |
+
downcast_bf16: 'no'
|
| 11 |
+
machine_rank: 0
|
| 12 |
+
main_training_function: main
|
| 13 |
+
mixed_precision: bf16
|
| 14 |
+
num_machines: 1
|
| 15 |
+
num_processes: 8
|
| 16 |
+
rdzv_backend: static
|
| 17 |
+
same_network: true
|
| 18 |
+
tpu_env: []
|
| 19 |
+
tpu_use_cluster: false
|
| 20 |
+
tpu_use_sudo: false
|
| 21 |
+
use_cpu: false
|
linalg_zero/config/sft/accelerate/zero3.yaml
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
compute_environment: LOCAL_MACHINE
|
| 2 |
+
debug: false
|
| 3 |
+
deepspeed_config:
|
| 4 |
+
deepspeed_multinode_launcher: standard
|
| 5 |
+
offload_optimizer_device: none
|
| 6 |
+
offload_param_device: none
|
| 7 |
+
zero3_init_flag: true
|
| 8 |
+
zero3_save_16bit_model: true
|
| 9 |
+
zero_stage: 3
|
| 10 |
+
distributed_type: DEEPSPEED
|
| 11 |
+
downcast_bf16: 'no'
|
| 12 |
+
machine_rank: 0
|
| 13 |
+
main_training_function: main
|
| 14 |
+
mixed_precision: bf16
|
| 15 |
+
num_machines: 1
|
| 16 |
+
num_processes: 1 # NOTE: The number of GPUs on one node
|
| 17 |
+
rdzv_backend: static
|
| 18 |
+
same_network: true
|
| 19 |
+
tpu_env: []
|
| 20 |
+
tpu_use_cluster: false
|
| 21 |
+
tpu_use_sudo: false
|
| 22 |
+
use_cpu: false
|
linalg_zero/config/sft/qwen2.5-3B/instruct.yaml
ADDED
|
@@ -0,0 +1,78 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# SFT config for a light touch-up of the Instruct model,
|
| 2 |
+
# targeting only the input/output layers (embed_tokens and lm_head).
|
| 3 |
+
|
| 4 |
+
# Model arguments
|
| 5 |
+
model_name_or_path: Qwen/Qwen2.5-3B-Instruct
|
| 6 |
+
model_revision: main
|
| 7 |
+
torch_dtype: bfloat16
|
| 8 |
+
attn_implementation: flash_attention_2
|
| 9 |
+
|
| 10 |
+
# Quantization / PEFT
|
| 11 |
+
load_in_4bit: false
|
| 12 |
+
load_in_8bit: false # Use 16bit training
|
| 13 |
+
bnb_4bit_quant_type: nf4
|
| 14 |
+
use_peft: false # IMPORTANT: do not create new LoRA adapters
|
| 15 |
+
|
| 16 |
+
# Data training arguments
|
| 17 |
+
dataset_name: atomwalk12/linalgzero-sft
|
| 18 |
+
dataset_config: default
|
| 19 |
+
eval_dataset_config: default
|
| 20 |
+
dataset_num_proc: 12
|
| 21 |
+
dataset_text_field: text
|
| 22 |
+
eos_token: <|im_end|>
|
| 23 |
+
add_special_tokens: true
|
| 24 |
+
|
| 25 |
+
# SFT trainer config
|
| 26 |
+
bf16: true
|
| 27 |
+
|
| 28 |
+
## Evaluation parameters
|
| 29 |
+
do_eval: true # final evaluation at the end of training
|
| 30 |
+
eval_on_start: true # evaluation at the start of training
|
| 31 |
+
eval_strategy: 'steps'
|
| 32 |
+
eval_steps: 100
|
| 33 |
+
max_eval_samples: 520
|
| 34 |
+
final_eval_max_samples: 520
|
| 35 |
+
|
| 36 |
+
metric_for_best_model: eval_format_accuracy
|
| 37 |
+
greater_is_better: true
|
| 38 |
+
load_best_model_at_end: true
|
| 39 |
+
save_total_limit: 5
|
| 40 |
+
eval_max_new_tokens: 800
|
| 41 |
+
|
| 42 |
+
callbacks:
|
| 43 |
+
- tool_calling_accuracy
|
| 44 |
+
|
| 45 |
+
## Training parameters
|
| 46 |
+
gradient_accumulation_steps: 16
|
| 47 |
+
gradient_checkpointing: true
|
| 48 |
+
gradient_checkpointing_kwargs:
|
| 49 |
+
use_reentrant: false
|
| 50 |
+
hub_model_id: atomwalk12/LinalgZero-SFT-Instruct
|
| 51 |
+
hub_strategy: every_save
|
| 52 |
+
learning_rate: 2e-5 # Alternatives: 5e-5/4e-5/3e-5/2e-5 and train for 2 epochs
|
| 53 |
+
log_level: info
|
| 54 |
+
logging_steps: 5
|
| 55 |
+
logging_strategy: steps
|
| 56 |
+
lr_scheduler_type: cosine_with_min_lr
|
| 57 |
+
lr_scheduler_kwargs:
|
| 58 |
+
min_lr_rate: 0.1
|
| 59 |
+
packing: false
|
| 60 |
+
max_grad_norm: 0.2
|
| 61 |
+
max_seq_length: 8192
|
| 62 |
+
max_length: 8192
|
| 63 |
+
max_steps: -1
|
| 64 |
+
num_train_epochs: 1
|
| 65 |
+
output_dir: results/LinalgZero-SFT-Instruct
|
| 66 |
+
overwrite_output_dir: true
|
| 67 |
+
per_device_eval_batch_size: 1
|
| 68 |
+
per_device_train_batch_size: 1
|
| 69 |
+
push_to_hub: true
|
| 70 |
+
report_to:
|
| 71 |
+
- wandb
|
| 72 |
+
save_strategy: steps
|
| 73 |
+
save_steps: 100
|
| 74 |
+
seed: 42
|
| 75 |
+
use_liger_kernel: false
|
| 76 |
+
warmup_ratio: 0.03
|
| 77 |
+
chat_template: "{%- if tools %}\n {{- '<|im_start|>system\\n' }}\n {%- if messages[0]['role'] == 'system' %}\n {{- messages[0]['content'] }}\n {%- else %}\n {{- 'You are a helpful assistant.' }}\n {%- endif %}\n {{- \"\\n\\n# Tools\\n\\nYou may call one or more functions to assist with the user query.\\n\\nYou are provided with function signatures within <tools></tools> XML tags:\\n<tools>\" }}\n {%- for tool in tools %}\n {{- \"\\n\" }}\n {{- tool | tojson }}\n {%- endfor %}\n {{- \"\\n</tools>\\n\\nFor each function call, return a json object with function name and arguments within <tool_call></tool_call> XML tags:\\n<tool_call>\\n{\\\"name\\\": <function-name>, \\\"arguments\\\": <args-json-object>}\\n</tool_call><|im_end|>\\n\" }}\n{%- else %}\n {%- if messages[0]['role'] == 'system' %}\n {{- '<|im_start|>system\\n' + messages[0]['content'] + '<|im_end|>\\n' }}\n {%- else %}\n {{- '<|im_start|>system\\nYou are a helpful assistant.<|im_end|>\\n' }}\n {%- endif %}\n{%- endif %}\n{%- for message in messages %}\n {%- if (message.role == \"user\") or (message.role == \"system\" and not loop.first) or (message.role == \"assistant\" and not message.tool_calls) %}\n {{- '<|im_start|>' + message.role + '\\n' + message.content + '<|im_end|>' + '\\n' }}\n {%- elif message.role == \"assistant\" %}\n {{- '<|im_start|>' + message.role }}\n {%- if message.content %}\n {{- '\\n' + message.content }}\n {%- endif %}\n {%- for tool_call in message.tool_calls %}\n {%- if tool_call.function is defined %}\n {%- set tool_call = tool_call.function %}\n {%- endif %}\n {{- '\\n<tool_call>\\n{\"name\": \"' }}\n {{- tool_call.name }}\n {{- '\", \"arguments\": ' }}\n {{- tool_call.arguments | tojson }}\n {{- '}\\n</tool_call>' }}\n {%- endfor %}\n {{- '<|im_end|>\\n' }}\n {%- elif message.role == \"tool\" %}\n {%- if (loop.index0 == 0) or (messages[loop.index0 - 1].role != \"tool\") %}\n {{- '<|im_start|>user' }}\n {%- endif %}\n {{- '\\n<tool_response>\\n' }}\n {{- message.content }}\n {{- '\\n</tool_response>' }}\n {%- if loop.last or (messages[loop.index0 + 1].role != \"tool\") %}\n {{- '<|im_end|>\\n' }}\n {%- endif %}\n {%- endif %}\n{%- endfor %}\n{%- if add_generation_prompt %}\n {{- '<|im_start|>assistant\\n' }}\n{%- endif %}\n"
|
| 78 |
+
optim: adamw_torch_fused
|
linalg_zero/config/sft/qwen2.5-3B/lora.yaml
ADDED
|
@@ -0,0 +1,97 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# NOTE: ADJUST gradient_accumulation_steps, per_device_eval_batch_size, per_device_train_batch_size
|
| 2 |
+
|
| 3 |
+
# Model arguments
|
| 4 |
+
model_name_or_path: Qwen/Qwen2.5-3B
|
| 5 |
+
model_revision: main
|
| 6 |
+
torch_dtype: bfloat16
|
| 7 |
+
attn_implementation: flash_attention_2
|
| 8 |
+
|
| 9 |
+
# Model arguments
|
| 10 |
+
load_in_4bit: false
|
| 11 |
+
load_in_8bit: false # Use 16bit training
|
| 12 |
+
bnb_4bit_quant_type: nf4
|
| 13 |
+
use_peft: true
|
| 14 |
+
lora_r: 32 # NOTE: Alternatively 16 and 64 if underfitting
|
| 15 |
+
lora_alpha: 64
|
| 16 |
+
lora_dropout: 0.1
|
| 17 |
+
lora_target_modules: ["q_proj", "k_proj", "v_proj", "o_proj", "gate_proj", "up_proj", "down_proj"]
|
| 18 |
+
lora_task_type: "CAUSAL_LM"
|
| 19 |
+
# vllm parameters
|
| 20 |
+
# gpu_memory_utilization: 0.95
|
| 21 |
+
# enforce_eager: true
|
| 22 |
+
# fast_inference: true
|
| 23 |
+
|
| 24 |
+
# Data training arguments
|
| 25 |
+
dataset_name: atomwalk12/linalgzero-sft
|
| 26 |
+
dataset_config: default
|
| 27 |
+
eval_dataset_config: default
|
| 28 |
+
dataset_num_proc: 12
|
| 29 |
+
dataset_text_field: text
|
| 30 |
+
eos_token: <|im_end|>
|
| 31 |
+
add_special_tokens: true
|
| 32 |
+
|
| 33 |
+
# SFT trainer config
|
| 34 |
+
bf16: true
|
| 35 |
+
|
| 36 |
+
## Evaluation parameters
|
| 37 |
+
do_eval: true # does a final evaluation step at the end of training
|
| 38 |
+
eval_on_start: true # does an evaluation step at the start of training
|
| 39 |
+
eval_strategy: 'steps' # can be "no","epoch","steps"
|
| 40 |
+
eval_steps: 100 # NOTE: Check also save_steps
|
| 41 |
+
max_eval_samples: 520 # use all evaluation samples (720 format checks -- 40*6*2+40*4*3+40*3*4 (520 samples))
|
| 42 |
+
final_eval_max_samples: 520
|
| 43 |
+
|
| 44 |
+
# NOTE: if we use a customized metric for best model selection, ensure to adjust:
|
| 45 |
+
# - metric_for_best_model: eval_format_accuracy or eval_loss
|
| 46 |
+
# - greater_is_better: true or false (if eval_loss is set)
|
| 47 |
+
# - load_best_model_at_end: true
|
| 48 |
+
metric_for_best_model: eval_format_accuracy
|
| 49 |
+
greater_is_better: true
|
| 50 |
+
load_best_model_at_end: true
|
| 51 |
+
save_total_limit: 5
|
| 52 |
+
eval_max_new_tokens: 800
|
| 53 |
+
# NOTE: for early stopping:
|
| 54 |
+
# - early_stopping_patience (3)
|
| 55 |
+
# - early_stopping_threshold (0.01)
|
| 56 |
+
# - callback (early_stopping)
|
| 57 |
+
|
| 58 |
+
callbacks:
|
| 59 |
+
- tool_calling_accuracy
|
| 60 |
+
# - push_to_hub_revision
|
| 61 |
+
|
| 62 |
+
|
| 63 |
+
## Training parameters
|
| 64 |
+
# NOTE: adjust batch size, lr
|
| 65 |
+
gradient_accumulation_steps: 16 # Recommended batch size: 16
|
| 66 |
+
gradient_checkpointing: true
|
| 67 |
+
gradient_checkpointing_kwargs:
|
| 68 |
+
use_reentrant: false
|
| 69 |
+
hub_model_id: atomwalk12/LinalgZero-SFT-LoRA
|
| 70 |
+
hub_strategy: every_save
|
| 71 |
+
learning_rate: 2e-5 # Alternatives: 5e-5/4e-5/3e-5/2e-5 and train for 2 epochs
|
| 72 |
+
log_level: info
|
| 73 |
+
logging_steps: 5
|
| 74 |
+
logging_strategy: steps
|
| 75 |
+
lr_scheduler_type: cosine_with_min_lr
|
| 76 |
+
lr_scheduler_kwargs:
|
| 77 |
+
min_lr_rate: 0.1
|
| 78 |
+
packing: false
|
| 79 |
+
max_grad_norm: 0.2
|
| 80 |
+
max_seq_length: 8192
|
| 81 |
+
max_length: 8192
|
| 82 |
+
max_steps: -1
|
| 83 |
+
num_train_epochs: 1 # NOTE: Alternatively 2 if underfitting
|
| 84 |
+
output_dir: results/LinalgZero-SFT-LoRA
|
| 85 |
+
overwrite_output_dir: true
|
| 86 |
+
per_device_eval_batch_size: 1 # Overridden to 1 by unsloth
|
| 87 |
+
per_device_train_batch_size: 1
|
| 88 |
+
push_to_hub: true
|
| 89 |
+
report_to:
|
| 90 |
+
- wandb
|
| 91 |
+
save_strategy: steps
|
| 92 |
+
save_steps: 100
|
| 93 |
+
seed: 42
|
| 94 |
+
use_liger_kernel: false # Supported only for Llama, Mistral, Mixtral, and Gemma models
|
| 95 |
+
warmup_ratio: 0.03
|
| 96 |
+
chat_template: "{%- if tools %}\n {{- '<|im_start|>system\\n' }}\n {%- if messages[0]['role'] == 'system' %}\n {{- messages[0]['content'] }}\n {%- else %}\n {{- 'You are a helpful assistant.' }}\n {%- endif %}\n {{- \"\\n\\n# Tools\\n\\nYou may call one or more functions to assist with the user query.\\n\\nYou are provided with function signatures within <tools></tools> XML tags:\\n<tools>\" }}\n {%- for tool in tools %}\n {{- \"\\n\" }}\n {{- tool | tojson }}\n {%- endfor %}\n {{- \"\\n</tools>\\n\\nFor each function call, return a json object with function name and arguments within <tool_call></tool_call> XML tags:\\n<tool_call>\\n{\\\"name\\\": <function-name>, \\\"arguments\\\": <args-json-object>}\\n</tool_call><|im_end|>\\n\" }}\n{%- else %}\n {%- if messages[0]['role'] == 'system' %}\n {{- '<|im_start|>system\\n' + messages[0]['content'] + '<|im_end|>\\n' }}\n {%- else %}\n {{- '<|im_start|>system\\nYou are a helpful assistant.<|im_end|>\\n' }}\n {%- endif %}\n{%- endif %}\n{%- for message in messages %}\n {%- if (message.role == \"user\") or (message.role == \"system\" and not loop.first) or (message.role == \"assistant\" and not message.tool_calls) %}\n {{- '<|im_start|>' + message.role + '\\n' + message.content + '<|im_end|>' + '\\n' }}\n {%- elif message.role == \"assistant\" %}\n {{- '<|im_start|>' + message.role }}\n {%- if message.content %}\n {{- '\\n' + message.content }}\n {%- endif %}\n {%- for tool_call in message.tool_calls %}\n {%- if tool_call.function is defined %}\n {%- set tool_call = tool_call.function %}\n {%- endif %}\n {{- '\\n<tool_call>\\n{\"name\": \"' }}\n {{- tool_call.name }}\n {{- '\", \"arguments\": ' }}\n {{- tool_call.arguments | tojson }}\n {{- '}\\n</tool_call>' }}\n {%- endfor %}\n {{- '<|im_end|>\\n' }}\n {%- elif message.role == \"tool\" %}\n {%- if (loop.index0 == 0) or (messages[loop.index0 - 1].role != \"tool\") %}\n {{- '<|im_start|>user' }}\n {%- endif %}\n {{- '\\n<tool_response>\\n' }}\n {{- message.content }}\n {{- '\\n</tool_response>' }}\n {%- if loop.last or (messages[loop.index0 + 1].role != \"tool\") %}\n {{- '<|im_end|>\\n' }}\n {%- endif %}\n {%- endif %}\n{%- endfor %}\n{%- if add_generation_prompt %}\n {{- '<|im_start|>assistant\\n' }}\n{%- endif %}\n"
|
| 97 |
+
optim: adamw_torch_fused
|
linalg_zero/config/sft/qwen2.5-3B/merged.yaml
ADDED
|
@@ -0,0 +1,78 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# SFT config for a light touch-up of the merged model,
|
| 2 |
+
# targeting only the input/output layers (embed_tokens and lm_head).
|
| 3 |
+
|
| 4 |
+
# Model arguments
|
| 5 |
+
model_name_or_path: results/LinalgZero-SFT-merged
|
| 6 |
+
model_revision: main
|
| 7 |
+
torch_dtype: bfloat16
|
| 8 |
+
attn_implementation: flash_attention_2
|
| 9 |
+
|
| 10 |
+
# Quantization / PEFT
|
| 11 |
+
load_in_4bit: false
|
| 12 |
+
load_in_8bit: false # Use 16bit training
|
| 13 |
+
bnb_4bit_quant_type: nf4
|
| 14 |
+
use_peft: false # IMPORTANT: do not create new LoRA adapters
|
| 15 |
+
|
| 16 |
+
# Data training arguments
|
| 17 |
+
dataset_name: atomwalk12/linalgzero-sft
|
| 18 |
+
dataset_config: default
|
| 19 |
+
eval_dataset_config: default
|
| 20 |
+
dataset_num_proc: 12
|
| 21 |
+
dataset_text_field: text
|
| 22 |
+
eos_token: <|im_end|>
|
| 23 |
+
add_special_tokens: true
|
| 24 |
+
|
| 25 |
+
# SFT trainer config
|
| 26 |
+
bf16: true
|
| 27 |
+
|
| 28 |
+
## Evaluation parameters
|
| 29 |
+
do_eval: true # final evaluation at the end of training
|
| 30 |
+
eval_on_start: true # evaluation at the start of training
|
| 31 |
+
eval_strategy: 'steps'
|
| 32 |
+
eval_steps: 100
|
| 33 |
+
max_eval_samples: 520
|
| 34 |
+
final_eval_max_samples: 520
|
| 35 |
+
|
| 36 |
+
metric_for_best_model: eval_format_accuracy
|
| 37 |
+
greater_is_better: true
|
| 38 |
+
load_best_model_at_end: true
|
| 39 |
+
save_total_limit: 5
|
| 40 |
+
eval_max_new_tokens: 800
|
| 41 |
+
|
| 42 |
+
callbacks:
|
| 43 |
+
- tool_calling_accuracy
|
| 44 |
+
|
| 45 |
+
## Training parameters
|
| 46 |
+
gradient_accumulation_steps: 16
|
| 47 |
+
gradient_checkpointing: true
|
| 48 |
+
gradient_checkpointing_kwargs:
|
| 49 |
+
use_reentrant: false
|
| 50 |
+
hub_model_id: atomwalk12/LinalgZero-SFT
|
| 51 |
+
hub_strategy: every_save
|
| 52 |
+
learning_rate: 2e-5 # Alternatives: 5e-5/4e-5/3e-5/2e-5 and train for 2 epochs
|
| 53 |
+
log_level: info
|
| 54 |
+
logging_steps: 5
|
| 55 |
+
logging_strategy: steps
|
| 56 |
+
lr_scheduler_type: cosine_with_min_lr
|
| 57 |
+
lr_scheduler_kwargs:
|
| 58 |
+
min_lr_rate: 0.1
|
| 59 |
+
packing: false
|
| 60 |
+
max_grad_norm: 0.2
|
| 61 |
+
max_seq_length: 8192
|
| 62 |
+
max_length: 8192
|
| 63 |
+
max_steps: -1
|
| 64 |
+
num_train_epochs: 1
|
| 65 |
+
output_dir: results/LinalgZero-SFT
|
| 66 |
+
overwrite_output_dir: true
|
| 67 |
+
per_device_eval_batch_size: 1
|
| 68 |
+
per_device_train_batch_size: 1
|
| 69 |
+
push_to_hub: true
|
| 70 |
+
report_to:
|
| 71 |
+
- wandb
|
| 72 |
+
save_strategy: steps
|
| 73 |
+
save_steps: 100
|
| 74 |
+
seed: 42
|
| 75 |
+
use_liger_kernel: false
|
| 76 |
+
warmup_ratio: 0.03
|
| 77 |
+
chat_template: "{%- if tools %}\n {{- '<|im_start|>system\\n' }}\n {%- if messages[0]['role'] == 'system' %}\n {{- messages[0]['content'] }}\n {%- else %}\n {{- 'You are a helpful assistant.' }}\n {%- endif %}\n {{- \"\\n\\n# Tools\\n\\nYou may call one or more functions to assist with the user query.\\n\\nYou are provided with function signatures within <tools></tools> XML tags:\\n<tools>\" }}\n {%- for tool in tools %}\n {{- \"\\n\" }}\n {{- tool | tojson }}\n {%- endfor %}\n {{- \"\\n</tools>\\n\\nFor each function call, return a json object with function name and arguments within <tool_call></tool_call> XML tags:\\n<tool_call>\\n{\\\"name\\\": <function-name>, \\\"arguments\\\": <args-json-object>}\\n</tool_call><|im_end|>\\n\" }}\n{%- else %}\n {%- if messages[0]['role'] == 'system' %}\n {{- '<|im_start|>system\\n' + messages[0]['content'] + '<|im_end|>\\n' }}\n {%- else %}\n {{- '<|im_start|>system\\nYou are a helpful assistant.<|im_end|>\\n' }}\n {%- endif %}\n{%- endif %}\n{%- for message in messages %}\n {%- if (message.role == \"user\") or (message.role == \"system\" and not loop.first) or (message.role == \"assistant\" and not message.tool_calls) %}\n {{- '<|im_start|>' + message.role + '\\n' + message.content + '<|im_end|>' + '\\n' }}\n {%- elif message.role == \"assistant\" %}\n {{- '<|im_start|>' + message.role }}\n {%- if message.content %}\n {{- '\\n' + message.content }}\n {%- endif %}\n {%- for tool_call in message.tool_calls %}\n {%- if tool_call.function is defined %}\n {%- set tool_call = tool_call.function %}\n {%- endif %}\n {{- '\\n<tool_call>\\n{\"name\": \"' }}\n {{- tool_call.name }}\n {{- '\", \"arguments\": ' }}\n {{- tool_call.arguments | tojson }}\n {{- '}\\n</tool_call>' }}\n {%- endfor %}\n {{- '<|im_end|>\\n' }}\n {%- elif message.role == \"tool\" %}\n {%- if (loop.index0 == 0) or (messages[loop.index0 - 1].role != \"tool\") %}\n {{- '<|im_start|>user' }}\n {%- endif %}\n {{- '\\n<tool_response>\\n' }}\n {{- message.content }}\n {{- '\\n</tool_response>' }}\n {%- if loop.last or (messages[loop.index0 + 1].role != \"tool\") %}\n {{- '<|im_end|>\\n' }}\n {%- endif %}\n {%- endif %}\n{%- endfor %}\n{%- if add_generation_prompt %}\n {{- '<|im_start|>assistant\\n' }}\n{%- endif %}\n"
|
| 78 |
+
optim: adamw_torch_fused
|
linalg_zero/config/sft/qwen2.5-3B/nst.yaml
ADDED
|
@@ -0,0 +1,99 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# NOTE: ADJUST gradient_accumulation_steps, per_device_eval_batch_size, per_device_train_batch_size
|
| 2 |
+
|
| 3 |
+
# Model arguments
|
| 4 |
+
model_name_or_path: Qwen/Qwen2.5-3B
|
| 5 |
+
model_revision: main
|
| 6 |
+
torch_dtype: bfloat16
|
| 7 |
+
attn_implementation: flash_attention_2
|
| 8 |
+
wandb_project: linalgzero-sft
|
| 9 |
+
wandb_run_id: run001-sft-nst
|
| 10 |
+
|
| 11 |
+
# Model arguments
|
| 12 |
+
load_in_4bit: false
|
| 13 |
+
load_in_8bit: false # Use 16bit training
|
| 14 |
+
bnb_4bit_quant_type: nf4
|
| 15 |
+
use_peft: true
|
| 16 |
+
lora_r: 32 # NOTE: Alternatively 16 and 64 if underfitting
|
| 17 |
+
lora_alpha: 64
|
| 18 |
+
lora_dropout: 0.1
|
| 19 |
+
lora_target_modules: ["q_proj", "k_proj", "v_proj", "o_proj", "gate_proj", "up_proj", "down_proj"]
|
| 20 |
+
lora_task_type: "CAUSAL_LM"
|
| 21 |
+
# vllm parameters
|
| 22 |
+
# gpu_memory_utilization: 0.95
|
| 23 |
+
# enforce_eager: true
|
| 24 |
+
# fast_inference: true
|
| 25 |
+
|
| 26 |
+
# Data training arguments
|
| 27 |
+
dataset_name: atomwalk12/linalgzero-sft-110
|
| 28 |
+
dataset_config: default
|
| 29 |
+
eval_dataset_config: default
|
| 30 |
+
dataset_num_proc: 12
|
| 31 |
+
dataset_text_field: text
|
| 32 |
+
eos_token: <|im_end|>
|
| 33 |
+
|
| 34 |
+
# SFT trainer config
|
| 35 |
+
bf16: true
|
| 36 |
+
|
| 37 |
+
## Evaluation parameters
|
| 38 |
+
do_eval: true # does a final evaluation step at the end of training
|
| 39 |
+
eval_on_start: false # does an evaluation step at the start of training
|
| 40 |
+
eval_strategy: 'steps' # can be "no","epoch","steps"
|
| 41 |
+
eval_steps: 110 # NOTE: Check also save_steps
|
| 42 |
+
max_eval_samples: 260 # use all evaluation samples (720 format checks -- 40*6*2+40*4*3+40*3*4 (520 samples))
|
| 43 |
+
final_eval_max_samples: 260
|
| 44 |
+
add_special_tokens: False
|
| 45 |
+
|
| 46 |
+
# NOTE: if we use a customized metric for best model selection, ensure to adjust:
|
| 47 |
+
# - metric_for_best_model: eval_format_accuracy or eval_loss
|
| 48 |
+
# - greater_is_better: true or false (if eval_loss is set)
|
| 49 |
+
# - load_best_model_at_end: true
|
| 50 |
+
metric_for_best_model: eval_format_accuracy
|
| 51 |
+
greater_is_better: true
|
| 52 |
+
load_best_model_at_end: true
|
| 53 |
+
save_total_limit: 30
|
| 54 |
+
eval_max_new_tokens: 800
|
| 55 |
+
# NOTE: for early stopping:
|
| 56 |
+
# - early_stopping_patience (3)
|
| 57 |
+
# - early_stopping_threshold (0.01)
|
| 58 |
+
# - callback (early_stopping)
|
| 59 |
+
|
| 60 |
+
callbacks:
|
| 61 |
+
- tool_calling_accuracy
|
| 62 |
+
# - push_to_hub_revision
|
| 63 |
+
|
| 64 |
+
|
| 65 |
+
## Training parameters
|
| 66 |
+
# NOTE: adjust batch size, lr
|
| 67 |
+
gradient_accumulation_steps: 16 # Recommended batch size: 16
|
| 68 |
+
gradient_checkpointing: true
|
| 69 |
+
gradient_checkpointing_kwargs:
|
| 70 |
+
use_reentrant: false
|
| 71 |
+
hub_model_id: atomwalk12/LinalgZero-SFT-LoRA-NST
|
| 72 |
+
hub_strategy: every_save
|
| 73 |
+
learning_rate: 2e-5 # Alternatives: 5e-5/4e-5/3e-5/2e-5 and train for 2 epochs
|
| 74 |
+
log_level: info
|
| 75 |
+
logging_steps: 5
|
| 76 |
+
logging_strategy: steps
|
| 77 |
+
lr_scheduler_type: cosine_with_min_lr
|
| 78 |
+
lr_scheduler_kwargs:
|
| 79 |
+
min_lr_rate: 0.1
|
| 80 |
+
packing: false
|
| 81 |
+
max_grad_norm: 0.2
|
| 82 |
+
max_seq_length: 8192
|
| 83 |
+
max_length: 8192
|
| 84 |
+
max_steps: -1
|
| 85 |
+
num_train_epochs: 1 # NOTE: Alternatively 2 if underfitting
|
| 86 |
+
output_dir: results/LinalgZero-SFT-LoRA-NST
|
| 87 |
+
overwrite_output_dir: true
|
| 88 |
+
per_device_eval_batch_size: 1 # Overridden to 1 by unsloth
|
| 89 |
+
per_device_train_batch_size: 1
|
| 90 |
+
push_to_hub: true
|
| 91 |
+
report_to:
|
| 92 |
+
- wandb
|
| 93 |
+
save_strategy: steps
|
| 94 |
+
save_steps: 110
|
| 95 |
+
seed: 42
|
| 96 |
+
use_liger_kernel: false # Supported only for Llama, Mistral, Mixtral, and Gemma models
|
| 97 |
+
warmup_ratio: 0.03
|
| 98 |
+
chat_template: "{%- if tools %}\n {{- '<|im_start|>system\\n' }}\n {%- if messages[0]['role'] == 'system' %}\n {{- messages[0]['content'] }}\n {%- else %}\n {{- 'You are a helpful assistant.' }}\n {%- endif %}\n {{- \"\\n\\n# Tools\\n\\nYou may call one or more functions to assist with the user query.\\n\\nYou are provided with function signatures within <tools></tools> XML tags:\\n<tools>\" }}\n {%- for tool in tools %}\n {{- \"\\n\" }}\n {{- tool | tojson }}\n {%- endfor %}\n {{- \"\\n</tools>\\n\\nFor each function call, return a json object with function name and arguments within <tool_call></tool_call> XML tags:\\n<tool_call>\\n{\\\"name\\\": <function-name>, \\\"arguments\\\": <args-json-object>}\\n</tool_call><|im_end|>\\n\" }}\n{%- else %}\n {%- if messages[0]['role'] == 'system' %}\n {{- '<|im_start|>system\\n' + messages[0]['content'] + '<|im_end|>\\n' }}\n {%- else %}\n {{- '<|im_start|>system\\nYou are a helpful assistant.<|im_end|>\\n' }}\n {%- endif %}\n{%- endif %}\n{%- for message in messages %}\n {%- if (message.role == \"user\") or (message.role == \"system\" and not loop.first) or (message.role == \"assistant\" and not message.tool_calls) %}\n {{- '<|im_start|>' + message.role + '\\n' + message.content + '<|im_end|>' + '\\n' }}\n {%- elif message.role == \"assistant\" %}\n {{- '<|im_start|>' + message.role }}\n {%- if message.content %}\n {{- '\\n' + message.content }}\n {%- endif %}\n {%- for tool_call in message.tool_calls %}\n {%- if tool_call.function is defined %}\n {%- set tool_call = tool_call.function %}\n {%- endif %}\n {{- '\\n<tool_call>\\n{\"name\": \"' }}\n {{- tool_call.name }}\n {{- '\", \"arguments\": ' }}\n {{- tool_call.arguments | tojson }}\n {{- '}\\n</tool_call>' }}\n {%- endfor %}\n {{- '<|im_end|>\\n' }}\n {%- elif message.role == \"tool\" %}\n {%- if (loop.index0 == 0) or (messages[loop.index0 - 1].role != \"tool\") %}\n {{- '<|im_start|>user' }}\n {%- endif %}\n {{- '\\n<tool_response>\\n' }}\n {{- message.content }}\n {{- '\\n</tool_response>' }}\n {%- if loop.last or (messages[loop.index0 + 1].role != \"tool\") %}\n {{- '<|im_end|>\\n' }}\n {%- endif %}\n {%- endif %}\n{%- endfor %}\n{%- if add_generation_prompt %}\n {{- '<|im_start|>assistant\\n' }}\n{%- endif %}\n"
|
| 99 |
+
optim: adamw_torch_fused
|
linalg_zero/demo/app.py
ADDED
|
@@ -0,0 +1,968 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import argparse
|
| 2 |
+
import gc
|
| 3 |
+
import json
|
| 4 |
+
import os
|
| 5 |
+
import threading
|
| 6 |
+
from typing import Any
|
| 7 |
+
|
| 8 |
+
import gradio as gr
|
| 9 |
+
import spaces # Import spaces early to enable ZeroGPU support
|
| 10 |
+
import torch
|
| 11 |
+
from datasets import load_dataset
|
| 12 |
+
from huggingface_hub import snapshot_download
|
| 13 |
+
from linalg_zero.distillation.components.models import DefaultConfig
|
| 14 |
+
from linalg_zero.distillation.data import FunctionInvocationInfo, ThoughtSchema
|
| 15 |
+
from linalg_zero.grpo.verifiers.xml_parser import XMLParser
|
| 16 |
+
from linalg_zero.shared.lib import get_lib, get_tools
|
| 17 |
+
from linalg_zero.shared.system_prompts import (ANSWER_CLOSE, ANSWER_OPEN,
|
| 18 |
+
THINK_CLOSE, THINK_OPEN,
|
| 19 |
+
TOOL_CALL_CLOSE, TOOL_CALL_OPEN,
|
| 20 |
+
TOOL_RESPONSE_CLOSE,
|
| 21 |
+
TOOL_RESPONSE_OPEN,
|
| 22 |
+
get_math_system_prompt)
|
| 23 |
+
from torch.utils._pytree import tree_map
|
| 24 |
+
from transformers import (AutoTokenizer, StoppingCriteria,
|
| 25 |
+
StoppingCriteriaList, TextIteratorStreamer, pipeline)
|
| 26 |
+
|
| 27 |
+
# Global event to signal cancellation from the UI thread to the generation thread
|
| 28 |
+
cancel_event = threading.Event()
|
| 29 |
+
|
| 30 |
+
access_token = os.environ["HF_TOKEN"]
|
| 31 |
+
DEMO_DIR = os.path.dirname(__file__)
|
| 32 |
+
ASSISTANT_AVATAR_PATH = os.path.join(DEMO_DIR, "assets", "linalgzero-avatar.svg")
|
| 33 |
+
|
| 34 |
+
# Optional: Disable GPU visibility if you wish to force CPU usage
|
| 35 |
+
# os.environ["CUDA_VISIBLE_DEVICES"] = " "
|
| 36 |
+
|
| 37 |
+
# ------------------------------
|
| 38 |
+
# Allowed model definitions
|
| 39 |
+
# ------------------------------
|
| 40 |
+
MODELS = {
|
| 41 |
+
"Qwen3-1.7B": {
|
| 42 |
+
"repo_id": "Qwen/Qwen3-1.7B",
|
| 43 |
+
"description": "Dense causal language model with 1.7B parameters.",
|
| 44 |
+
"params_b": 1.7,
|
| 45 |
+
},
|
| 46 |
+
"atomwalk12/LinAlgZero-GRPO": {
|
| 47 |
+
"repo_id": "atomwalk12/LinAlgZero-GRPO-merged",
|
| 48 |
+
"description": "LinAlgZero GRPO fine-tuned model.",
|
| 49 |
+
"params_b": 3.0,
|
| 50 |
+
},
|
| 51 |
+
"atomwalk12/LinalgZero-SFT": {
|
| 52 |
+
"repo_id": "atomwalk12/LinalgZero-SFT",
|
| 53 |
+
"description": "LinAlgZero SFT fine-tuned model.",
|
| 54 |
+
"params_b": 3.0,
|
| 55 |
+
},
|
| 56 |
+
}
|
| 57 |
+
# Global cache for pipelines to avoid re-loading.
|
| 58 |
+
PIPELINES = {}
|
| 59 |
+
MODEL_SNAPSHOT_PATHS: dict[str, str] = {}
|
| 60 |
+
SYSTEM_PROMPT = get_math_system_prompt(include_examples=False)
|
| 61 |
+
TOOL_LIBRARY = get_lib()
|
| 62 |
+
TOOL_SCHEMAS = get_tools()
|
| 63 |
+
PARSER = XMLParser()
|
| 64 |
+
MODEL_MESSAGE_CONFIG = DefaultConfig()
|
| 65 |
+
TRACE_TITLES = {"💭 Thought", "🛠 Tool Call", "📦 Tool Response"}
|
| 66 |
+
KEEP_SPECIAL_TOKENS = {
|
| 67 |
+
ANSWER_OPEN,
|
| 68 |
+
ANSWER_CLOSE,
|
| 69 |
+
THINK_OPEN,
|
| 70 |
+
THINK_CLOSE,
|
| 71 |
+
TOOL_CALL_OPEN,
|
| 72 |
+
TOOL_CALL_CLOSE,
|
| 73 |
+
TOOL_RESPONSE_OPEN,
|
| 74 |
+
TOOL_RESPONSE_CLOSE,
|
| 75 |
+
}
|
| 76 |
+
SIMPLE_EXAMPLE_QUERIES = [
|
| 77 |
+
"What is the rank of matrix A = [[2, 3], [2, -4]]?",
|
| 78 |
+
"Step 1: find the transpose of A = [[1, 2], [3, 4]]. Step 2: find the trace of the result.",
|
| 79 |
+
"Find the determinant of [[3, 1], [2, 5]].",
|
| 80 |
+
"Find the Frobenius norm of [[3, -2], [-1, 5]].",
|
| 81 |
+
"Find the cofactor matrix of [[5, 2], [1, 3]].",
|
| 82 |
+
]
|
| 83 |
+
|
| 84 |
+
|
| 85 |
+
def _parse_launch_args() -> argparse.Namespace:
|
| 86 |
+
parser = argparse.ArgumentParser(add_help=False)
|
| 87 |
+
parser.add_argument(
|
| 88 |
+
"--share",
|
| 89 |
+
action="store_true",
|
| 90 |
+
help="Enable a public Gradio share link.",
|
| 91 |
+
)
|
| 92 |
+
args, _ = parser.parse_known_args()
|
| 93 |
+
return args
|
| 94 |
+
|
| 95 |
+
|
| 96 |
+
def _extract_example_query(example: dict[str, Any]) -> str | None:
|
| 97 |
+
query = example.get("query")
|
| 98 |
+
if isinstance(query, str) and query.strip():
|
| 99 |
+
return query.strip()
|
| 100 |
+
|
| 101 |
+
messages = example.get("messages")
|
| 102 |
+
if isinstance(messages, str):
|
| 103 |
+
try:
|
| 104 |
+
messages = json.loads(messages)
|
| 105 |
+
except Exception:
|
| 106 |
+
return None
|
| 107 |
+
|
| 108 |
+
if isinstance(messages, list):
|
| 109 |
+
for message in reversed(messages):
|
| 110 |
+
if not isinstance(message, dict):
|
| 111 |
+
continue
|
| 112 |
+
if message.get("role") != "user":
|
| 113 |
+
continue
|
| 114 |
+
content = message.get("content")
|
| 115 |
+
if isinstance(content, str) and content.strip():
|
| 116 |
+
return content.strip()
|
| 117 |
+
return None
|
| 118 |
+
|
| 119 |
+
|
| 120 |
+
def _truncate_example_label(query: str, max_chars: int = 96) -> str:
|
| 121 |
+
one_line_query = " ".join(query.split())
|
| 122 |
+
if len(one_line_query) <= max_chars:
|
| 123 |
+
return one_line_query
|
| 124 |
+
return one_line_query[: max_chars - 3] + "..."
|
| 125 |
+
|
| 126 |
+
|
| 127 |
+
def _load_dataset_example_queries(limit: int = 10) -> tuple[list[str], str | None, str | None]:
|
| 128 |
+
dataset_candidates = {
|
| 129 |
+
"atomwalk12/linalgzero-grpo": ["test", "validation", "train"],
|
| 130 |
+
"atomwalk12/linalgzero": ["validation", "test", "train"],
|
| 131 |
+
"atomwalk12/linalgzero-sft": ["test", "validation", "train"],
|
| 132 |
+
}
|
| 133 |
+
|
| 134 |
+
for repo, splits in dataset_candidates.items():
|
| 135 |
+
for split in splits:
|
| 136 |
+
try:
|
| 137 |
+
dataset = load_dataset(repo, split=split, streaming=True)
|
| 138 |
+
except Exception:
|
| 139 |
+
continue
|
| 140 |
+
|
| 141 |
+
queries: list[str] = []
|
| 142 |
+
seen_queries: set[str] = set()
|
| 143 |
+
for example in dataset:
|
| 144 |
+
query = _extract_example_query(example)
|
| 145 |
+
if not query or query in seen_queries:
|
| 146 |
+
continue
|
| 147 |
+
seen_queries.add(query)
|
| 148 |
+
queries.append(query)
|
| 149 |
+
if len(queries) >= limit:
|
| 150 |
+
return queries, repo, split
|
| 151 |
+
|
| 152 |
+
if queries:
|
| 153 |
+
return queries, repo, split
|
| 154 |
+
|
| 155 |
+
return [], None, None
|
| 156 |
+
|
| 157 |
+
|
| 158 |
+
DATASET_EXAMPLE_QUERIES, DATASET_EXAMPLE_REPO, DATASET_EXAMPLE_SPLIT = _load_dataset_example_queries()
|
| 159 |
+
DATASET_EXAMPLE_CHOICES = [(_truncate_example_label(query), query) for query in DATASET_EXAMPLE_QUERIES]
|
| 160 |
+
SIMPLE_EXAMPLE_ROWS = [[query] for query in SIMPLE_EXAMPLE_QUERIES]
|
| 161 |
+
if DATASET_EXAMPLE_REPO is not None and DATASET_EXAMPLE_SPLIT is not None:
|
| 162 |
+
DATASET_EXAMPLE_INFO = f"Loaded from {DATASET_EXAMPLE_REPO} ({DATASET_EXAMPLE_SPLIT})"
|
| 163 |
+
else:
|
| 164 |
+
DATASET_EXAMPLE_INFO = "Dataset examples unavailable"
|
| 165 |
+
|
| 166 |
+
# Match the current GRPO/SFT evaluation configs:
|
| 167 |
+
# - temperature = 0.0
|
| 168 |
+
# - top_p = null (functionally ignored in deterministic mode)
|
| 169 |
+
# - repetition_penalty = 1.0
|
| 170 |
+
# In the actual GRPO eval agent, non-train splits force deterministic decoding
|
| 171 |
+
# (do_sample=False, temperature=0.0, top_p=None), so top_k is effectively unused.
|
| 172 |
+
EVAL_TEMPERATURE = 0.0
|
| 173 |
+
UI_TOP_K_DEFAULT = 40
|
| 174 |
+
UI_TOP_P_DEFAULT = 1.0
|
| 175 |
+
EVAL_REPETITION_PENALTY = 1.0
|
| 176 |
+
DEFAULT_MAX_TOKENS = 1024
|
| 177 |
+
ZERO_GPU_DURATION_SECONDS = 120
|
| 178 |
+
|
| 179 |
+
def prepare_model_artifacts(model_name: str, *, local_files_only: bool = False) -> str:
|
| 180 |
+
cached_path = MODEL_SNAPSHOT_PATHS.get(model_name)
|
| 181 |
+
if cached_path is not None and os.path.exists(cached_path):
|
| 182 |
+
return cached_path
|
| 183 |
+
|
| 184 |
+
repo = MODELS[model_name]["repo_id"]
|
| 185 |
+
model_path = snapshot_download(
|
| 186 |
+
repo_id=repo,
|
| 187 |
+
token=access_token,
|
| 188 |
+
local_files_only=local_files_only,
|
| 189 |
+
)
|
| 190 |
+
MODEL_SNAPSHOT_PATHS[model_name] = model_path
|
| 191 |
+
return model_path
|
| 192 |
+
|
| 193 |
+
|
| 194 |
+
def load_pipeline(model_name):
|
| 195 |
+
"""
|
| 196 |
+
Load and cache a transformers pipeline for text generation.
|
| 197 |
+
Tries bfloat16, falls back to float16 or float32 if unsupported.
|
| 198 |
+
"""
|
| 199 |
+
global PIPELINES
|
| 200 |
+
if model_name in PIPELINES:
|
| 201 |
+
return PIPELINES[model_name]
|
| 202 |
+
model_path = prepare_model_artifacts(model_name, local_files_only=True)
|
| 203 |
+
tokenizer = AutoTokenizer.from_pretrained(
|
| 204 |
+
model_path,
|
| 205 |
+
token=access_token,
|
| 206 |
+
local_files_only=True,
|
| 207 |
+
)
|
| 208 |
+
for dtype in (torch.bfloat16, torch.float16, torch.float32):
|
| 209 |
+
try:
|
| 210 |
+
pipe = pipeline(
|
| 211 |
+
task="text-generation",
|
| 212 |
+
model=model_path,
|
| 213 |
+
tokenizer=tokenizer,
|
| 214 |
+
trust_remote_code=True,
|
| 215 |
+
dtype=dtype, # Use `dtype` instead of deprecated `torch_dtype`
|
| 216 |
+
device_map="auto",
|
| 217 |
+
use_cache=True, # Enable past-key-value caching
|
| 218 |
+
token=access_token,
|
| 219 |
+
local_files_only=True,
|
| 220 |
+
)
|
| 221 |
+
PIPELINES[model_name] = pipe
|
| 222 |
+
return pipe
|
| 223 |
+
except Exception:
|
| 224 |
+
continue
|
| 225 |
+
# Final fallback
|
| 226 |
+
pipe = pipeline(
|
| 227 |
+
task="text-generation",
|
| 228 |
+
model=model_path,
|
| 229 |
+
tokenizer=tokenizer,
|
| 230 |
+
trust_remote_code=True,
|
| 231 |
+
device_map="auto",
|
| 232 |
+
use_cache=True,
|
| 233 |
+
local_files_only=True,
|
| 234 |
+
)
|
| 235 |
+
PIPELINES[model_name] = pipe
|
| 236 |
+
return pipe
|
| 237 |
+
|
| 238 |
+
def _extract_visible_title(message: dict[str, Any]) -> str | None:
|
| 239 |
+
metadata = message.get("metadata")
|
| 240 |
+
if isinstance(metadata, dict):
|
| 241 |
+
title = metadata.get("title")
|
| 242 |
+
if isinstance(title, str):
|
| 243 |
+
return title
|
| 244 |
+
return None
|
| 245 |
+
|
| 246 |
+
|
| 247 |
+
def _build_llm_messages(chat_history: list[dict[str, Any]] | None, user_msg: str) -> list[dict[str, Any]]:
|
| 248 |
+
messages = [{"role": "system", "content": SYSTEM_PROMPT}]
|
| 249 |
+
for msg in chat_history or []:
|
| 250 |
+
role = msg.get("role")
|
| 251 |
+
content = msg.get("content")
|
| 252 |
+
if role not in {"user", "assistant"} or not isinstance(content, str):
|
| 253 |
+
continue
|
| 254 |
+
if role == "assistant" and _extract_visible_title(msg) in TRACE_TITLES:
|
| 255 |
+
continue
|
| 256 |
+
messages.append({"role": role, "content": content})
|
| 257 |
+
messages.append({"role": "user", "content": user_msg})
|
| 258 |
+
return messages
|
| 259 |
+
|
| 260 |
+
|
| 261 |
+
def _render_prompt(messages: list[dict[str, Any]], tokenizer: AutoTokenizer) -> str:
|
| 262 |
+
if hasattr(tokenizer, "chat_template") and tokenizer.chat_template:
|
| 263 |
+
kwargs: dict[str, Any] = {
|
| 264 |
+
"tokenize": False,
|
| 265 |
+
"add_generation_prompt": True,
|
| 266 |
+
"tools": TOOL_SCHEMAS,
|
| 267 |
+
}
|
| 268 |
+
try:
|
| 269 |
+
return tokenizer.apply_chat_template(messages, enable_thinking=True, **kwargs)
|
| 270 |
+
except TypeError:
|
| 271 |
+
return tokenizer.apply_chat_template(messages, **kwargs)
|
| 272 |
+
|
| 273 |
+
prompt = messages[0]["content"].strip() + "\n"
|
| 274 |
+
for msg in messages[1:]:
|
| 275 |
+
if msg["role"] == "user":
|
| 276 |
+
prompt += "User: " + str(msg["content"]).strip() + "\n"
|
| 277 |
+
elif msg["role"] == "assistant":
|
| 278 |
+
prompt += "Assistant: " + str(msg.get("content", "")).strip() + "\n"
|
| 279 |
+
for tool_call in msg.get("tool_calls", []):
|
| 280 |
+
function_info = tool_call.get("function", {})
|
| 281 |
+
prompt += (
|
| 282 |
+
f"{TOOL_CALL_OPEN}"
|
| 283 |
+
+ json.dumps({
|
| 284 |
+
"name": function_info.get("name"),
|
| 285 |
+
"arguments": function_info.get("arguments"),
|
| 286 |
+
})
|
| 287 |
+
+ f"{TOOL_CALL_CLOSE}\n"
|
| 288 |
+
)
|
| 289 |
+
elif msg["role"] == "tool":
|
| 290 |
+
prompt += f"User: {TOOL_RESPONSE_OPEN}{msg['content']}{TOOL_RESPONSE_CLOSE}\n"
|
| 291 |
+
if not prompt.strip().endswith("Assistant:"):
|
| 292 |
+
prompt += "Assistant: "
|
| 293 |
+
return prompt
|
| 294 |
+
|
| 295 |
+
|
| 296 |
+
class StopOnSequences(StoppingCriteria):
|
| 297 |
+
def __init__(self, stop_token_sequences: list[list[int]]) -> None:
|
| 298 |
+
super().__init__()
|
| 299 |
+
self.stop_token_sequences = [seq for seq in stop_token_sequences if seq]
|
| 300 |
+
|
| 301 |
+
def __call__(self, input_ids: torch.LongTensor, scores: torch.FloatTensor, **kwargs: Any) -> bool:
|
| 302 |
+
if cancel_event.is_set():
|
| 303 |
+
return True
|
| 304 |
+
|
| 305 |
+
current_tokens = input_ids[0].tolist()
|
| 306 |
+
for seq in self.stop_token_sequences:
|
| 307 |
+
if len(current_tokens) >= len(seq) and current_tokens[-len(seq):] == seq:
|
| 308 |
+
return True
|
| 309 |
+
return False
|
| 310 |
+
|
| 311 |
+
|
| 312 |
+
def _get_stop_token_sequences(tokenizer: AutoTokenizer) -> list[list[int]]:
|
| 313 |
+
stop_strings = [TOOL_CALL_CLOSE, ANSWER_CLOSE, TOOL_RESPONSE_OPEN, TOOL_RESPONSE_CLOSE]
|
| 314 |
+
return [tokenizer.encode(stop_text, add_special_tokens=False) for stop_text in stop_strings]
|
| 315 |
+
|
| 316 |
+
|
| 317 |
+
def _decode_generated_tokens(tokenizer: AutoTokenizer, generated_tokens: torch.Tensor) -> str:
|
| 318 |
+
raw_output = tokenizer.decode(generated_tokens, skip_special_tokens=False)
|
| 319 |
+
cleaned_output = raw_output
|
| 320 |
+
for special_token in tokenizer.all_special_tokens:
|
| 321 |
+
if special_token not in KEEP_SPECIAL_TOKENS:
|
| 322 |
+
cleaned_output = cleaned_output.replace(special_token, "")
|
| 323 |
+
return cleaned_output
|
| 324 |
+
|
| 325 |
+
|
| 326 |
+
def _clean_decoded_text(tokenizer: AutoTokenizer, text: str) -> str:
|
| 327 |
+
cleaned_text = text
|
| 328 |
+
for special_token in tokenizer.all_special_tokens:
|
| 329 |
+
if special_token not in KEEP_SPECIAL_TOKENS:
|
| 330 |
+
cleaned_text = cleaned_text.replace(special_token, "")
|
| 331 |
+
return cleaned_text
|
| 332 |
+
|
| 333 |
+
|
| 334 |
+
def _extract_partial_tag_content(message: str, tag: str) -> str | None:
|
| 335 |
+
open_tag = f"<{tag}>"
|
| 336 |
+
close_tag = f"</{tag}>"
|
| 337 |
+
start_idx = message.rfind(open_tag)
|
| 338 |
+
if start_idx == -1:
|
| 339 |
+
return None
|
| 340 |
+
content = message[start_idx + len(open_tag):]
|
| 341 |
+
end_idx = content.find(close_tag)
|
| 342 |
+
if end_idx != -1:
|
| 343 |
+
content = content[:end_idx]
|
| 344 |
+
return content.strip()
|
| 345 |
+
|
| 346 |
+
|
| 347 |
+
def _extract_live_sections(message: str) -> dict[str, str | None]:
|
| 348 |
+
return {
|
| 349 |
+
"thought": _extract_partial_tag_content(message, "think"),
|
| 350 |
+
"tool_call": _extract_partial_tag_content(message, "tool_call"),
|
| 351 |
+
"answer": _extract_partial_tag_content(message, "answer"),
|
| 352 |
+
}
|
| 353 |
+
|
| 354 |
+
|
| 355 |
+
def _set_live_message(
|
| 356 |
+
history: list[dict[str, Any]],
|
| 357 |
+
live_indices: dict[str, int],
|
| 358 |
+
key: str,
|
| 359 |
+
content: str | None,
|
| 360 |
+
*,
|
| 361 |
+
title: str | None = None,
|
| 362 |
+
code_block_lang: str | None = None,
|
| 363 |
+
) -> None:
|
| 364 |
+
if not content:
|
| 365 |
+
return
|
| 366 |
+
|
| 367 |
+
rendered_content = content
|
| 368 |
+
if code_block_lang is not None:
|
| 369 |
+
rendered_content = f"```{code_block_lang}\n{content}\n```"
|
| 370 |
+
|
| 371 |
+
idx = live_indices.get(key)
|
| 372 |
+
if idx is None:
|
| 373 |
+
message: dict[str, Any] = {"role": "assistant", "content": rendered_content}
|
| 374 |
+
if title is not None:
|
| 375 |
+
message["metadata"] = {"title": title}
|
| 376 |
+
history.append(message)
|
| 377 |
+
live_indices[key] = len(history) - 1
|
| 378 |
+
return
|
| 379 |
+
|
| 380 |
+
history[idx]["content"] = rendered_content
|
| 381 |
+
|
| 382 |
+
|
| 383 |
+
def _build_debug_info() -> str:
|
| 384 |
+
tool_lines = "\n".join(f"- `{tool_name}`" for tool_name in sorted(TOOL_LIBRARY.keys()))
|
| 385 |
+
return f"### Available Tools\n{tool_lines}\n\n### System Prompt\n```text\n{SYSTEM_PROMPT}\n```"
|
| 386 |
+
|
| 387 |
+
|
| 388 |
+
def _stream_assistant_turn(
|
| 389 |
+
pipe: Any,
|
| 390 |
+
messages: list[dict[str, Any]],
|
| 391 |
+
max_tokens: int,
|
| 392 |
+
temperature: float,
|
| 393 |
+
top_k: int,
|
| 394 |
+
top_p: float,
|
| 395 |
+
repeat_penalty: float,
|
| 396 |
+
):
|
| 397 |
+
prompt = _render_prompt(messages, pipe.tokenizer)
|
| 398 |
+
inputs = pipe.tokenizer(
|
| 399 |
+
prompt,
|
| 400 |
+
return_tensors="pt",
|
| 401 |
+
truncation=True,
|
| 402 |
+
padding=bool(getattr(pipe.tokenizer, "pad_token_id", None)),
|
| 403 |
+
)
|
| 404 |
+
device = getattr(pipe, "device", None) or pipe.model.device
|
| 405 |
+
inputs = tree_map(lambda x: x.to(device) if hasattr(x, "to") else x, inputs)
|
| 406 |
+
|
| 407 |
+
streamer = TextIteratorStreamer(
|
| 408 |
+
pipe.tokenizer,
|
| 409 |
+
skip_prompt=True,
|
| 410 |
+
skip_special_tokens=False,
|
| 411 |
+
)
|
| 412 |
+
|
| 413 |
+
generation_kwargs: dict[str, Any] = {
|
| 414 |
+
"input_ids": inputs["input_ids"],
|
| 415 |
+
"attention_mask": inputs["attention_mask"],
|
| 416 |
+
"max_new_tokens": int(max_tokens),
|
| 417 |
+
"repetition_penalty": float(repeat_penalty),
|
| 418 |
+
"use_cache": True,
|
| 419 |
+
"pad_token_id": getattr(pipe.tokenizer, "pad_token_id", pipe.tokenizer.eos_token_id),
|
| 420 |
+
"stopping_criteria": StoppingCriteriaList([StopOnSequences(_get_stop_token_sequences(pipe.tokenizer))]),
|
| 421 |
+
"streamer": streamer,
|
| 422 |
+
}
|
| 423 |
+
eos_token_id = getattr(pipe.tokenizer, "eos_token_id", None)
|
| 424 |
+
if eos_token_id is not None:
|
| 425 |
+
generation_kwargs["eos_token_id"] = eos_token_id
|
| 426 |
+
if temperature > 0:
|
| 427 |
+
generation_kwargs.update({
|
| 428 |
+
"do_sample": True,
|
| 429 |
+
"temperature": float(temperature),
|
| 430 |
+
"top_k": int(top_k),
|
| 431 |
+
"top_p": float(top_p),
|
| 432 |
+
})
|
| 433 |
+
else:
|
| 434 |
+
generation_kwargs["do_sample"] = False
|
| 435 |
+
|
| 436 |
+
error_holder: dict[str, Exception] = {}
|
| 437 |
+
|
| 438 |
+
def _run_generation() -> None:
|
| 439 |
+
try:
|
| 440 |
+
with torch.inference_mode():
|
| 441 |
+
pipe.model.generate(**generation_kwargs)
|
| 442 |
+
except Exception as exc:
|
| 443 |
+
error_holder["error"] = exc
|
| 444 |
+
finally:
|
| 445 |
+
streamer.end()
|
| 446 |
+
|
| 447 |
+
generation_thread = threading.Thread(target=_run_generation, daemon=True)
|
| 448 |
+
generation_thread.start()
|
| 449 |
+
|
| 450 |
+
raw_output = ""
|
| 451 |
+
yield raw_output, prompt
|
| 452 |
+
for chunk in streamer:
|
| 453 |
+
raw_output = _clean_decoded_text(pipe.tokenizer, raw_output + chunk)
|
| 454 |
+
yield raw_output, prompt
|
| 455 |
+
|
| 456 |
+
generation_thread.join()
|
| 457 |
+
if "error" in error_holder:
|
| 458 |
+
raise error_holder["error"]
|
| 459 |
+
|
| 460 |
+
|
| 461 |
+
def _parse_assistant_output(raw_output: str, context: list[dict[str, Any]]) -> tuple[ThoughtSchema | None, dict[str, Any]]:
|
| 462 |
+
analysis = PARSER.analyze_message_in_context(
|
| 463 |
+
context,
|
| 464 |
+
message=raw_output,
|
| 465 |
+
tool_names=list(TOOL_LIBRARY.keys()),
|
| 466 |
+
)
|
| 467 |
+
if not bool(analysis["is_valid_think_then_tool_or_answer"]):
|
| 468 |
+
return None, analysis
|
| 469 |
+
if analysis["has_answer"] and not bool(analysis["answer_policy_valid"]):
|
| 470 |
+
return None, analysis
|
| 471 |
+
|
| 472 |
+
tool_call: FunctionInvocationInfo | None = None
|
| 473 |
+
tool_info = analysis["tool"]
|
| 474 |
+
if analysis["has_tool_call"]:
|
| 475 |
+
if not bool(tool_info["json_valid"]):
|
| 476 |
+
return None, analysis
|
| 477 |
+
tool_call = FunctionInvocationInfo(
|
| 478 |
+
name=str(tool_info["name"]),
|
| 479 |
+
arguments=dict(tool_info["arguments"]),
|
| 480 |
+
)
|
| 481 |
+
|
| 482 |
+
return ThoughtSchema(
|
| 483 |
+
thought=analysis["thought"] or "",
|
| 484 |
+
tool_call=tool_call,
|
| 485 |
+
final_answer=analysis["answer"],
|
| 486 |
+
completed=analysis["answer"] is not None,
|
| 487 |
+
), analysis
|
| 488 |
+
|
| 489 |
+
|
| 490 |
+
def _execute_tool_call(message: ThoughtSchema) -> dict[str, str]:
|
| 491 |
+
if message.tool_call is None:
|
| 492 |
+
raise ValueError("Tool call is required")
|
| 493 |
+
|
| 494 |
+
name = message.tool_call.name
|
| 495 |
+
arguments = message.tool_call.arguments
|
| 496 |
+
try:
|
| 497 |
+
if name not in TOOL_LIBRARY:
|
| 498 |
+
return {
|
| 499 |
+
"function_name": name,
|
| 500 |
+
"execution_result": f"ERROR: Function '{name}' not found in library",
|
| 501 |
+
}
|
| 502 |
+
result = TOOL_LIBRARY[name](**arguments)
|
| 503 |
+
return {"function_name": name, "execution_result": str(result)}
|
| 504 |
+
except Exception as exc:
|
| 505 |
+
return {
|
| 506 |
+
"function_name": name,
|
| 507 |
+
"execution_result": f"ERROR: {type(exc).__name__}: {exc}",
|
| 508 |
+
}
|
| 509 |
+
|
| 510 |
+
|
| 511 |
+
# Keep a single GPU allocation for the whole multi-turn solve, with a fixed
|
| 512 |
+
# budget that is simpler and more predictable than a heuristic estimate.
|
| 513 |
+
@spaces.GPU(duration=ZERO_GPU_DURATION_SECONDS)
|
| 514 |
+
def chat_response(
|
| 515 |
+
user_msg,
|
| 516 |
+
chat_history,
|
| 517 |
+
show_tool_trace,
|
| 518 |
+
enable_streaming,
|
| 519 |
+
max_tool_turns,
|
| 520 |
+
model_name,
|
| 521 |
+
max_tokens,
|
| 522 |
+
temperature,
|
| 523 |
+
top_k,
|
| 524 |
+
top_p,
|
| 525 |
+
repeat_penalty,
|
| 526 |
+
):
|
| 527 |
+
"""
|
| 528 |
+
Generates responses by iteratively calling linear algebra tools until a final answer is produced.
|
| 529 |
+
"""
|
| 530 |
+
cancel_event.clear()
|
| 531 |
+
|
| 532 |
+
history = list(chat_history or [])
|
| 533 |
+
history.append({"role": "user", "content": user_msg})
|
| 534 |
+
debug_info = _build_debug_info()
|
| 535 |
+
|
| 536 |
+
try:
|
| 537 |
+
pipe = load_pipeline(model_name)
|
| 538 |
+
context = _build_llm_messages(chat_history, user_msg)
|
| 539 |
+
|
| 540 |
+
yield history, debug_info
|
| 541 |
+
|
| 542 |
+
for step_idx in range(int(max_tool_turns)):
|
| 543 |
+
raw_output = ""
|
| 544 |
+
live_indices: dict[str, int] = {}
|
| 545 |
+
|
| 546 |
+
turn_stream = _stream_assistant_turn(
|
| 547 |
+
pipe=pipe,
|
| 548 |
+
messages=context,
|
| 549 |
+
max_tokens=max_tokens,
|
| 550 |
+
temperature=temperature,
|
| 551 |
+
top_k=top_k,
|
| 552 |
+
top_p=top_p,
|
| 553 |
+
repeat_penalty=repeat_penalty,
|
| 554 |
+
)
|
| 555 |
+
|
| 556 |
+
if enable_streaming:
|
| 557 |
+
for partial_output, _current_prompt in turn_stream:
|
| 558 |
+
raw_output = partial_output
|
| 559 |
+
|
| 560 |
+
live_sections = _extract_live_sections(raw_output)
|
| 561 |
+
if show_tool_trace:
|
| 562 |
+
_set_live_message(
|
| 563 |
+
history,
|
| 564 |
+
live_indices,
|
| 565 |
+
"thought",
|
| 566 |
+
live_sections["thought"],
|
| 567 |
+
title="💭 Thought",
|
| 568 |
+
)
|
| 569 |
+
_set_live_message(
|
| 570 |
+
history,
|
| 571 |
+
live_indices,
|
| 572 |
+
"tool_call",
|
| 573 |
+
live_sections["tool_call"],
|
| 574 |
+
title="🛠 Tool Call",
|
| 575 |
+
code_block_lang="json",
|
| 576 |
+
)
|
| 577 |
+
_set_live_message(
|
| 578 |
+
history,
|
| 579 |
+
live_indices,
|
| 580 |
+
"answer",
|
| 581 |
+
live_sections["answer"],
|
| 582 |
+
)
|
| 583 |
+
|
| 584 |
+
yield history, debug_info
|
| 585 |
+
else:
|
| 586 |
+
for partial_output, _current_prompt in turn_stream:
|
| 587 |
+
raw_output = partial_output
|
| 588 |
+
|
| 589 |
+
if cancel_event.is_set():
|
| 590 |
+
history.append({"role": "assistant", "content": "[Generation Canceled]"})
|
| 591 |
+
yield history, debug_info
|
| 592 |
+
break
|
| 593 |
+
|
| 594 |
+
parsed, analysis = _parse_assistant_output(raw_output, context)
|
| 595 |
+
if parsed is None:
|
| 596 |
+
history.append({
|
| 597 |
+
"role": "assistant",
|
| 598 |
+
"content": "I couldn't produce a valid tool call or final answer for this problem.",
|
| 599 |
+
})
|
| 600 |
+
yield history, debug_info
|
| 601 |
+
break
|
| 602 |
+
|
| 603 |
+
assistant_message = MODEL_MESSAGE_CONFIG.format_assistant_message(parsed)
|
| 604 |
+
if assistant_message is None:
|
| 605 |
+
history.append({"role": "assistant", "content": "The model returned an empty action."})
|
| 606 |
+
yield history, debug_info
|
| 607 |
+
break
|
| 608 |
+
|
| 609 |
+
context.append(assistant_message)
|
| 610 |
+
|
| 611 |
+
if show_tool_trace and parsed.thought:
|
| 612 |
+
_set_live_message(
|
| 613 |
+
history,
|
| 614 |
+
live_indices,
|
| 615 |
+
"thought",
|
| 616 |
+
parsed.thought,
|
| 617 |
+
title="💭 Thought",
|
| 618 |
+
)
|
| 619 |
+
|
| 620 |
+
if parsed.tool_call is not None:
|
| 621 |
+
if show_tool_trace:
|
| 622 |
+
_set_live_message(
|
| 623 |
+
history,
|
| 624 |
+
live_indices,
|
| 625 |
+
"tool_call",
|
| 626 |
+
json.dumps(
|
| 627 |
+
{
|
| 628 |
+
"name": parsed.tool_call.name,
|
| 629 |
+
"arguments": parsed.tool_call.arguments,
|
| 630 |
+
},
|
| 631 |
+
indent=2,
|
| 632 |
+
),
|
| 633 |
+
title="🛠 Tool Call",
|
| 634 |
+
code_block_lang="json",
|
| 635 |
+
)
|
| 636 |
+
|
| 637 |
+
tool_result = _execute_tool_call(parsed)
|
| 638 |
+
context.append(MODEL_MESSAGE_CONFIG.create_tool_message(context, tool_result))
|
| 639 |
+
|
| 640 |
+
if show_tool_trace:
|
| 641 |
+
history.append({
|
| 642 |
+
"role": "assistant",
|
| 643 |
+
"content": tool_result["execution_result"],
|
| 644 |
+
"metadata": {"title": "📦 Tool Response"},
|
| 645 |
+
})
|
| 646 |
+
|
| 647 |
+
yield history, debug_info
|
| 648 |
+
continue
|
| 649 |
+
|
| 650 |
+
if parsed.final_answer is not None:
|
| 651 |
+
answer_idx = live_indices.get("answer")
|
| 652 |
+
if answer_idx is not None:
|
| 653 |
+
history[answer_idx]["content"] = parsed.final_answer
|
| 654 |
+
else:
|
| 655 |
+
history.append({"role": "assistant", "content": parsed.final_answer})
|
| 656 |
+
yield history, debug_info
|
| 657 |
+
break
|
| 658 |
+
else:
|
| 659 |
+
history.append({
|
| 660 |
+
"role": "assistant",
|
| 661 |
+
"content": "I hit the tool-turn limit before finishing the problem.",
|
| 662 |
+
})
|
| 663 |
+
yield history, debug_info
|
| 664 |
+
except GeneratorExit:
|
| 665 |
+
print("Chat response cancelled.")
|
| 666 |
+
return
|
| 667 |
+
except Exception as e:
|
| 668 |
+
history.append({"role": "assistant", "content": f"Error: {e}"})
|
| 669 |
+
yield history, debug_info
|
| 670 |
+
finally:
|
| 671 |
+
gc.collect()
|
| 672 |
+
|
| 673 |
+
|
| 674 |
+
demo_theme = gr.themes.Soft(
|
| 675 |
+
primary_hue="indigo",
|
| 676 |
+
secondary_hue="purple",
|
| 677 |
+
neutral_hue="slate",
|
| 678 |
+
radius_size="lg",
|
| 679 |
+
font=[gr.themes.GoogleFont("Inter"), "Arial", "sans-serif"]
|
| 680 |
+
)
|
| 681 |
+
|
| 682 |
+
demo_css = """
|
| 683 |
+
.chatbot { border-radius: 12px; box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1); }
|
| 684 |
+
button.primary { font-weight: 600; }
|
| 685 |
+
.gradio-accordion { margin-bottom: 12px; }
|
| 686 |
+
"""
|
| 687 |
+
|
| 688 |
+
with gr.Blocks(title="LLM Inference with ZeroGPU") as demo:
|
| 689 |
+
# Header
|
| 690 |
+
gr.Markdown("""
|
| 691 |
+
# 🧠 LinAlgZero Demo
|
| 692 |
+
### Multi-turn linear algebra solving with tool calling
|
| 693 |
+
""")
|
| 694 |
+
|
| 695 |
+
with gr.Row():
|
| 696 |
+
# Left Panel - Configuration
|
| 697 |
+
with gr.Column(scale=3):
|
| 698 |
+
# Core Settings (Always Visible)
|
| 699 |
+
with gr.Group():
|
| 700 |
+
gr.Markdown("### ⚙️ Core Settings")
|
| 701 |
+
model_dd = gr.Dropdown(
|
| 702 |
+
label="🤖 Model",
|
| 703 |
+
choices=list(MODELS.keys()),
|
| 704 |
+
value="atomwalk12/LinAlgZero-GRPO",
|
| 705 |
+
info="Select the language model to use"
|
| 706 |
+
)
|
| 707 |
+
trace_chk = gr.Checkbox(
|
| 708 |
+
label="🔍 Show Tool Trace",
|
| 709 |
+
value=True,
|
| 710 |
+
info="Show thoughts, tool calls, and tool responses in the chat"
|
| 711 |
+
)
|
| 712 |
+
stream_chk = gr.Checkbox(
|
| 713 |
+
label="⚡ Enable Live Streaming",
|
| 714 |
+
value=True,
|
| 715 |
+
info="Stream partial model output while each tool-calling turn is being generated"
|
| 716 |
+
)
|
| 717 |
+
max_tool_turns = gr.Slider(
|
| 718 |
+
1, 12, value=6, step=1,
|
| 719 |
+
label="Max Tool Turns",
|
| 720 |
+
info="Maximum number of tool-calling turns before stopping"
|
| 721 |
+
)
|
| 722 |
+
|
| 723 |
+
example_dropdown = gr.Dropdown(
|
| 724 |
+
label="📚 Dataset Example",
|
| 725 |
+
choices=DATASET_EXAMPLE_CHOICES,
|
| 726 |
+
value=None,
|
| 727 |
+
info=DATASET_EXAMPLE_INFO,
|
| 728 |
+
allow_custom_value=False,
|
| 729 |
+
)
|
| 730 |
+
|
| 731 |
+
# Advanced Settings (Collapsible)
|
| 732 |
+
with gr.Accordion("🎛️ Advanced Generation Parameters", open=True):
|
| 733 |
+
max_tok = gr.Slider(
|
| 734 |
+
64, 16384, value=DEFAULT_MAX_TOKENS, step=32,
|
| 735 |
+
label="Max Tokens",
|
| 736 |
+
info="Maximum length of generated response"
|
| 737 |
+
)
|
| 738 |
+
temp = gr.Slider(
|
| 739 |
+
0.0, 2.0, value=EVAL_TEMPERATURE, step=0.1,
|
| 740 |
+
label="Temperature",
|
| 741 |
+
info="Higher = more creative, Lower = more focused"
|
| 742 |
+
)
|
| 743 |
+
with gr.Row():
|
| 744 |
+
k = gr.Slider(
|
| 745 |
+
1, 100, value=UI_TOP_K_DEFAULT, step=1,
|
| 746 |
+
label="Top-K",
|
| 747 |
+
info="Number of top tokens to consider",
|
| 748 |
+
interactive=False,
|
| 749 |
+
)
|
| 750 |
+
p = gr.Slider(
|
| 751 |
+
0.1, 1.0, value=UI_TOP_P_DEFAULT, step=0.05,
|
| 752 |
+
label="Top-P",
|
| 753 |
+
info="Nucleus sampling threshold",
|
| 754 |
+
interactive=False,
|
| 755 |
+
)
|
| 756 |
+
rp = gr.Slider(
|
| 757 |
+
1.0, 2.0, value=EVAL_REPETITION_PENALTY, step=0.1,
|
| 758 |
+
label="Repetition Penalty",
|
| 759 |
+
info="Penalize repeated tokens"
|
| 760 |
+
)
|
| 761 |
+
|
| 762 |
+
# Actions
|
| 763 |
+
with gr.Column():
|
| 764 |
+
reset_params_btn = gr.Button("↺ Restore Default Params", variant="secondary")
|
| 765 |
+
clr = gr.Button("🗑️ Clear Chat", variant="secondary", scale=1)
|
| 766 |
+
|
| 767 |
+
# Right Panel - Chat Interface
|
| 768 |
+
with gr.Column(scale=7):
|
| 769 |
+
chat = gr.Chatbot(
|
| 770 |
+
height=1200,
|
| 771 |
+
label="💬 Conversation",
|
| 772 |
+
buttons=["copy"],
|
| 773 |
+
avatar_images=(None, ASSISTANT_AVATAR_PATH),
|
| 774 |
+
layout="bubble"
|
| 775 |
+
)
|
| 776 |
+
|
| 777 |
+
# Input Area
|
| 778 |
+
with gr.Row():
|
| 779 |
+
txt = gr.Textbox(
|
| 780 |
+
placeholder="💭 Type your message here... (Press Enter to send)",
|
| 781 |
+
scale=9,
|
| 782 |
+
container=False,
|
| 783 |
+
show_label=False,
|
| 784 |
+
lines=1,
|
| 785 |
+
max_lines=5
|
| 786 |
+
)
|
| 787 |
+
with gr.Column(scale=1, min_width=120):
|
| 788 |
+
submit_btn = gr.Button("📤 Send", variant="primary", size="lg", interactive=False)
|
| 789 |
+
cancel_btn = gr.Button("⏹️ Stop", variant="stop", visible=False, size="lg")
|
| 790 |
+
|
| 791 |
+
# Example Prompts
|
| 792 |
+
gr.Examples(
|
| 793 |
+
examples=SIMPLE_EXAMPLE_ROWS,
|
| 794 |
+
inputs=txt,
|
| 795 |
+
label="💡 Example Prompts"
|
| 796 |
+
)
|
| 797 |
+
|
| 798 |
+
# Debug/Status Info (Collapsible)
|
| 799 |
+
with gr.Accordion("🔍 Debug Info", open=False):
|
| 800 |
+
dbg = gr.Markdown(value=_build_debug_info())
|
| 801 |
+
|
| 802 |
+
# --- Event Listeners ---
|
| 803 |
+
|
| 804 |
+
# Group all inputs for cleaner event handling
|
| 805 |
+
chat_inputs = [txt, chat, trace_chk, stream_chk, max_tool_turns, model_dd, max_tok, temp, k, p, rp]
|
| 806 |
+
# Group all UI components that can be updated.
|
| 807 |
+
ui_components = [chat, dbg, txt, submit_btn, cancel_btn]
|
| 808 |
+
|
| 809 |
+
def submit_and_manage_ui(user_msg, chat_history, *args):
|
| 810 |
+
"""
|
| 811 |
+
Orchestrator function that manages UI state and calls the backend chat function.
|
| 812 |
+
It uses a try...finally block to ensure the UI is always reset.
|
| 813 |
+
"""
|
| 814 |
+
if not user_msg.strip():
|
| 815 |
+
# If the message is empty, do nothing.
|
| 816 |
+
# We yield an empty dict to avoid any state changes.
|
| 817 |
+
yield {}
|
| 818 |
+
return
|
| 819 |
+
|
| 820 |
+
# 1. Update UI to "generating" state.
|
| 821 |
+
# Crucially, we do NOT update the `chat` component here, as the backend
|
| 822 |
+
# will provide the correctly formatted history in the first response chunk.
|
| 823 |
+
yield {
|
| 824 |
+
txt: gr.update(value="", interactive=False),
|
| 825 |
+
submit_btn: gr.update(interactive=False),
|
| 826 |
+
cancel_btn: gr.update(visible=True),
|
| 827 |
+
}
|
| 828 |
+
|
| 829 |
+
cancelled = False
|
| 830 |
+
try:
|
| 831 |
+
model_name = args[3]
|
| 832 |
+
prepare_model_artifacts(model_name)
|
| 833 |
+
|
| 834 |
+
# 2. Call the backend and stream updates
|
| 835 |
+
backend_args = [user_msg, chat_history] + list(args)
|
| 836 |
+
for response_chunk in chat_response(*backend_args):
|
| 837 |
+
yield {
|
| 838 |
+
chat: response_chunk[0],
|
| 839 |
+
dbg: response_chunk[1],
|
| 840 |
+
}
|
| 841 |
+
except GeneratorExit:
|
| 842 |
+
# Mark as cancelled and re-raise to prevent "generator ignored GeneratorExit"
|
| 843 |
+
cancelled = True
|
| 844 |
+
print("Generation cancelled by user.")
|
| 845 |
+
raise
|
| 846 |
+
except Exception as e:
|
| 847 |
+
print(f"An error occurred during generation: {e}")
|
| 848 |
+
# If an error happens, add it to the chat history to inform the user.
|
| 849 |
+
error_history = (chat_history or []) + [
|
| 850 |
+
{'role': 'user', 'content': user_msg},
|
| 851 |
+
{'role': 'assistant', 'content': f"**An error occurred:** {str(e)}"}
|
| 852 |
+
]
|
| 853 |
+
yield {chat: error_history}
|
| 854 |
+
finally:
|
| 855 |
+
# Only reset UI if not cancelled (to avoid "generator ignored GeneratorExit")
|
| 856 |
+
if not cancelled:
|
| 857 |
+
print("Resetting UI state.")
|
| 858 |
+
yield {
|
| 859 |
+
txt: gr.update(interactive=True),
|
| 860 |
+
submit_btn: gr.update(interactive=False),
|
| 861 |
+
cancel_btn: gr.update(visible=False),
|
| 862 |
+
}
|
| 863 |
+
|
| 864 |
+
def set_cancel_flag():
|
| 865 |
+
"""Called by the cancel button, sets the global event."""
|
| 866 |
+
cancel_event.set()
|
| 867 |
+
print("Cancellation signal sent.")
|
| 868 |
+
|
| 869 |
+
def reset_ui_after_cancel():
|
| 870 |
+
"""Reset UI components after cancellation."""
|
| 871 |
+
cancel_event.clear() # Clear the flag for next generation
|
| 872 |
+
print("UI reset after cancellation.")
|
| 873 |
+
return {
|
| 874 |
+
txt: gr.update(interactive=True),
|
| 875 |
+
submit_btn: gr.update(interactive=False),
|
| 876 |
+
cancel_btn: gr.update(visible=False),
|
| 877 |
+
}
|
| 878 |
+
|
| 879 |
+
def clear_chat_state():
|
| 880 |
+
"""Clear the chat and restore default UI state."""
|
| 881 |
+
cancel_event.clear()
|
| 882 |
+
return {
|
| 883 |
+
chat: [],
|
| 884 |
+
dbg: _build_debug_info(),
|
| 885 |
+
txt: "",
|
| 886 |
+
submit_btn: gr.update(interactive=False),
|
| 887 |
+
cancel_btn: gr.update(visible=False),
|
| 888 |
+
}
|
| 889 |
+
|
| 890 |
+
def set_example_text(example_query: str | None) -> str:
|
| 891 |
+
return example_query or ""
|
| 892 |
+
|
| 893 |
+
def apply_example_selection(evt: gr.SelectData):
|
| 894 |
+
if evt is None or not evt.selected:
|
| 895 |
+
return gr.skip(), gr.skip()
|
| 896 |
+
example_text = set_example_text(evt.value)
|
| 897 |
+
return gr.update(value=example_text), toggle_submit_button(example_text)
|
| 898 |
+
|
| 899 |
+
def toggle_submit_button(text: str | None):
|
| 900 |
+
return gr.update(interactive=bool(text and text.strip()))
|
| 901 |
+
|
| 902 |
+
def toggle_sampling_controls(temperature: float):
|
| 903 |
+
sampling_enabled = float(temperature) > 0.0
|
| 904 |
+
return [
|
| 905 |
+
gr.update(interactive=sampling_enabled),
|
| 906 |
+
gr.update(interactive=sampling_enabled),
|
| 907 |
+
]
|
| 908 |
+
|
| 909 |
+
def restore_generation_defaults():
|
| 910 |
+
return (
|
| 911 |
+
gr.update(value=DEFAULT_MAX_TOKENS),
|
| 912 |
+
gr.update(value=EVAL_TEMPERATURE),
|
| 913 |
+
gr.update(value=UI_TOP_K_DEFAULT, interactive=False),
|
| 914 |
+
gr.update(value=UI_TOP_P_DEFAULT, interactive=False),
|
| 915 |
+
gr.update(value=EVAL_REPETITION_PENALTY),
|
| 916 |
+
)
|
| 917 |
+
|
| 918 |
+
# Event for submitting text via Enter key or Submit button
|
| 919 |
+
submit_event = txt.submit(
|
| 920 |
+
fn=submit_and_manage_ui,
|
| 921 |
+
inputs=chat_inputs,
|
| 922 |
+
outputs=ui_components,
|
| 923 |
+
)
|
| 924 |
+
submit_click_event = submit_btn.click(
|
| 925 |
+
fn=submit_and_manage_ui,
|
| 926 |
+
inputs=chat_inputs,
|
| 927 |
+
outputs=ui_components,
|
| 928 |
+
)
|
| 929 |
+
|
| 930 |
+
# Event for the "Cancel" button.
|
| 931 |
+
# It sets the cancel flag, cancels the submit event, then resets the UI.
|
| 932 |
+
cancel_btn.click(
|
| 933 |
+
fn=set_cancel_flag,
|
| 934 |
+
cancels=[submit_event, submit_click_event]
|
| 935 |
+
).then(
|
| 936 |
+
fn=reset_ui_after_cancel,
|
| 937 |
+
outputs=ui_components
|
| 938 |
+
)
|
| 939 |
+
|
| 940 |
+
txt.input(fn=toggle_submit_button, inputs=txt, outputs=submit_btn)
|
| 941 |
+
txt.change(fn=toggle_submit_button, inputs=txt, outputs=submit_btn)
|
| 942 |
+
example_dropdown.select(
|
| 943 |
+
fn=apply_example_selection,
|
| 944 |
+
outputs=[txt, submit_btn],
|
| 945 |
+
)
|
| 946 |
+
temp.change(fn=toggle_sampling_controls, inputs=temp, outputs=[k, p])
|
| 947 |
+
reset_params_btn.click(
|
| 948 |
+
fn=restore_generation_defaults,
|
| 949 |
+
outputs=[max_tok, temp, k, p, rp],
|
| 950 |
+
)
|
| 951 |
+
|
| 952 |
+
# Clear chat action
|
| 953 |
+
clr.click(
|
| 954 |
+
fn=set_cancel_flag,
|
| 955 |
+
cancels=[submit_event, submit_click_event]
|
| 956 |
+
).then(
|
| 957 |
+
fn=clear_chat_state,
|
| 958 |
+
outputs=ui_components
|
| 959 |
+
)
|
| 960 |
+
|
| 961 |
+
if __name__ == "__main__":
|
| 962 |
+
launch_args = _parse_launch_args()
|
| 963 |
+
demo.launch(
|
| 964 |
+
theme=demo_theme,
|
| 965 |
+
css=demo_css,
|
| 966 |
+
share=launch_args.share,
|
| 967 |
+
ssr_mode=False,
|
| 968 |
+
)
|