Skip to content

Commit 71f6721

Browse files
author
Sanggyu Lee
committed
[ggma] Add gyu (ggma yielding utility) tool
Implement gyu CLI tool to automate GGMA model package creation: - Merge prefill.py and decode.py into unified export.py - Create modular gpm tool structure: - gyu/init.py: Setup venv, install deps (CPU-only torch), clone TICO, extract o2o tools - gyu/import.py: Download complete model from HuggingFace - gyu/export.py: Run conversion pipeline and create .ggma package - gyu/common.py: Shared utilities and constants - gyu/clean.py: Remove building directory - gyu/gyu: Bash wrapper to dispatch commands Documentation: - Rename README.md → DEVELOPER.md (technical guide) - Add USER.md (user-facing guide)
1 parent fd78f13 commit 71f6721

File tree

12 files changed

+446
-156
lines changed

12 files changed

+446
-156
lines changed

runtime/ggma/examples/generate_text/README.md renamed to runtime/ggma/examples/generate_text/DEVELOPER.md

Lines changed: 4 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
1-
# TinyLlama Text Generation Example
1+
# TinyLlama Text Generation Developer Guide
22

3-
This document provides a step‑by‑step guide for generating and processing a TinyLlama textgeneration model.
3+
This document provides a detailed technical guide for generating, processing, and optimizing the TinyLlama text-generation model. For basic usage, see [USER.md](USER.md).
44

55
## Summary
66

@@ -22,14 +22,6 @@ source _/bin/activate
2222
pip install -r requirements.txt
2323
```
2424

25-
### 3. Install TICO (Torch IR to Circle ONE)
26-
```bash
27-
# Clone the repository
28-
git clone https://github.com/Samsung/TICO.git
29-
# Install it in editable mode
30-
pip install -e TICO
31-
```
32-
3325
### 4. Get [o2o](https://github.com/Samsung/ONE/pull/16233) in PATH
3426
*Requires the GitHub CLI (`gh`).*
3527
```bash
@@ -41,8 +33,8 @@ export PATH=../../../../tools/o2o:$PATH
4133

4234
### 1. Create the prefill and decode Circle model files
4335
```bash
44-
python prefill.py # Generates prefill.circle
45-
python decode.py # Generates decode_.circle
36+
python tinyllama.py --mode prefill # Generates prefill.circle
37+
python tinyllama.py --mode decode # Generates decode_.circle
4638
```
4739

4840
Verify the generated files:
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
# TinyLlama Text Generation User Guide
2+
3+
This guide shows how to create a GGMA package for the TinyLlama model using the `gyu` (GGMA Yielding Utility) tool.
4+
5+
## Quick Start
6+
7+
```bash
8+
cd runtime/ggma/examples/generate_text/
9+
10+
# 1. Initialize environment (one-time setup)
11+
gyu/gyu init
12+
13+
# Clean up everything (including venv and tools)
14+
gyu/gyu clean --all
15+
16+
# 2. Import model from HuggingFace
17+
gyu/gyu import Maykeye/TinyLLama-v0
18+
19+
# 3. Export to GGMA package
20+
gyu/gyu export -s tinyllama.py -p tinyllama.pipeline
21+
22+
# 4. Your package is ready at: build/tinyllama-v0.ggma/
23+
```
24+
25+
## Result
26+
27+
After running the commands above, you'll have a complete GGMA package:
28+
29+
```bash
30+
$ tree build/tinyllama-v0.ggma/
31+
build/tinyllama-v0.ggma/
32+
├── config.json
33+
├── model.circle
34+
├── tokenizer.json
35+
└── tokenizer.model
36+
```
37+
38+
The package is ready to use with `ggma_run`.
39+
40+
## Build ggma and Run the ggma package
41+
42+
```bash
43+
# Build ggma_run (from ONE root)
44+
make -j$(nproc)
45+
make install
46+
47+
# Run the model
48+
Product/out/bin/ggma_run build/tinyllama-v0.ggma
49+
```
50+
51+
For developers who want to understand what happens under the hood, see [DEVELOPER.md](DEVELOPER.md).

