Text Generation
Transformers
Safetensors
English
llama
astheral
instruct
qlora
conversational
text-generation-inference
Instructions to use SEE7/Astheral with libraries, inference providers, notebooks, and local apps. Follow these links to get started.
- Libraries
- Transformers
How to use SEE7/Astheral with Transformers:
# Use a pipeline as a high-level helper from transformers import pipeline pipe = pipeline("text-generation", model="SEE7/Astheral") messages = [ {"role": "user", "content": "Who are you?"}, ] pipe(messages)# pip install -U transformers accelerate # Load model directly from transformers import AutoTokenizer, AutoModelForCausalLM tokenizer = AutoTokenizer.from_pretrained("SEE7/Astheral") model = AutoModelForCausalLM.from_pretrained("SEE7/Astheral", device_map="auto") messages = [ {"role": "user", "content": "Who are you?"}, ] inputs = tokenizer.apply_chat_template( messages, add_generation_prompt=True, tokenize=True, return_dict=True, return_tensors="pt", ).to(model.device) outputs = model.generate(**inputs, max_new_tokens=256) print(tokenizer.decode(outputs[0][inputs["input_ids"].shape[-1]:])) - Notebooks
- Google Colab
- Kaggle
- Local Apps Settings
- vLLM
How to use SEE7/Astheral with vLLM:
Install from pip and serve model
# Install vLLM from pip: pip install vllm # Start the vLLM server: vllm serve "SEE7/Astheral" # Call the server using curl (OpenAI-compatible API): curl -X POST "http://localhost:8000/v1/chat/completions" \ -H "Content-Type: application/json" \ --data '{ "model": "SEE7/Astheral", "messages": [ { "role": "user", "content": "What is the capital of France?" } ] }'Use Docker
docker model run hf.co/SEE7/Astheral
- SGLang
How to use SEE7/Astheral with SGLang:
Install from pip and serve model
# Install SGLang from pip: pip install sglang # Start the SGLang server: python3 -m sglang.launch_server \ --model-path "SEE7/Astheral" \ --host 0.0.0.0 \ --port 30000 # Call the server using curl (OpenAI-compatible API): curl -X POST "http://localhost:30000/v1/chat/completions" \ -H "Content-Type: application/json" \ --data '{ "model": "SEE7/Astheral", "messages": [ { "role": "user", "content": "What is the capital of France?" } ] }'Use Docker images
docker run --gpus all \ --shm-size 32g \ -p 30000:30000 \ -v ~/.cache/huggingface:/root/.cache/huggingface \ --env "HF_TOKEN=<secret>" \ --ipc=host \ lmsysorg/sglang:latest \ python3 -m sglang.launch_server \ --model-path "SEE7/Astheral" \ --host 0.0.0.0 \ --port 30000 # Call the server using curl (OpenAI-compatible API): curl -X POST "http://localhost:30000/v1/chat/completions" \ -H "Content-Type: application/json" \ --data '{ "model": "SEE7/Astheral", "messages": [ { "role": "user", "content": "What is the capital of France?" } ] }' - Docker Model Runner
How to use SEE7/Astheral with Docker Model Runner:
docker model run hf.co/SEE7/Astheral
Download v2/train.py from SEE7/Astheral: direct link, hf CLI and curl.
- Browser
- Download file 12.7 kB
-
https://huggingface.co/SEE7/Astheral/resolve/main/v2/train.py
- Command line
-
hf download hf://SEE7/Astheral/v2/train.py
-
curl -L -o train.py https://huggingface.co/SEE7/Astheral/resolve/main/v2/train.py
12.7 kB
| """Astheral v2: remote L4 QLoRA training and measured release.""" | |
| import os,json,time,gc,hashlib,shutil,math,random,urllib.request,collections | |
| from pathlib import Path | |
| import torch | |
| from torch.utils.data import Dataset | |
| from transformers import AutoModelForCausalLM,AutoTokenizer,BitsAndBytesConfig,Trainer,TrainingArguments,TrainerCallback,set_seed | |
| from peft import LoraConfig,get_peft_model,prepare_model_for_kbit_training,PeftModel | |
| from huggingface_hub import HfApi,hf_hub_download | |
| from evaluate import CASES,check,summary | |
| ROOT=Path(__file__).resolve().parent | |
| OUT=Path("/tmp/astheral-v2-output");OUT.mkdir(exist_ok=True) | |
| REPO="SEE7/Astheral";BASE="NousResearch/DeepHermes-3-Llama-3-8B-Preview";REV="53d902c7dcfa4c749a3b455149064f82af2e2549" | |
| api=HfApi();start=time.monotonic();set_seed(20261113) | |
| assert torch.cuda.is_available(),"Remote CUDA GPU required" | |
| assert "L4" in torch.cuda.get_device_name(0),"This job is provisioned for remote L4" | |
| print("REMOTE_HARDWARE",torch.cuda.get_device_name(0),flush=True) | |
| settings=json.loads((ROOT/"settings.json").read_text()) | |
| tok=AutoTokenizer.from_pretrained(BASE,revision=REV);tok.pad_token_id=128004 | |
| def messages(prompt,mode): | |
| return [{"role":"system","content":settings["system"]+("\n"+settings["thinking"] if mode=="thinking" else "")},{"role":"user","content":prompt}] | |
| class Samples(Dataset): | |
| def __init__(self,rows): | |
| self.items=[];self.kept=[];self.skipped=0 | |
| evalprompts={c["prompt"] for c in CASES} | |
| for r in rows: | |
| if any(m["role"]=="user" and m["content"] in evalprompts for m in r["messages"]):raise RuntimeError("Evaluation overlap") | |
| prompt=tok.apply_chat_template(r["messages"][:-1],tokenize=True,add_generation_prompt=True) | |
| full=tok.apply_chat_template(r["messages"],tokenize=True,add_generation_prompt=False) | |
| if len(full)>1280:self.skipped+=1;continue | |
| assert full[:len(prompt)]==prompt | |
| self.items.append({"input_ids":full,"labels":[-100]*len(prompt)+full[len(prompt):]}) | |
| self.kept.append(r) | |
| def __len__(self):return len(self.items) | |
| def __getitem__(self,i):return self.items[i] | |
| def collate(batch): | |
| size=max(len(x["input_ids"]) for x in batch) | |
| return {k:torch.tensor([x[k]+[fill]*(size-len(x[k])) for x in batch]) for k,fill in [("input_ids",128004),("labels",-100)]}|{"attention_mask":torch.tensor([[1]*len(x["input_ids"])+[0]*(size-len(x["input_ids"])) for x in batch])} | |
| records=[json.loads(s) for s in (ROOT/"curriculum.jsonl").read_text().splitlines()] | |
| random.Random(20261113).shuffle(records) | |
| # Loss-validation conversations are disjoint from training. | |
| valid=Samples(records[:64]);train=Samples(records[64:]) | |
| assert len(train)>1800 | |
| (OUT/"train.jsonl").write_text("\n".join(json.dumps(r) for r in train.kept)+"\n") | |
| (OUT/"validation.jsonl").write_text("\n".join(json.dumps(r) for r in valid.kept)+"\n") | |
| print("DATA_READY",len(train),len(valid),"skipped_long",train.skipped,flush=True) | |
| quant=BitsAndBytesConfig(load_in_4bit=True,bnb_4bit_quant_type="nf4",bnb_4bit_use_double_quant=True,bnb_4bit_compute_dtype=torch.bfloat16) | |
| model=AutoModelForCausalLM.from_pretrained(BASE,revision=REV,torch_dtype=torch.bfloat16,device_map={"":"cuda:0"},attn_implementation="sdpa") | |
| def generate(tag,cases=CASES,batch_size=4): | |
| model.eval();tok.padding_side="left";results=[] | |
| for mode in ["instruct","thinking"]: | |
| selected=[c for c in cases if c["mode"]==mode] | |
| for i in range(0,len(selected),batch_size): | |
| group=selected[i:i+batch_size];set_seed(667+i) | |
| texts=[tok.apply_chat_template(messages(c["prompt"],mode),tokenize=False,add_generation_prompt=True) for c in group] | |
| inputs=tok(texts,padding=True,return_tensors="pt").to("cuda") | |
| out=model.generate(**inputs,max_new_tokens=1024 if mode=="thinking" else 384,do_sample=False,pad_token_id=128004,eos_token_id=128009) | |
| for c,o in zip(group,out): | |
| answer=tok.decode(o[inputs["input_ids"].shape[1]:],skip_special_tokens=True) | |
| results.append({**c,"output":answer,"pass":check(c,answer)}) | |
| print("EVAL",tag,c["id"],results[-1]["pass"],flush=True) | |
| tok.padding_side="right" | |
| (OUT/(tag+"_outputs.json")).write_text(json.dumps(results,indent=2)) | |
| print("SCORE",tag,json.dumps(summary(results)),flush=True) | |
| return results | |
| baseline=generate("base",batch_size=2) | |
| del model;gc.collect();torch.cuda.empty_cache() | |
| model=AutoModelForCausalLM.from_pretrained(BASE,revision=REV,quantization_config=quant,torch_dtype=torch.bfloat16,device_map={"":"cuda:0"},attn_implementation="sdpa") | |
| model=prepare_model_for_kbit_training(model,use_gradient_checkpointing=True,gradient_checkpointing_kwargs={"use_reentrant":False}) | |
| model=get_peft_model(model,LoraConfig(r=32,lora_alpha=64,lora_dropout=0.05,target_modules="all-linear",task_type="CAUSAL_LM",bias="none")) | |
| model.config.use_cache=False | |
| trainable=sum(p.numel() for p in model.parameters() if p.requires_grad) | |
| model.print_trainable_parameters() | |
| class Budget(TrainerCallback): | |
| def on_step_end(self,args,state,control,**kwargs): | |
| if time.monotonic()-start>7200:control.should_training_stop=True | |
| return control | |
| args=TrainingArguments(output_dir="/tmp/astheral-v2-checkpoints",num_train_epochs=1.5,per_device_train_batch_size=1,per_device_eval_batch_size=1,gradient_accumulation_steps=8,learning_rate=4e-5,lr_scheduler_type="cosine",warmup_ratio=0.05,weight_decay=0.01,bf16=True,gradient_checkpointing=True,gradient_checkpointing_kwargs={"use_reentrant":False},optim="paged_adamw_8bit",logging_steps=10,save_strategy="no",report_to="none",remove_unused_columns=False,seed=20261113,data_seed=20261113,max_grad_norm=0.5,dataloader_num_workers=0) | |
| trainer=Trainer(model=model,args=args,train_dataset=train,eval_dataset=valid,data_collator=collate,callbacks=[Budget()]) | |
| before=trainer.evaluate();result=trainer.train();after=trainer.evaluate() | |
| model.config.use_cache=True;model.gradient_checkpointing_disable() | |
| candidate=generate("astheral") | |
| def gate(outputs): | |
| s=summary(outputs) | |
| return s["regression"]["passed"]==s["regression"]["total"] and s["fresh"]["passed"]>=max(31,summary(baseline)["fresh"]["passed"]) and s["number"]["passed"]>=14 and s["code"]["passed"]>=5 and s["exact"]["passed"]+s["json"]["passed"]>=12 | |
| quality=gate(candidate) | |
| # Save the adapter and reports before any merge, regardless of evaluation result. | |
| adapter=OUT/"adapter";model.save_pretrained(adapter,safe_serialization=True);tok.save_pretrained(adapter) | |
| metrics={"version":"2","base_model":BASE,"base_revision":REV,"training_examples":len(train),"validation_examples":len(valid),"skipped_long_examples":train.skipped,"trainable_parameters":trainable,"rank":32,"target_modules":"all-linear","quantization":"NF4 QLoRA; BF16 adapter computation","optimizer_steps":trainer.state.global_step,"epochs":trainer.state.epoch,"learning_rate":4e-5,"validation_loss_before":before["eval_loss"],"validation_loss_after":after["eval_loss"],"training":result.metrics,"base_scores":summary(baseline),"astheral_scores":summary(candidate),"quality_gate_passed":quality,"hardware":torch.cuda.get_device_name(0),"max_gpu_memory_allocated_bytes":torch.cuda.max_memory_allocated(),"elapsed_seconds":time.monotonic()-start,"generation":{"sampling":False,"instruct_max_new_tokens":384,"thinking_max_new_tokens":1024},"evaluation_design":"36 frozen fresh checks, two known regression cases, six review-only samples. Same settings for base and fine-tune."} | |
| (OUT/"evaluation.json").write_text(json.dumps(metrics,indent=2)) | |
| (OUT/"trainer_log.json").write_text(json.dumps(trainer.state.log_history,indent=2)) | |
| for name in ["train.py","prepare_data.py","evaluate.py","settings.json","requirements.txt","data_sources.json"]: | |
| shutil.copy2(ROOT/name,OUT/name) | |
| api.upload_folder(repo_id=REPO,folder_path=OUT,path_in_repo="v2-results",commit_message="Record v2 trained adapter and complete frozen-suite comparison") | |
| print("QUALITY_GATE",quality,json.dumps(metrics),flush=True) | |
| if not quality: | |
| print("QUANTIZED_GATE_MISSED; checking the final BF16 merge before release decision",flush=True) | |
| # Merge into original BF16 weights, rather than dequantized training weights. | |
| del trainer,model;gc.collect();torch.cuda.empty_cache() | |
| base=AutoModelForCausalLM.from_pretrained(BASE,revision=REV,torch_dtype=torch.bfloat16,device_map={"":"cuda:0"},attn_implementation="sdpa") | |
| model=PeftModel.from_pretrained(base,adapter).merge_and_unload(safe_merge=True) | |
| model.config._name_or_path="Llama-3.1-Astheral" | |
| model.config.astheral_base_model=BASE;model.config.astheral_version="2";model.config.astheral_modes=["instruct","thinking"] | |
| # Check the known failures again after conversion back to BF16. | |
| merged_checks=generate("merged_regressions",[c for c in CASES if c["group"]=="regression"],batch_size=1) | |
| if not all(c["pass"] for c in merged_checks):raise RuntimeError("Merged checkpoint regression check failed") | |
| model.save_pretrained(OUT,safe_serialization=True,max_shard_size="4GB");tok.save_pretrained(OUT) | |
| del model,base;gc.collect();torch.cuda.empty_cache() | |
| model=AutoModelForCausalLM.from_pretrained(OUT,torch_dtype=torch.bfloat16,device_map={"":"cuda:0"},attn_implementation="sdpa") | |
| merged_outputs=generate("astheral_merged",batch_size=2) | |
| if not gate(merged_outputs):raise RuntimeError("Reloaded merged model did not pass release gate") | |
| metrics["merged_scores"]=summary(merged_outputs) | |
| metrics["quality_gate_passed"]=True | |
| metrics["training_source_counts"]=dict(collections.Counter(r["source"] for r in train.kept)) | |
| metrics["elapsed_seconds"]=time.monotonic()-start | |
| (OUT/"evaluation.json").write_text(json.dumps(metrics,indent=2)) | |
| (OUT/"reload_smoke_test.json").write_text(json.dumps({"loaded_from":"saved merged BF16 checkpoint","regressions_passed":True,"full_suite_gate_passed":True},indent=2)) | |
| (OUT/"DATA.md").write_text("# Training data\n\nExact selected examples are in train.jsonl and validation.jsonl. Source revisions, component counts and dataset licenses are recorded in data_sources.json. Original Astheral examples are CC0-1.0. Model weights retain the applicable Llama license.\n") | |
| for name in ["inference.py","LICENSE","UPSTREAM_LLAMA3_LICENSE","NOTICE"]: | |
| shutil.copy2(ROOT/name if (ROOT/name).exists() else Path(hf_hub_download(REPO,name)),OUT/name) | |
| license_urls={"DATA_LICENSE_GSM8K.txt":"https://raw.githubusercontent.com/openai/grade-school-math/master/LICENSE","DATA_LICENSE_APACHE2.txt":"https://www.apache.org/licenses/LICENSE-2.0.txt"} | |
| for name,url in license_urls.items(): | |
| (OUT/name).write_bytes((ROOT/name).read_bytes() if (ROOT/name).exists() else urllib.request.urlopen(url,timeout=60).read()) | |
| (OUT/"DATA_LICENSE_ODCBY.txt").write_text("Contains information from bigcode/self-oss-instruct-sc2-exec-filter-50k, made available under the ODC Attribution License v1.0: https://opendatacommons.org/licenses/by/1-0/. Source: https://huggingface.co/datasets/bigcode/self-oss-instruct-sc2-exec-filter-50k\n") | |
| (OUT/"NOTICE").write_text((OUT/"NOTICE").read_text()+"\nContains data from HuggingFaceTB/SmolTalk (Apache 2.0), openai/gsm8k (MIT; Copyright (c) 2021 OpenAI), and bigcode/self-oss-instruct-sc2-exec-filter-50k, made available under the ODC Attribution License: https://opendatacommons.org/licenses/by/1-0/. Source revisions and transformations are recorded in data_sources.json and prepare_data.py.\n") | |
| card=(ROOT/"MODEL_CARD.md").read_text() | |
| card+="\n## Measured evaluation\n\n"+json.dumps({"base":metrics["base_scores"],"astheral":metrics["merged_scores"]},indent=2)+"\n\nFull prompts, outputs, code tests and training records are included. The 36 fresh checks cover constrained output, multi-step numerical problems and executable Python functions. The two prior failures are tracked separately as regression checks.\n" | |
| (OUT/"README.md").write_text(card) | |
| manifest={} | |
| for f in OUT.rglob("*"): | |
| if f.is_file(): | |
| h=hashlib.sha256() | |
| with f.open("rb") as stream: | |
| for chunk in iter(lambda:stream.read(8388608),b""):h.update(chunk) | |
| manifest[str(f.relative_to(OUT))]={"bytes":f.stat().st_size,"sha256":h.hexdigest()} | |
| (OUT/"artifact_manifest.json").write_text(json.dumps(manifest,indent=2)) | |
| # Freeze historical v1 results before updating the release. | |
| api.create_tag(REPO,tag="v1",revision=api.model_info(REPO).sha,exist_ok=True) | |
| api.upload_folder(repo_id=REPO,folder_path=OUT,delete_patterns=["training/*","coding_smoke_test.json"],commit_message="Release Astheral v2 with balanced training, expanded adapters and passing quality gates") | |
| api.create_tag(REPO,tag="v2",revision=api.model_info(REPO).sha,exist_ok=True) | |
| print("PUBLISHED_V2",api.model_info(REPO).sha,flush=True) | |