import os
import subprocess
import signal
os.environ["GRADIO_ANALYTICS_ENABLED"] = "False"
import gradio as gr
import tempfile
from huggingface_hub import HfApi, ModelCard, whoami
from gradio_huggingfacehub_search import HuggingfaceHubSearch
from pathlib import Path
from textwrap import dedent
from apscheduler.schedulers.background import BackgroundScheduler
HF_TOKEN = os.environ.get("HF_TOKEN")
CONVERSION_SCRIPT = "convert_lora_to_gguf.py"
def process_model(peft_model_id: str, q_method: str, private_repo, oauth_token: gr.OAuthToken | None):
if oauth_token.token is None:
raise ValueError("You must be logged in to use GGUF-my-lora")
model_name = peft_model_id.split('/')[-1]
gguf_output_name = f"{model_name}-{q_method.lower()}.gguf"
try:
api = HfApi(token=oauth_token.token)
dl_pattern = ["*.md", "*.json", "*.model"]
pattern = (
"*.safetensors"
if any(
file.path.endswith(".safetensors")
for file in api.list_repo_tree(
repo_id=peft_model_id,
recursive=True,
)
)
else "*.bin"
)
dl_pattern += [pattern]
if not os.path.exists("downloads"):
os.makedirs("downloads")
if not os.path.exists("outputs"):
os.makedirs("outputs")
with tempfile.TemporaryDirectory(dir="outputs") as outputdir:
gguf_output_path = Path(outputdir)/gguf_output_name
readme_output_path = Path(outputdir)/"README.md"
with tempfile.TemporaryDirectory(dir="downloads") as tmpdir:
# Keep the model name as the dirname so the model name metadata is populated correctly
local_dir = Path(tmpdir)/model_name
print(local_dir)
api.snapshot_download(repo_id=peft_model_id, local_dir=local_dir, local_dir_use_symlinks=False, allow_patterns=dl_pattern)
print("Model downloaded successfully!")
print(f"Current working directory: {os.getcwd()}")
print(f"Model directory contents: {os.listdir(local_dir)}")
adapter_config_dir = local_dir/"adapter_config.json"
if not os.path.exists(adapter_config_dir):
raise Exception('adapter_config.json not found. Please ensure the selected repo is a PEFT LoRA model.
If you are converting a model (not a LoRA adapter), please use GGUF-my-repo instead.')
result = subprocess.run([
"python",
f"llama.cpp/{CONVERSION_SCRIPT}",
local_dir,
"--outtype",
q_method.lower(),
"--outfile",
gguf_output_path,
], shell=False, capture_output=True)
print(result)
if result.returncode != 0:
raise Exception(f"Error converting to GGUF {q_method}: {result.stderr}")
print("Model converted to GGUF successfully!")
print(f"Converted model path: {gguf_output_path}")
# Create empty repo
username = whoami(oauth_token.token)["name"]
new_repo_url = api.create_repo(repo_id=f"{username}/{model_name}-{q_method}-GGUF", exist_ok=True, private=private_repo)
new_repo_id = new_repo_url.repo_id
print("Repo created successfully!", new_repo_url)
# Upload the GGUF model
api.upload_file(
path_or_fileobj=gguf_output_path,
path_in_repo=gguf_output_name,
repo_id=new_repo_id,
)
print("Uploaded", gguf_output_name)
try:
card = ModelCard.load(peft_model_id, token=oauth_token.token)
except:
card = ModelCard("")
if card.data.tags is None:
card.data.tags = []
card.data.tags.append("llama-cpp")
card.data.tags.append("gguf-my-lora")
card.data.base_model = peft_model_id
card.text = dedent(
f"""
# {new_repo_id}
This LoRA adapter was converted to GGUF format from [`{peft_model_id}`](https://huggingface.co/{peft_model_id}) via the ggml.ai's [GGUF-my-lora](https://huggingface.co/spaces/ggml-org/gguf-my-lora) space.
Refer to the [original adapter repository](https://huggingface.co/{peft_model_id}) for more details.
## Use with llama.cpp
```bash
# with cli
llama-cli -m base_model.gguf --lora {gguf_output_name} (...other args)
# with server
llama-server -m base_model.gguf --lora {gguf_output_name} (...other args)
```
To know more about LoRA usage with llama.cpp server, refer to the [llama.cpp server documentation](https://github.com/ggerganov/llama.cpp/blob/master/examples/server/README.md).
"""
)
card.save(readme_output_path)
api.upload_file(
path_or_fileobj=readme_output_path,
path_in_repo="README.md",
repo_id=new_repo_id,
)
return (
f'