gyung's picture
Upload demo_cpu.py with huggingface_hub
94ac84f verified
Raw
History Blame Contribute Delete
14.8 kB
#!/usr/bin/env python3
"""
HybriKo-117M Linux Function Calling Demo (CPU Version)
CPU-optimized version that auto-downloads from HuggingFace.
Works on Windows/Linux/macOS without GPU.
Usage:
pip install torch sentencepiece huggingface_hub
python demo_cpu.py --query "현재 폴더의 파일 목록을 보여줘"
python demo_cpu.py # Interactive mode
"""
import torch
import sentencepiece as spm
import sys
import json
import re
import argparse
import os
import io
# Set UTF-8 encoding for Windows compatibility
if sys.stdout.encoding != 'utf-8':
sys.stdout = io.TextIOWrapper(sys.stdout.buffer, encoding='utf-8', errors='replace')
if sys.stdin.encoding != 'utf-8':
sys.stdin = io.TextIOWrapper(sys.stdin.buffer, encoding='utf-8', errors='replace')
def download_files():
"""Download model files from HuggingFace."""
try:
from huggingface_hub import hf_hub_download
except ImportError:
print("Installing huggingface_hub...")
os.system("pip install -q huggingface_hub")
from huggingface_hub import hf_hub_download
repo_id = "Yaongi/HybriKo-117M-LinuxFC-SFT-v2"
files_to_download = [
"configuration_hybridko.py",
"modeling_hybridko.py",
"pytorch_model.pt",
"HybriKo_tok.model",
]
for filename in files_to_download:
if not os.path.exists(filename):
print(f"Downloading {filename}...")
hf_hub_download(repo_id, filename, local_dir=".")
# Download files first
download_files()
# Add current directory to path
sys.path.insert(0, ".")
# Now import model
from configuration_hybridko import HybriKoConfig
from modeling_hybridko import HybriKoModel
# System prompt used during training
SYSTEM_PROMPT = """You are a Linux command assistant. You can use many tools (functions) to help users with their Linux tasks.
At each step, you need to give your thought to analyze the status now and what to do next, with a function call to actually execute your step. Your output should follow this format:
Thought:
Action
Action Input:
After the call, you will get the call result, and you are now in a new state.
Then you will analyze your status now, then decide what to do next...
After many (Thought-call) pairs, you finally perform the task, then you can give your final answer.
Remember:
1. The state change is irreversible, you can't go back to one of the former state.
2. All the thought is short, at most in 5 sentences.
3. ALWAYS call "Finish" function at the end of the task.
4. If you cannot handle the task with the available tools, say you don't know and call Finish with give_answer.
You have access of the following tools:
[
{
"name": "ls_command",
"description": "List directory contents.",
"parameters": {
"type": "object",
"properties": {
"path": {"type": "string"},
"options": {"type": "string"}
},
"required": ["path"]
}
},
{
"name": "cd_command",
"description": "Change the current working directory.",
"parameters": {
"type": "object",
"properties": {"path": {"type": "string"}},
"required": ["path"]
}
},
{
"name": "mkdir_command",
"description": "Create a new directory.",
"parameters": {
"type": "object",
"properties": {"path": {"type": "string"}},
"required": ["path"]
}
},
{
"name": "rm_command",
"description": "Remove files or directories.",
"parameters": {
"type": "object",
"properties": {
"path": {"type": "string"},
"recursive": {"type": "boolean"}
},
"required": ["path"]
}
},
{
"name": "cp_command",
"description": "Copy files or directories.",
"parameters": {
"type": "object",
"properties": {
"source": {"type": "string"},
"destination": {"type": "string"}
},
"required": ["source", "destination"]
}
},
{
"name": "mv_command",
"description": "Move or rename files.",
"parameters": {
"type": "object",
"properties": {
"source": {"type": "string"},
"destination": {"type": "string"}
},
"required": ["source", "destination"]
}
},
{
"name": "find_command",
"description": "Find files by name pattern.",
"parameters": {
"type": "object",
"properties": {
"path": {"type": "string"},
"name": {"type": "string"}
},
"required": ["path", "name"]
}
},
{
"name": "cat_command",
"description": "Display file contents.",
"parameters": {
"type": "object",
"properties": {"file": {"type": "string"}},
"required": ["file"]
}
},
{
"name": "grep_command",
"description": "Search for patterns in files.",
"parameters": {
"type": "object",
"properties": {
"pattern": {"type": "string"},
"file": {"type": "string"}
},
"required": ["pattern", "file"]
}
},
{
"name": "head_command",
"description": "Display first lines of a file.",
"parameters": {
"type": "object",
"properties": {
"file": {"type": "string"},
"lines": {"type": "integer"}
},
"required": ["file"]
}
},
{
"name": "tail_command",
"description": "Display last lines of a file.",
"parameters": {
"type": "object",
"properties": {
"file": {"type": "string"},
"lines": {"type": "integer"}
},
"required": ["file"]
}
},
{
"name": "wc_command",
"description": "Count lines, words, and bytes.",
"parameters": {
"type": "object",
"properties": {"file": {"type": "string"}},
"required": ["file"]
}
},
{
"name": "ps_command",
"description": "Display running processes.",
"parameters": {
"type": "object",
"properties": {"options": {"type": "string"}},
"required": []
}
},
{
"name": "df_command",
"description": "Display disk space usage.",
"parameters": {
"type": "object",
"properties": {"options": {"type": "string"}},
"required": []
}
},
{
"name": "du_command",
"description": "Display directory space usage.",
"parameters": {
"type": "object",
"properties": {
"path": {"type": "string"},
"options": {"type": "string"}
},
"required": ["path"]
}
},
{
"name": "top_command",
"description": "Display system processes in real-time.",
"parameters": {
"type": "object",
"properties": {},
"required": []
}
},
{
"name": "ping_command",
"description": "Test network connectivity.",
"parameters": {
"type": "object",
"properties": {
"host": {"type": "string"},
"count": {"type": "integer"}
},
"required": ["host"]
}
},
{
"name": "curl_command",
"description": "Transfer data from URL.",
"parameters": {
"type": "object",
"properties": {
"url": {"type": "string"},
"options": {"type": "string"}
},
"required": ["url"]
}
},
{
"name": "chmod_command",
"description": "Change file permissions.",
"parameters": {
"type": "object",
"properties": {
"mode": {"type": "string"},
"file": {"type": "string"}
},
"required": ["mode", "file"]
}
},
{
"name": "tar_command",
"description": "Archive or extract files.",
"parameters": {
"type": "object",
"properties": {
"options": {"type": "string"},
"archive": {"type": "string"},
"files": {"type": "string"}
},
"required": ["options", "archive"]
}
},
{
"name": "Finish",
"description": "Complete the task.",
"parameters": {
"type": "object",
"properties": {"give_answer": {"type": "string"}},
"required": ["give_answer"]
}
}
]"""
def load_model(num_threads=-1):
"""Load model and tokenizer for CPU inference."""
# Set CPU threads (-1 = auto-detect max)
if num_threads <= 0:
num_threads = os.cpu_count() or 4
torch.set_num_threads(num_threads)
print(f"Using {num_threads} CPU threads (max: {os.cpu_count()})")
print("Loading tokenizer...")
sp = spm.SentencePieceProcessor()
sp.Load("HybriKo_tok.model")
print("Loading model (CPU mode)...")
config = HybriKoConfig(
d_model=768, n_layers=12, vocab_size=32000,
n_heads=12, n_kv_heads=3, ff_mult=3,
max_seq_len=6144
)
model = HybriKoModel(config)
checkpoint = torch.load("pytorch_model.pt", map_location="cpu", weights_only=False)
model.load_state_dict(checkpoint["model_state_dict"])
# Disable Flash Attention for CPU (Flash Attention requires CUDA)
for module in model.modules():
if hasattr(module, 'use_flash_attention'):
module.use_flash_attention = False
model.eval()
print(f"✅ Model loaded on CPU ({sum(p.numel() for p in model.parameters()) / 1e6:.1f}M params)\n")
return model, sp
@torch.inference_mode()
def generate(model, tokenizer, prompt, max_new_tokens=150):
"""Generate response with CPU-optimized inference."""
input_ids = tokenizer.EncodeAsIds(prompt)
input_tensor = torch.tensor([input_ids], dtype=torch.long)
prompt_len = len(input_ids)
generated = input_tensor
for _ in range(max_new_tokens):
outputs = model(generated)
logits = outputs["logits"] if isinstance(outputs, dict) else outputs.logits
next_token_logits = logits[:, -1, :]
next_token = torch.argmax(next_token_logits, dim=-1, keepdim=True)
generated = torch.cat([generated, next_token], dim=1)
# Check for completion
new_tokens = generated[0, prompt_len:].tolist()
new_text = tokenizer.DecodeIds(new_tokens)
if "<|im_end|>" in new_text:
break
# Stop when we have complete Action Input (JSON closed)
if "Action Input:" in new_text:
ai_idx = new_text.find("Action Input:")
after_ai = new_text[ai_idx + 13:].strip()
if after_ai.startswith("{"):
brace_count = 0
for i, c in enumerate(after_ai):
if c == "{":
brace_count += 1
elif c == "}":
brace_count -= 1
if brace_count == 0:
return new_text
new_tokens = generated[0, prompt_len:].tolist()
return tokenizer.DecodeIds(new_tokens)
def create_prompt(user_input):
"""Create ChatML format prompt."""
return f"<|im_start|>system\n{SYSTEM_PROMPT}<|im_end|>\n<|im_start|>user\n{user_input}<|im_end|>\n<|im_start|>assistant\n"
def parse_response(response):
"""Parse response into components."""
if "<|im_end|>" in response:
response = response.split("<|im_end|}")[0]
if "<|im_start|>" in response:
response = response.split("<|im_start|>")[0]
result = {"thought": None, "action": None, "action_input": None, "raw": response}
thought_match = re.search(r"Thought:\s*(.+?)(?=\s*Action:|\s*$)", response, re.DOTALL)
if thought_match:
result["thought"] = thought_match.group(1).strip()
action_match = re.search(r"Action:\s*(\w+)", response)
if action_match:
result["action"] = action_match.group(1)
input_match = re.search(r"Action Input:\s*(\{[^}]+\})", response, re.DOTALL)
if input_match:
try:
result["action_input"] = json.loads(input_match.group(1))
except:
result["action_input"] = input_match.group(1)
return result
def run_single(model, tokenizer, user_input):
"""Run single inference."""
prompt = create_prompt(user_input)
response = generate(model, tokenizer, prompt)
return parse_response(response)
def main():
parser = argparse.ArgumentParser(description="HybriKo Linux FC Demo (CPU)")
parser.add_argument("--query", type=str, help="Query to process")
parser.add_argument("--threads", type=int, default=-1, help="Number of CPU threads (-1 = auto max)")
args = parser.parse_args()
print("=" * 60)
print(" HybriKo-117M Linux Function Calling Demo (CPU)")
print("=" * 60)
model, tokenizer = load_model(args.threads)
if args.query:
# Single query mode
result = run_single(model, tokenizer, args.query)
print(f"Input: {args.query}")
print("-" * 40)
if result["thought"]:
print(f"Thought: {result['thought']}")
if result["action"]:
print(f"Action: {result['action']}")
if result["action_input"]:
print(f"Input: {json.dumps(result['action_input'], ensure_ascii=False)}")
if not result["thought"] and not result["action"]:
print(f"[Raw]: {result['raw'][:500]}")
print("-" * 40)
else:
# Interactive mode
print("Supported: ls, cd, mkdir, rm, cp, mv, find, cat, grep, head, tail, wc, ps, df, du, top, ping, curl, chmod, tar")
print("Type 'quit' to exit\n")
while True:
try:
print("[User] ", end="", flush=True)
user_input = sys.stdin.readline()
if not user_input:
break
user_input = user_input.strip()
if not user_input:
continue
if user_input.lower() in ["quit", "exit", "q"]:
break
result = run_single(model, tokenizer, user_input)
print("\n[HybriKo]")
print("-" * 40)
if result["thought"]:
print(f"Thought: {result['thought']}")
if result["action"]:
print(f"Action: {result['action']}")
if result["action_input"]:
print(f"Input: {json.dumps(result['action_input'], ensure_ascii=False)}")
print("-" * 40 + "\n")
except KeyboardInterrupt:
break
except EOFError:
break
print("Goodbye!")
if __name__ == "__main__":
main()
Free AI Image Generator No sign-up. Instant results. Open Now