From d2a9610a116012256706c329fbafaa7b80feb37b Mon Sep 17 00:00:00 2001 From: ModelHub XC Date: Fri, 12 Jun 2026 16:07:16 +0800 Subject: [PATCH] =?UTF-8?q?=E5=88=9D=E5=A7=8B=E5=8C=96=E9=A1=B9=E7=9B=AE?= =?UTF-8?q?=EF=BC=8C=E7=94=B1ModelHub=20XC=E7=A4=BE=E5=8C=BA=E6=8F=90?= =?UTF-8?q?=E4=BE=9B=E6=A8=A1=E5=9E=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Model: ksjpswaroop/zindango-slm Source: Original Platform --- .gitattributes | 39 +++++++++ README.md | 83 +++++++++++++++++++ chat_template.jinja | 134 +++++++++++++++++++++++++++++++ config.json | 34 ++++++++ generation_config.json | 9 +++ model.safetensors | 3 + scripts/convert_and_push_gguf.py | 98 ++++++++++++++++++++++ scripts/llamacpp_chat.py | 105 ++++++++++++++++++++++++ scripts/llamacpp_chat.sh | 25 ++++++ scripts/push_scripts_to_hub.py | 63 +++++++++++++++ scripts/push_to_hub.py | 85 ++++++++++++++++++++ scripts/test_zindango_gguf.py | 94 ++++++++++++++++++++++ tokenizer.json | 3 + tokenizer_config.json | 16 ++++ zindango-slm-Q4_K_M.gguf | 3 + zindango-slm-Q8_0.gguf | 3 + zindango-slm-f16.gguf | 3 + 17 files changed, 800 insertions(+) create mode 100644 .gitattributes create mode 100644 README.md create mode 100644 chat_template.jinja create mode 100644 config.json create mode 100644 generation_config.json create mode 100644 model.safetensors create mode 100644 scripts/convert_and_push_gguf.py create mode 100644 scripts/llamacpp_chat.py create mode 100644 scripts/llamacpp_chat.sh create mode 100644 scripts/push_scripts_to_hub.py create mode 100644 scripts/push_to_hub.py create mode 100644 scripts/test_zindango_gguf.py create mode 100644 tokenizer.json create mode 100644 tokenizer_config.json create mode 100644 zindango-slm-Q4_K_M.gguf create mode 100644 zindango-slm-Q8_0.gguf create mode 100644 zindango-slm-f16.gguf diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..02cbd68 --- /dev/null +++ b/.gitattributes @@ -0,0 +1,39 @@ +*.7z filter=lfs diff=lfs merge=lfs -text +*.arrow filter=lfs diff=lfs merge=lfs -text +*.bin filter=lfs diff=lfs merge=lfs -text +*.bz2 filter=lfs diff=lfs merge=lfs -text +*.ckpt filter=lfs diff=lfs merge=lfs -text +*.ftz filter=lfs diff=lfs merge=lfs -text +*.gz filter=lfs diff=lfs merge=lfs -text +*.h5 filter=lfs diff=lfs merge=lfs -text +*.joblib filter=lfs diff=lfs merge=lfs -text +*.lfs.* filter=lfs diff=lfs merge=lfs -text +*.mlmodel filter=lfs diff=lfs merge=lfs -text +*.model filter=lfs diff=lfs merge=lfs -text +*.msgpack filter=lfs diff=lfs merge=lfs -text +*.npy filter=lfs diff=lfs merge=lfs -text +*.npz filter=lfs diff=lfs merge=lfs -text +*.onnx filter=lfs diff=lfs merge=lfs -text +*.ot filter=lfs diff=lfs merge=lfs -text +*.parquet filter=lfs diff=lfs merge=lfs -text +*.pb filter=lfs diff=lfs merge=lfs -text +*.pickle filter=lfs diff=lfs merge=lfs -text +*.pkl filter=lfs diff=lfs merge=lfs -text +*.pt filter=lfs diff=lfs merge=lfs -text +*.pth filter=lfs diff=lfs merge=lfs -text +*.rar filter=lfs diff=lfs merge=lfs -text +*.safetensors filter=lfs diff=lfs merge=lfs -text +saved_model/**/* filter=lfs diff=lfs merge=lfs -text +*.tar.* filter=lfs diff=lfs merge=lfs -text +*.tar filter=lfs diff=lfs merge=lfs -text +*.tflite filter=lfs diff=lfs merge=lfs -text +*.tgz filter=lfs diff=lfs merge=lfs -text +*.wasm filter=lfs diff=lfs merge=lfs -text +*.xz filter=lfs diff=lfs merge=lfs -text +*.zip filter=lfs diff=lfs merge=lfs -text +*.zst filter=lfs diff=lfs merge=lfs -text +*tfevents* filter=lfs diff=lfs merge=lfs -text +tokenizer.json filter=lfs diff=lfs merge=lfs -text +zindango-slm-f16.gguf filter=lfs diff=lfs merge=lfs -text +zindango-slm-Q8_0.gguf filter=lfs diff=lfs merge=lfs -text +zindango-slm-Q4_K_M.gguf filter=lfs diff=lfs merge=lfs -text diff --git a/README.md b/README.md new file mode 100644 index 0000000..10b61be --- /dev/null +++ b/README.md @@ -0,0 +1,83 @@ +--- +language: +- en +license: apache-2.0 +library_name: transformers +pipeline_tag: text-generation +tags: +- zindango +- instruction-tuned +- english-only +- sft +--- + +# zindango-slm + +A lightweight, capable instruction-following model for Zindango. Fine-tuned for clarity, versatility, and personal AI workloads. + +## Features + +- **Task-agnostic**: Handles summaries, Q&A, drafting, analysis, and open-ended assistance +- **Consistent identity**: Reliably introduces itself as zindango-slm, the Zindango model +- **English-optimized**: Tuned for natural, coherent responses in English + +## Why zindango-slm for Personal AI + +- **3B parameters** — Runs on consumer hardware (CPU, modest GPUs, edge devices) without cloud dependencies +- **Compact and fast** — Low latency for real-time conversations and local inference +- **Privacy-preserving** — Run entirely on-device; no data leaves your machine +- **Customizable base** — Easy to further fine-tune for your own workflows and preferences +- **GGUF support** — Use with llama.cpp for efficient CPU inference and broad compatibility + +## GGUF (llama.cpp) + +For CPU/Edge inference with [llama.cpp](https://github.com/ggml-org/llama.cpp): + +| File | Size | Quality | +|------|------|---------| +| `zindango-slm-f16.gguf` | ~7.9GB | Best | +| `zindango-slm-Q8_0.gguf` | ~4.2GB | High | + +```bash +# Q8_0 (recommended for most systems) +llama-cli -m ksjpswaroop/zindango-slm:zindango-slm-Q8_0.gguf -p "Who are you?" + +# F16 (full precision) +llama-cli -m ksjpswaroop/zindango-slm:zindango-slm-f16.gguf -p "Who are you?" +``` + +## Usage (Transformers) + +```python +from transformers import AutoModelForCausalLM, AutoTokenizer + +model = AutoModelForCausalLM.from_pretrained("ksjpswaroop/zindango-slm", trust_remote_code=True) +tokenizer = AutoTokenizer.from_pretrained("ksjpswaroop/zindango-slm", trust_remote_code=True) + +messages = [{"role": "user", "content": "Who are you?"}] +text = tokenizer.apply_chat_template(messages, tokenize=False, add_generation_prompt=True) +inputs = tokenizer(text, return_tensors="pt").to(model.device) +out = model.generate(**inputs, max_new_tokens=256, pad_token_id=tokenizer.pad_token_id) +response = tokenizer.decode(out[0][inputs["input_ids"].shape[1]:], skip_special_tokens=True) +print(response) +``` + +Or with pipeline: + +```python +from transformers import pipeline + +gen = pipeline("text-generation", model="ksjpswaroop/zindango-slm", trust_remote_code=True) +out = gen("Who created you?", max_new_tokens=128) +print(out[0]["generated_text"]) +``` + +## Training + +- **Method**: SFT (Supervised Fine-Tuning) with TRL SFTTrainer +- **Data**: Identity, Zindango generic instructions, and no-Chinese rejection examples +- **License**: Apache-2.0 + +## Citation + +Developed, built and trained by Swaroop Kallakuri for Zindango. diff --git a/chat_template.jinja b/chat_template.jinja new file mode 100644 index 0000000..4866a62 --- /dev/null +++ b/chat_template.jinja @@ -0,0 +1,134 @@ + + {%- if tools %} + {{- '<|im_start|>system +' }} + {%- if messages[0].role == 'system' %} + {{- messages[0].content + ' + +' }} + {%- else %} + {{- 'You are a tool-calling expert. Given a question and a set of available tools, perform one or more function calls to achieve the goal. If no tool fits, respond in natural language. If parameters are missing, ask the user. If tool results suffice, summarize and reply.' }} + {%- endif %} + {{- "# Tools + +You may call one or more functions to assist with the user query. + +You are provided with function signatures within XML tags: +" }} + {%- for tool in tools %} + {{- " +" }} + {{- tool | tojson }} + {%- endfor %} + {{- " + + +For each function call, return a json object with function name and arguments within XML tags: + +{\"name\": , \"arguments\": } +<|im_end|> +" }} + {%- else %} + {%- if messages[0].role == 'system' %} + {{- '<|im_start|>system +' + messages[0].content + '<|im_end|> +' }} + {%- else %} + {{- '<|im_start|>system +You are zindango-slm, a helpful assistant for Zindango.<|im_end|> +' }} + {%- endif %} + {%- endif %} + {%- set ns = namespace(multi_step_tool=true, last_query_index=messages|length - 1) %} + {%- for message in messages[::-1] %} + {%- set index = (messages|length - 1) - loop.index0 %} + {%- if ns.multi_step_tool and message.role == "user" and message.content is string and not(message.content.startswith('') and message.content.endswith('')) %} + {%- set ns.multi_step_tool = false %} + {%- set ns.last_query_index = index %} + {%- endif %} + {%- endfor %} + {%- for message in messages %} + {%- if message.content is string %} + {%- set content = message.content %} + {%- else %} + {%- set content = '' %} + {%- endif %} + {%- if (message.role == "user") or (message.role == "system" and not loop.first) %} + {{- '<|im_start|>' + message.role + ' +' + content + '<|im_end|>' + ' +' }} + {%- elif message.role == "assistant" %} + {%- set reasoning_content = '' %} + {%- if message.reasoning_content is string %} + {%- set reasoning_content = message.reasoning_content %} + {%- else %} + {%- if '' in content %} + {%- set reasoning_content = content.split('')[0].rstrip(' +').split('')[-1].lstrip(' +') %} + {%- set content = content.split('')[-1].lstrip(' +') %} + {%- endif %} + {%- endif %} + {%- if loop.index0 > ns.last_query_index or keep_all_think or (extra_body is defined and extra_body.keep_all_think) %} + {%- if loop.last or (not loop.last and reasoning_content) %} + {{- '<|im_start|>' + message.role + ' + +' + reasoning_content.strip(' +') + ' + + +' + content.lstrip(' +') }} + {%- else %} + {{- '<|im_start|>' + message.role + ' +' + content }} + {%- endif %} + {%- else %} + {{- '<|im_start|>' + message.role + ' +' + content }} + {%- endif %} + {%- if message.tool_calls %} + {%- for tool_call in message.tool_calls %} + {%- if (loop.first and content) or (not loop.first) %} + {{- ' +' }} + {%- endif %} + {%- if tool_call.function %} + {%- set tool_call = tool_call.function %} + {%- endif %} + {{- ' +{"name": "' }} + {{- tool_call.name }} + {{- '", "arguments": ' }} + {%- if tool_call.arguments is string %} + {{- tool_call.arguments }} + {%- else %} + {{- tool_call.arguments | tojson }} + {%- endif %} + {{- '} +' }} + {%- endfor %} + {%- endif %} + {{- '<|im_end|> +' }} + {%- elif message.role == "tool" %} + {%- if loop.first or (messages[loop.index0 - 1].role != "tool") %} + {{- '<|im_start|>user' }} + {%- endif %} + {{- ' + +' }} + {{- content }} + {{- ' +' }} + {%- if loop.last or (messages[loop.index0 + 1].role != "tool") %} + {{- '<|im_end|> +' }} + {%- endif %} + {%- endif %} + {%- endfor %} + {%- if add_generation_prompt %} + {{- '<|im_start|>assistant +' }} + {%- endif %} diff --git a/config.json b/config.json new file mode 100644 index 0000000..4e5cd23 --- /dev/null +++ b/config.json @@ -0,0 +1,34 @@ +{ + "architectures": [ + "LlamaForCausalLM" + ], + "attention_bias": false, + "attention_dropout": 0.0, + "bos_token_id": 166100, + "dtype": "float32", + "embd_pdrop": 0.0, + "eos_token_id": 166101, + "head_dim": 128, + "hidden_act": "silu", + "hidden_size": 2560, + "initializer_range": 0.02, + "intermediate_size": 10496, + "max_position_embeddings": 262144, + "mlp_bias": false, + "model_type": "llama", + "num_attention_heads": 20, + "num_hidden_layers": 32, + "num_key_value_heads": 4, + "pad_token_id": 0, + "pretraining_tp": 1, + "resid_pdrop": 0.0, + "rms_norm_eps": 1e-05, + "rope_parameters": { + "rope_theta": 70000000, + "rope_type": "default" + }, + "tie_word_embeddings": false, + "transformers_version": "5.1.0", + "use_cache": false, + "vocab_size": 166144 +} diff --git a/generation_config.json b/generation_config.json new file mode 100644 index 0000000..8e4b1c6 --- /dev/null +++ b/generation_config.json @@ -0,0 +1,9 @@ +{ + "_from_model_config": true, + "bos_token_id": 166100, + "eos_token_id": [ + 166101 + ], + "pad_token_id": 0, + "transformers_version": "5.1.0" +} diff --git a/model.safetensors b/model.safetensors new file mode 100644 index 0000000..c17485f --- /dev/null +++ b/model.safetensors @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:11acc7b933ccb9418e9aa80fbca6233c3b2dc213f6b03a5e8d1e52538e40d485 +size 15734582264 diff --git a/scripts/convert_and_push_gguf.py b/scripts/convert_and_push_gguf.py new file mode 100644 index 0000000..4257f71 --- /dev/null +++ b/scripts/convert_and_push_gguf.py @@ -0,0 +1,98 @@ +#!/usr/bin/env python3 +""" +Convert zindango-slm to GGUF and push to Hugging Face. + +Requires: llama.cpp cloned, gguf, sentencepiece + pip install gguf sentencepiece + git clone https://github.com/ggml-org/llama.cpp + +Usage: + python scripts/convert_and_push_gguf.py [--model-dir PATH] [--quantize Q4_K_M] +""" + +import argparse +import subprocess +import sys +from pathlib import Path + +from huggingface_hub import HfApi, create_repo, upload_folder, upload_file + + +def main(): + parser = argparse.ArgumentParser() + parser.add_argument("--model-dir", default="outputs/zindango-slm-20260215_124754") + parser.add_argument("--llama-cpp", default="/home/piren/projects/llama.cpp") + parser.add_argument("--quantize", choices=["q4_k_m", "q5_k_m", "q8_0", "none"], default="none") + parser.add_argument("--repo-id", default=None) + parser.add_argument("--skip-create", action="store_true") + parser.add_argument("--push-only", action="store_true", help="Skip conversion, only push existing GGUF") + args = parser.parse_args() + + project_root = Path(__file__).resolve().parent.parent + model_dir = project_root / args.model_dir + out_dir = project_root / "outputs" + f16_gguf = out_dir / "zindango-slm-f16.gguf" + + if not args.push_only: + llama_cpp = Path(args.llama_cpp) + if not (llama_cpp / "convert_hf_to_gguf.py").exists(): + raise SystemExit(f"llama.cpp not found at {llama_cpp}. Clone it first.") + if not model_dir.exists(): + raise SystemExit(f"Model not found: {model_dir}") + + # Convert to F16 GGUF + cmd = [ + sys.executable, + str(llama_cpp / "convert_hf_to_gguf.py"), + str(model_dir), + "--outtype", "f16", + "--outfile", str(f16_gguf), + ] + print("Converting to GGUF f16...") + subprocess.run(cmd, check=True) + + # Optionally quantize + if args.quantize != "none": + quant_bin = llama_cpp / "build" / "bin" / "llama-quantize" + if not quant_bin.exists(): + quant_bin = llama_cpp / "bin" / "llama-quantize" + if quant_bin.exists(): + q_gguf = out_dir / f"zindango-slm-{args.quantize}.gguf" + cmd = [str(quant_bin), str(f16_gguf), str(q_gguf), args.quantize.upper()] + print(f"Quantizing to {args.quantize}...") + subprocess.run(cmd, check=True) + else: + print("llama-quantize not found; skipping quantization") + + # Push to Hub + api = HfApi() + user = api.whoami() + username = user["name"] + repo_id = args.repo_id or f"{username}/zindango-slm" + + if not args.skip_create: + try: + create_repo(repo_id, repo_type="model", exist_ok=True) + except Exception as e: + if "403" in str(e).lower() or "forbidden" in str(e).lower(): + print("Create repo failed. Run with --skip-create after creating manually.") + raise + + # Upload GGUF file(s): f16 + any quantized (q4_k_m, q8_0, etc.) + quant_ggufs = list(out_dir.glob("zindango-slm-q*.gguf")) + list(out_dir.glob("zindango-slm-Q*.gguf")) + for gguf_path in [f16_gguf] + quant_ggufs: + if gguf_path.exists(): + print(f"Uploading {gguf_path.name}...") + upload_file( + path_or_fileobj=str(gguf_path), + path_in_repo=gguf_path.name, + repo_id=repo_id, + repo_type="model", + commit_message=f"Add {gguf_path.name}", + ) + + print(f"Done. Model: https://huggingface.co/{repo_id}") + + +if __name__ == "__main__": + main() diff --git a/scripts/llamacpp_chat.py b/scripts/llamacpp_chat.py new file mode 100644 index 0000000..798e819 --- /dev/null +++ b/scripts/llamacpp_chat.py @@ -0,0 +1,105 @@ +#!/usr/bin/env python3 +""" +llama.cpp chat with zindango-slm (GGUF) for English chat verification. +Uses llama-cpp-python with the Q8_0 quantized model from Hugging Face. +""" +import os +import sys + + +def main(): + try: + from llama_cpp import Llama + except ImportError: + print("llama-cpp-python not installed.") + print("Install: pip install llama-cpp-python") + print("Or use pre-built wheels: pip install llama-cpp-python --extra-index-url https://abetlen.github.io/llama-cpp-python/whl/cpu") + print("For GPU: pip install llama-cpp-python --extra-index-url https://abetlen.github.io/llama-cpp-python/whl/cu121") + print("\nAlternatively run: ./scripts/llamacpp_chat.sh (requires llama-cli from llama.cpp)") + return 1 + + script_dir = os.path.dirname(os.path.abspath(__file__)) + project_root = os.path.dirname(script_dir) + model_dir = os.path.join(project_root, "models", "zindango-slm") + gguf_path = os.path.join(model_dir, "zindango-slm-Q8_0.gguf") + + if not os.path.isfile(gguf_path): + print(f"GGUF not found at {gguf_path}") + print("Download with: huggingface-cli download ksjpswaroop/zindango-slm zindango-slm-Q8_0.gguf --local-dir models/zindango-slm") + os.makedirs(model_dir, exist_ok=True) + try: + from huggingface_hub import hf_hub_download + print("Downloading zindango-slm-Q8_0.gguf from Hugging Face...") + path = hf_hub_download( + repo_id="ksjpswaroop/zindango-slm", + filename="zindango-slm-Q8_0.gguf", + local_dir=model_dir, + local_dir_use_symlinks=False, + ) + gguf_path = path + except Exception as e: + print(f"Download failed: {e}") + return 1 + + print("Loading zindango-slm (Q8_0)...") + llm = Llama( + model_path=gguf_path, + n_ctx=2048, + n_threads=os.cpu_count() or 4, + chat_format="chatml", + verbose=False, + ) + + messages = [ + {"role": "system", "content": "You are a helpful assistant. Always respond in English."}, + ] + + print("\n" + "=" * 60) + print("zindango-slm Chat (llama.cpp) - English verification") + print("=" * 60) + print("Type your message and press Enter. Commands: /quit, /clear") + print() + + while True: + try: + user_input = input("You: ").strip() + except (EOFError, KeyboardInterrupt): + print("\nBye!") + break + + if not user_input: + continue + if user_input.lower() in ("/quit", "/exit", "quit", "exit"): + print("Bye!") + break + if user_input.lower() == "/clear": + messages = [messages[0]] + print("[Context cleared]") + continue + + messages.append({"role": "user", "content": user_input}) + + print("Assistant: ", end="", flush=True) + stream = llm.create_chat_completion( + messages=messages, + max_tokens=512, + temperature=0.7, + stream=True, + ) + full_reply = "" + for chunk in stream: + delta = chunk["choices"][0].get("delta", {}) + content = delta.get("content", "") + if content: + print(content, end="", flush=True) + full_reply += content + print() + + if full_reply: + messages.append({"role": "assistant", "content": full_reply}) + + return 0 + + +if __name__ == "__main__": + sys.exit(main()) diff --git a/scripts/llamacpp_chat.sh b/scripts/llamacpp_chat.sh new file mode 100644 index 0000000..5993dd0 --- /dev/null +++ b/scripts/llamacpp_chat.sh @@ -0,0 +1,25 @@ +#!/bin/bash +# llama.cpp chat with zindango-slm for English verification +# Prerequisites: llama.cpp built (llama-cli) and GGUF model + +set -e +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +PROJECT_ROOT="$(dirname "$SCRIPT_DIR")" +MODEL_DIR="${MODEL_DIR:-$PROJECT_ROOT/models/zindango-slm}" +GGUF="${GGUF:-$MODEL_DIR/zindango-slm-Q8_0.gguf}" +LLAMA_CLI="${LLAMA_CLI:-llama-cli}" + +if ! command -v "$LLAMA_CLI" &>/dev/null; then + echo "llama-cli not found. Build llama.cpp or set LLAMA_CLI:" + echo " git clone https://github.com/ggerganov/llama.cpp && cd llama.cpp && make" + exit 1 +fi + +if [[ ! -f "$GGUF" ]]; then + echo "GGUF not found. Downloading..." + mkdir -p "$MODEL_DIR" + huggingface-cli download ksjpswaroop/zindango-slm zindango-slm-Q8_0.gguf --local-dir "$MODEL_DIR" || exit 1 +fi + +echo "=== zindango-slm Chat (llama.cpp) - English verification ===" +exec "$LLAMA_CLI" -m "$GGUF" -c 2048 -i diff --git a/scripts/push_scripts_to_hub.py b/scripts/push_scripts_to_hub.py new file mode 100644 index 0000000..cdb3a54 --- /dev/null +++ b/scripts/push_scripts_to_hub.py @@ -0,0 +1,63 @@ +#!/usr/bin/env python3 +""" +Push zindango-slm scripts to Hugging Face model repo (scripts/ folder). + +Usage: + python scripts/push_scripts_to_hub.py [--repo-id USERNAME/zindango-slm] +""" + +import argparse +from pathlib import Path + +from huggingface_hub import HfApi, create_repo, upload_file + + +def main(): + parser = argparse.ArgumentParser() + parser.add_argument("--repo-id", default=None) + parser.add_argument("--skip-create", action="store_true") + args = parser.parse_args() + + sft_root = Path(__file__).resolve().parent.parent + benchmark_root = sft_root.parent / "nanbeige-benchmark-verification" + + api = HfApi() + user = api.whoami() + username = user["name"] + repo_id = args.repo_id or f"{username}/zindango-slm" + + if not args.skip_create: + try: + create_repo(repo_id, repo_type="model", exist_ok=True) + except Exception as e: + if "403" in str(e).lower() or "forbidden" in str(e).lower(): + print("Create repo failed. Run with --skip-create.") + raise + + scripts_to_upload = [ + (sft_root / "scripts/convert_and_push_gguf.py", "scripts/convert_and_push_gguf.py"), + (sft_root / "scripts/push_to_hub.py", "scripts/push_to_hub.py"), + (sft_root / "scripts/push_scripts_to_hub.py", "scripts/push_scripts_to_hub.py"), + (benchmark_root / "scripts/test_zindango_gguf.py", "scripts/test_zindango_gguf.py"), + (benchmark_root / "scripts/llamacpp_chat.py", "scripts/llamacpp_chat.py"), + (benchmark_root / "scripts/llamacpp_chat.sh", "scripts/llamacpp_chat.sh"), + ] + + for local_path, path_in_repo in scripts_to_upload: + if not local_path.exists(): + print(f"Skipping {local_path} (not found)") + continue + print(f"Uploading {path_in_repo}...") + upload_file( + path_or_fileobj=str(local_path), + path_in_repo=path_in_repo, + repo_id=repo_id, + repo_type="model", + commit_message=f"Add {path_in_repo}", + ) + + print(f"Done. https://huggingface.co/{repo_id}") + + +if __name__ == "__main__": + main() diff --git a/scripts/push_to_hub.py b/scripts/push_to_hub.py new file mode 100644 index 0000000..7b0627a --- /dev/null +++ b/scripts/push_to_hub.py @@ -0,0 +1,85 @@ +#!/usr/bin/env python3 +""" +Push zindango-slm to Hugging Face Hub. +Usage: + python scripts/push_to_hub.py [--model-dir PATH] [--repo-id USERNAME/zindango-slm] +Requires: huggingface-cli login (or HF_TOKEN env) +""" + +import argparse +from pathlib import Path + +from huggingface_hub import HfApi, create_repo, upload_folder + + +def main(): + parser = argparse.ArgumentParser(description="Push zindango-slm to Hugging Face Hub") + parser.add_argument( + "--model-dir", + type=str, + default="outputs/zindango-slm-20260215_124754", + help="Local model directory to upload", + ) + parser.add_argument( + "--repo-id", + type=str, + default=None, + help="Hub repo ID (username/zindango-slm). Default: infer from login", + ) + parser.add_argument( + "--private", + action="store_true", + help="Create private repository", + ) + parser.add_argument( + "--skip-create", + action="store_true", + help="Skip create_repo (use if repo already exists or create manually at https://huggingface.co/new)", + ) + args = parser.parse_args() + + model_dir = Path(args.model_dir) + if not model_dir.exists(): + raise SystemExit(f"Model directory not found: {model_dir}") + + api = HfApi() + try: + user = api.whoami() + username = user["name"] + except Exception as e: + raise SystemExit( + f"Not logged in to Hugging Face. Run: huggingface-cli login\nError: {e}" + ) + + repo_id = args.repo_id or f"{username}/zindango-slm" + print(f"Uploading to https://huggingface.co/{repo_id}") + + # Create repo if needed (skip if --skip-create or 403) + if not args.skip_create: + try: + create_repo(repo_id, repo_type="model", private=args.private, exist_ok=True) + except Exception as e: + err = str(e).lower() + if "403" in err or "forbidden" in err or "rights" in err: + print("Note: Could not create repo (check token write access).") + print("Create it manually at: https://huggingface.co/new") + print("Then run with: --skip-create") + raise + if "already exists" not in err and "409" not in err: + raise + + # Exclude checkpoint subdir and training artifacts + ignore_patterns = ["checkpoint-*", "training_args.bin", "*.pt", ".git*"] + + upload_folder( + folder_path=str(model_dir), + repo_id=repo_id, + repo_type="model", + ignore_patterns=ignore_patterns, + commit_message="Upload zindango-slm: SFT for Zindango (English-only)", + ) + print(f"Done. Model: https://huggingface.co/{repo_id}") + + +if __name__ == "__main__": + main() diff --git a/scripts/test_zindango_gguf.py b/scripts/test_zindango_gguf.py new file mode 100644 index 0000000..0c5e7ce --- /dev/null +++ b/scripts/test_zindango_gguf.py @@ -0,0 +1,94 @@ +#!/usr/bin/env python3 +""" +Test zindango-slm: GGUF (llama-cpp-python) or HF (transformers) fallback. +Runs a single prompt to verify the model loads and generates. +""" +import os +import sys + + +def test_gguf(gguf_path: str) -> bool: + """Test via llama-cpp-python if available.""" + try: + from llama_cpp import Llama + except ImportError: + return False + + print("Loading zindango-slm (GGUF) with llama-cpp-python...") + llm = Llama( + model_path=gguf_path, + n_ctx=512, + n_threads=os.cpu_count() or 4, + chat_format="chatml", + verbose=False, + ) + messages = [ + {"role": "system", "content": "You are a helpful assistant. Reply briefly."}, + {"role": "user", "content": "Who are you? One sentence only."}, + ] + out = llm.create_chat_completion(messages=messages, max_tokens=64, temperature=0.7) + reply = out["choices"][0]["message"]["content"] + print("Reply:", reply) + return bool(reply.strip()) + + +def test_transformers(local_path: str | None = None) -> bool: + """Test via transformers (HF model) as fallback when GGUF/llama.cpp unavailable.""" + try: + from transformers import AutoModelForCausalLM, AutoTokenizer + except ImportError: + print("transformers not installed: pip install transformers torch") + return False + + model_id = local_path if local_path and os.path.isdir(local_path) else "ksjpswaroop/zindango-slm" + print(f"Testing zindango-slm (transformers) - fallback when llama-cpp unavailable...") + tokenizer = AutoTokenizer.from_pretrained(model_id, trust_remote_code=True) + model = AutoModelForCausalLM.from_pretrained( + model_id, + trust_remote_code=True, + torch_dtype="auto", + low_cpu_mem_usage=True, + ) + messages = [{"role": "user", "content": "Who are you? One sentence only."}] + text = tokenizer.apply_chat_template( + messages, tokenize=False, add_generation_prompt=True + ) + inputs = tokenizer(text, return_tensors="pt") + out = model.generate( + **inputs, max_new_tokens=64, pad_token_id=tokenizer.pad_token_id or tokenizer.eos_token_id + ) + reply = tokenizer.decode( + out[0][inputs["input_ids"].shape[1] :], skip_special_tokens=True + ) + print("Reply:", reply) + return bool(reply.strip()) + + +def main(): + script_dir = os.path.dirname(os.path.abspath(__file__)) + project_root = os.path.dirname(script_dir) + model_dir = os.path.join(project_root, "models", "zindango-slm") + + # Prefer Q8_0, then f16 + for name in ("zindango-slm-Q8_0.gguf", "zindango-slm-f16.gguf"): + gguf_path = os.path.join(model_dir, name) + if os.path.isfile(gguf_path): + print(f"Trying GGUF: {gguf_path}") + if test_gguf(gguf_path): + print("\n[OK] zindango-slm GGUF test passed.") + return 0 + break + + print("\nllama-cpp-python unavailable or failed. Using transformers fallback...") + local_hf = os.path.join(project_root, "models", "zindango-slm-hf") + if test_transformers(local_hf): + print("\n[OK] zindango-slm transformers test passed.") + return 0 + + print("\n[FAIL] No working backend. Install: pip install transformers torch") + print("For GGUF: pip install llama-cpp-python") + return 1 + + +if __name__ == "__main__": + sys.exit(main()) diff --git a/tokenizer.json b/tokenizer.json new file mode 100644 index 0000000..f23c3b2 --- /dev/null +++ b/tokenizer.json @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:1d8f0326910136aca20831249220b38ce5299527647bc8c6b65404485c479740 +size 18451122 diff --git a/tokenizer_config.json b/tokenizer_config.json new file mode 100644 index 0000000..085af8f --- /dev/null +++ b/tokenizer_config.json @@ -0,0 +1,16 @@ +{ + "add_prefix_space": true, + "backend": "tokenizers", + "bos_token": "<|im_start|>", + "clean_up_tokenization_spaces": false, + "eos_token": "<|im_end|>", + "is_local": false, + "legacy": true, + "model_max_length": 1000000000000000019884624838656, + "pad_token": "", + "sp_model_kwargs": {}, + "spaces_between_special_tokens": false, + "tokenizer_class": "TokenizersBackend", + "unk_token": "", + "use_default_system_prompt": false +} diff --git a/zindango-slm-Q4_K_M.gguf b/zindango-slm-Q4_K_M.gguf new file mode 100644 index 0000000..d984047 --- /dev/null +++ b/zindango-slm-Q4_K_M.gguf @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:a93106f871d4be0962ad6f1dc74c157c3a6ca290a179d7e8a94929460c950dd2 +size 4183947456 diff --git a/zindango-slm-Q8_0.gguf b/zindango-slm-Q8_0.gguf new file mode 100644 index 0000000..e73253e --- /dev/null +++ b/zindango-slm-Q8_0.gguf @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:deb81161ccd7d5c200a642638e522ac74cd8dd4510974964c09ad0fee96e06a1 +size 4183946912 diff --git a/zindango-slm-f16.gguf b/zindango-slm-f16.gguf new file mode 100644 index 0000000..376a865 --- /dev/null +++ b/zindango-slm-f16.gguf @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:0f1e19e4306ed070df9ec49a9ecda7023cacfe2e46104959f139aa6a423403c2 +size 7871576576