| |
| |
| |
|
|
| import subprocess |
| from pathlib import Path |
| from huggingface_hub import HfApi, create_repo |
| from peft import AutoPeftModelForCausalLM |
| from transformers import AutoTokenizer |
|
|
| |
| ADAPTER_REPO = "kingjux/ffmpeg-command-generator" |
| OUTPUT_REPO = "kingjux/ffmpeg-command-generator-gguf" |
|
|
| print("=" * 50) |
| print("GGUF Conversion for LM Studio") |
| print("=" * 50) |
|
|
| |
| print("\n[1/3] Loading adapter and merging with base model...") |
| model = AutoPeftModelForCausalLM.from_pretrained( |
| ADAPTER_REPO, |
| device_map="auto", |
| trust_remote_code=True, |
| ) |
| tokenizer = AutoTokenizer.from_pretrained(ADAPTER_REPO, trust_remote_code=True) |
|
|
| print("Merging LoRA weights...") |
| merged_model = model.merge_and_unload() |
|
|
| merged_path = Path("/tmp/merged_model") |
| merged_path.mkdir(exist_ok=True) |
| print(f"Saving merged model to {merged_path}...") |
| merged_model.save_pretrained(merged_path) |
| tokenizer.save_pretrained(merged_path) |
|
|
| |
| print("\n[2/3] Converting to GGUF...") |
| llama_cpp_path = Path("/tmp/llama.cpp") |
| if not llama_cpp_path.exists(): |
| subprocess.run([ |
| "git", "clone", "--depth", "1", |
| "https://github.com/ggerganov/llama.cpp.git", |
| str(llama_cpp_path) |
| ], check=True) |
|
|
| |
| subprocess.run([ |
| "pip", "install", "-q", "-r", |
| str(llama_cpp_path / "requirements" / "requirements-convert_hf_to_gguf.txt") |
| ], check=True) |
|
|
| gguf_output_dir = Path("/tmp/gguf_output") |
| gguf_output_dir.mkdir(exist_ok=True) |
|
|
| |
| f16_path = gguf_output_dir / "ffmpeg-command-generator-f16.gguf" |
| subprocess.run([ |
| "python", str(llama_cpp_path / "convert_hf_to_gguf.py"), |
| str(merged_path), |
| "--outfile", str(f16_path), |
| "--outtype", "f16" |
| ], check=True) |
| print(f"Created: {f16_path}") |
|
|
| |
| print("\n[3/3] Uploading to Hugging Face Hub...") |
| api = HfApi() |
| create_repo(OUTPUT_REPO, repo_type="model", exist_ok=True) |
|
|
| |
| model_card = """--- |
| license: apache-2.0 |
| base_model: Qwen/Qwen2.5-0.5B-Instruct |
| tags: |
| - gguf |
| - ffmpeg |
| - command-generation |
| - lm-studio |
| - ollama |
| --- |
| |
| # FFMPEG Command Generator (GGUF) |
| |
| Fine-tuned Qwen2.5-0.5B that generates FFMPEG commands from natural language with chain-of-thought reasoning. |
| |
| ## Quick Start |
| |
| ### LM Studio |
| ```bash |
| lms import kingjux/ffmpeg-command-generator-gguf |
| ``` |
| |
| ### Ollama |
| ```bash |
| ollama run hf.co/kingjux/ffmpeg-command-generator-gguf |
| ``` |
| |
| ## Example |
| |
| **Input:** "Convert video.mp4 to webm format" |
| |
| **Output:** |
| ``` |
| <think> |
| Task: Convert MP4 to WebM |
| - WebM uses VP9 video + Opus audio |
| - Use -c:v libvpx-vp9 for video |
| - Use -c:a libopus for audio |
| </think> |
| |
| ffmpeg -i video.mp4 -c:v libvpx-vp9 -c:a libopus output.webm |
| ``` |
| |
| ## Training |
| - Base: Qwen2.5-0.5B-Instruct |
| - Method: LoRA fine-tuning (r=16, alpha=32) |
| - Dataset: 30 FFMPEG command examples with CoT reasoning |
| - Trained on HuggingFace Jobs (T4 GPU) |
| """ |
|
|
| card_path = gguf_output_dir / "README.md" |
| card_path.write_text(model_card) |
|
|
| |
| for file in [card_path, f16_path]: |
| print(f"Uploading {file.name}...") |
| api.upload_file( |
| path_or_fileobj=str(file), |
| path_in_repo=file.name, |
| repo_id=OUTPUT_REPO, |
| repo_type="model" |
| ) |
|
|
| print("\n" + "=" * 50) |
| print("DONE!") |
| print(f"Model: https://huggingface.co/{OUTPUT_REPO}") |
| print(f"\nLM Studio: lms import {OUTPUT_REPO}") |
| print("=" * 50) |
|
|