runtime/ggma/examples/generate_text/decode.py

Lines changed: 0 additions & 68 deletions
This file was deleted.
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
#!/usr/bin/env python3
2+
import shutil
3+
import os
4+
5+
import argparse
6+
from common import VENV_DIR
7+
8+
def main():
9+
parser = argparse.ArgumentParser(description="Clean build artifacts")
10+
parser.add_argument("--all", action="store_true", help="Remove all generated files including venv, TICO, and o2o")
11+
args = parser.parse_args()
12+
13+
# Always remove build directory
14+
build_dir = "build"
15+
if os.path.exists(build_dir):
16+
print(f"Removing {build_dir} directory...")
17+
shutil.rmtree(build_dir)
18+
else:
19+
print(f"{build_dir} directory does not exist.")
20+
21+
if args.all:
22+
dirs_to_remove = ["TICO", "o2o", VENV_DIR]
23+
for d in dirs_to_remove:
24+
if os.path.exists(d):
25+
print(f"Removing {d} directory...")
26+
shutil.rmtree(d)
27+
print("Full clean complete.")
28+
else:
29+
print("Clean complete.")
30+
31+
if __name__ == "__main__":
32+
main()
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
import subprocess
2+
3+
# Constants
4+
VENV_DIR = "venv"
5+
PR_WORKTREE = "_pr_16233"
6+
PR_BRANCH = "pr-16233"
7+
PR_REF = "refs/pull/16233/head"
8+
9+
def run_command(cmd, cwd=None, env=None, check=True):
10+
print(f"Running: {cmd}")
11+
subprocess.run(cmd, shell=True, cwd=cwd, env=env, check=check)
Lines changed: 112 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,112 @@
1+
#!/usr/bin/env python3
2+
import argparse
3+
import shutil
4+
import json
5+
import yaml
6+
import os
7+
from common import VENV_DIR, run_command
8+
9+
def main():
10+
parser = argparse.ArgumentParser(description="Export model to GGMA package")
11+
parser.add_argument("-s", "--script", required=True, help="Export script to use (e.g., tinyllama.py)")
12+
parser.add_argument("-p", "--pipeline", required=True, help="Pipeline configuration file (e.g., tinyllama.pipeline)")
13+
args = parser.parse_args()
14+
15+
export_script_name = args.script
16+
pipeline_config_path = os.path.abspath(args.pipeline)
17+
18+
# Change to build directory
19+
build_dir = "build"
20+
if not os.path.exists(build_dir):
21+
print(f"Error: {build_dir} directory does not exist. Run 'gyu import' first.")
22+
return
23+
24+
os.chdir(build_dir)
25+
26+
# Load pipeline configuration
27+
if not os.path.exists(pipeline_config_path):
28+
print(f"Error: Pipeline configuration file {pipeline_config_path} not found.")
29+
return
30+
31+
with open(pipeline_config_path, "r") as f:
32+
pipeline_config = yaml.safe_load(f)
33+
34+
# Find model directory by config.json
35+
model_dir = None
36+
model_id = None
37+
for d in os.listdir("."):
38+
config_path = os.path.join(d, "config.json")
39+
if os.path.isdir(d) and os.path.exists(config_path):
40+
model_dir = d
41+
# Read model ID from config.json
42+
with open(config_path, "r") as f:
43+
config = json.load(f)
44+
model_id = config.get("_name_or_path", d)
45+
print(f"Using local model directory: {model_dir}")
46+
print(f"Model ID from config: {model_id}")
47+
break
48+
49+
if not model_dir:
50+
raise ValueError("No local model directory found (directory with config.json)")
51+
52+
# Add o2o tools to PATH
53+
env = os.environ.copy()
54+
o2o_path = os.path.abspath("../o2o")
55+
env["PATH"] = f"{o2o_path}:{env['PATH']}"
56+
57+
python_bin = os.path.join("..", VENV_DIR, "bin", "python3")
58+
export_script = os.path.join("..", export_script_name)
59+
60+
# 1. Generate prefill and decode circles
61+
print(f"Running {export_script_name} (prefill)...")
62+
run_command(f"{python_bin} {export_script} --mode prefill --model {model_dir}", env=env)
63+
64+
print(f"Running {export_script_name} (decode)...")
65+
run_command(f"{python_bin} {export_script} --mode decode --model {model_dir}", env=env)
66+
67+
# Helper to run pipeline command
68+
def run_pipeline_step(step_name):
69+
if step_name in pipeline_config:
70+
print(f"Running {step_name} pipeline...")
71+
cmd = pipeline_config[step_name]
72+
# If cmd is a multiline string (from YAML |), it might contain newlines.
73+
# We can replace newlines with spaces or let the shell handle it if it's a single command string.
74+
# For safety with pipes, we replace newlines with spaces if they are meant to be a single line command.
75+
# But YAML block scalar preserves newlines.
76+
# If the user wrote it with pipes at the start of lines, we should join them.
77+
cmd = cmd.replace("\n", " ")
78+
run_command(cmd, env=env)
79+
80+
# 2. Pipeline (decode)
81+
run_pipeline_step("decode")
82+
83+
# 3. Merge
84+
run_pipeline_step("merge")
85+
86+
# 4. Create package directory and copy files
87+
# Find source directory with tokenizer.json
88+
source_dir = None
89+
for d in os.listdir("."):
90+
if os.path.isdir(d) and os.path.exists(os.path.join(d, "tokenizer.json")):
91+
source_dir = d
92+
break
93+
94+
if source_dir:
95+
package_dir = f"{source_dir}.ggma"
96+
print(f"Creating package directory {package_dir}...")
97+
os.makedirs(package_dir, exist_ok=True)
98+
99+
# Copy tokenizer and config files
100+
for filename in ["tokenizer.json", "tokenizer.model", "config.json"]:
101+
src = os.path.join(source_dir, filename)
102+
if os.path.exists(src):
103+
shutil.copy2(src, package_dir)
104+
105+
# Move model.circle
106+
print(f"Moving model.circle to {package_dir}...")
107+
shutil.move("model.circle", os.path.join(package_dir, "model.circle"))
108+
else:
109+
print("Warning: Could not find source directory (directory with tokenizer.json). Leaving model.circle in current dir.")
110+
111+
if __name__ == "__main__":
112+
main()
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
#!/bin/bash
2+
SCRIPT_DIR=$(dirname "$0")
3+
COMMAND="$1"
4+
shift # Remove command from arguments
5+
6+
if [ "$COMMAND" == "init" ]; then
7+
python3 "$SCRIPT_DIR/$COMMAND.py" "$@"
8+
else
9+
if [ ! -f "venv/bin/python3" ]; then
10+
echo "Error: Environment not initialized. Run 'gyu init' first."
11+
exit 1
12+
fi
13+
venv/bin/python3 "$SCRIPT_DIR/$COMMAND.py" "$@"
14+
fi
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
#!/usr/bin/env python3
2+
import sys
3+
import os
4+
from huggingface_hub import snapshot_download
5+
6+
def main():
7+
if len(sys.argv) < 2:
8+
print("Usage: gyu import <model_id>")
9+
sys.exit(1)
10+
11+
model_id = sys.argv[1]
12+
model_basename = model_id.split("/")[-1].lower()
13+
14+
# Create build directory
15+
build_dir = "build"
16+
os.makedirs(build_dir, exist_ok=True)
17+
18+
# Download into build directory
19+
target_dir = os.path.join(build_dir, model_basename)
20+
print(f"Downloading model files for {model_id} into {target_dir}...")
21+
22+
snapshot_download(repo_id=model_id, local_dir=target_dir)
23+
24+
if __name__ == "__main__":
25+
main()

0 commit comments

Comments
 (0)