gyung commited on
Commit
0825961
·
verified ·
1 Parent(s): bf98101

Upload demo_local_cpu.py with huggingface_hub

Browse files
Files changed (1) hide show
  1. demo_local_cpu.py +261 -0
demo_local_cpu.py ADDED
@@ -0,0 +1,261 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/usr/bin/env python3
2
+ """
3
+ HybriKo-117M Linux Function Calling Demo (Local/Colab CPU)
4
+
5
+ Downloads model from HuggingFace and runs on CPU.
6
+ Usage:
7
+ pip install huggingface_hub sentencepiece
8
+ python scripts/demo_local_cpu.py --query "현재 폴더 보여줘"
9
+ """
10
+
11
+ import torch
12
+ import sentencepiece as spm
13
+ import sys
14
+ import json
15
+ import re
16
+ import argparse
17
+ import os
18
+
19
+ # HuggingFace에서 필요한 파일 다운로드
20
+ def download_files():
21
+ try:
22
+ from huggingface_hub import hf_hub_download
23
+ except ImportError:
24
+ os.system("pip install -q huggingface_hub")
25
+ from huggingface_hub import hf_hub_download
26
+
27
+ repo_id = "Yaongi/HybriKo-117M-LinuxFC-SFT-v2"
28
+ files_to_download = [
29
+ "configuration_hybridko.py",
30
+ "modeling_hybridko.py",
31
+ "pytorch_model.pt",
32
+ "HybriKo_tok.model",
33
+ ]
34
+
35
+ for filename in files_to_download:
36
+ if not os.path.exists(filename):
37
+ print(f"Downloading {filename}...")
38
+ hf_hub_download(repo_id, filename, local_dir=".")
39
+
40
+ # 파일 다운로드
41
+ download_files()
42
+
43
+ # 현재 디렉토리를 Python path에 추가
44
+ sys.path.insert(0, ".")
45
+
46
+ # 이제 import 가능
47
+ from configuration_hybridko import HybriKoConfig
48
+ from modeling_hybridko import HybriKoModel
49
+
50
+
51
+ # Exact system prompt used during training
52
+ SYSTEM_PROMPT = """You are a Linux command assistant. You can use many tools (functions) to help users with their Linux tasks.
53
+ 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:
54
+ Thought:
55
+ Action
56
+ Action Input:
57
+
58
+ After the call, you will get the call result, and you are now in a new state.
59
+ Then you will analyze your status now, then decide what to do next...
60
+ After many (Thought-call) pairs, you finally perform the task, then you can give your final answer.
61
+
62
+ Remember:
63
+ 1. The state change is irreversible, you can't go back to one of the former state.
64
+ 2. All the thought is short, at most in 5 sentences.
65
+ 3. ALWAYS call "Finish" function at the end of the task.
66
+ 4. If you cannot handle the task with the available tools, say you don't know and call Finish with give_answer.
67
+
68
+ You have access of the following tools:
69
+ [
70
+ {"name": "ls_command", "description": "List directory contents.", "parameters": {"type": "object", "properties": {"path": {"type": "string"}, "options": {"type": "string"}}, "required": ["path"]}},
71
+ {"name": "cd_command", "description": "Change the current working directory.", "parameters": {"type": "object", "properties": {"path": {"type": "string"}}, "required": ["path"]}},
72
+ {"name": "mkdir_command", "description": "Create a new directory.", "parameters": {"type": "object", "properties": {"path": {"type": "string"}}, "required": ["path"]}},
73
+ {"name": "rm_command", "description": "Remove files or directories.", "parameters": {"type": "object", "properties": {"path": {"type": "string"}, "recursive": {"type": "boolean"}}, "required": ["path"]}},
74
+ {"name": "cp_command", "description": "Copy files or directories.", "parameters": {"type": "object", "properties": {"source": {"type": "string"}, "destination": {"type": "string"}}, "required": ["source", "destination"]}},
75
+ {"name": "mv_command", "description": "Move or rename files.", "parameters": {"type": "object", "properties": {"source": {"type": "string"}, "destination": {"type": "string"}}, "required": ["source", "destination"]}},
76
+ {"name": "find_command", "description": "Find files by name pattern.", "parameters": {"type": "object", "properties": {"path": {"type": "string"}, "name": {"type": "string"}}, "required": ["path", "name"]}},
77
+ {"name": "cat_command", "description": "Display file contents.", "parameters": {"type": "object", "properties": {"file": {"type": "string"}}, "required": ["file"]}},
78
+ {"name": "grep_command", "description": "Search for patterns in files.", "parameters": {"type": "object", "properties": {"pattern": {"type": "string"}, "file": {"type": "string"}}, "required": ["pattern", "file"]}},
79
+ {"name": "head_command", "description": "Display first lines of a file.", "parameters": {"type": "object", "properties": {"file": {"type": "string"}, "lines": {"type": "integer"}}, "required": ["file"]}},
80
+ {"name": "tail_command", "description": "Display last lines of a file.", "parameters": {"type": "object", "properties": {"file": {"type": "string"}, "lines": {"type": "integer"}}, "required": ["file"]}},
81
+ {"name": "wc_command", "description": "Count lines, words, and bytes.", "parameters": {"type": "object", "properties": {"file": {"type": "string"}}, "required": ["file"]}},
82
+ {"name": "ps_command", "description": "Display running processes.", "parameters": {"type": "object", "properties": {"options": {"type": "string"}}, "required": []}},
83
+ {"name": "df_command", "description": "Display disk space usage.", "parameters": {"type": "object", "properties": {"options": {"type": "string"}}, "required": []}},
84
+ {"name": "du_command", "description": "Display directory space usage.", "parameters": {"type": "object", "properties": {"path": {"type": "string"}, "options": {"type": "string"}}, "required": ["path"]}},
85
+ {"name": "top_command", "description": "Display system processes in real-time.", "parameters": {"type": "object", "properties": {}, "required": []}},
86
+ {"name": "ping_command", "description": "Test network connectivity.", "parameters": {"type": "object", "properties": {"host": {"type": "string"}, "count": {"type": "integer"}}, "required": ["host"]}},
87
+ {"name": "curl_command", "description": "Transfer data from URL.", "parameters": {"type": "object", "properties": {"url": {"type": "string"}, "options": {"type": "string"}}, "required": ["url"]}},
88
+ {"name": "chmod_command", "description": "Change file permissions.", "parameters": {"type": "object", "properties": {"mode": {"type": "string"}, "file": {"type": "string"}}, "required": ["mode", "file"]}},
89
+ {"name": "tar_command", "description": "Archive or extract files.", "parameters": {"type": "object", "properties": {"options": {"type": "string"}, "archive": {"type": "string"}, "files": {"type": "string"}}, "required": ["options", "archive"]}},
90
+ {"name": "Finish", "description": "Complete the task.", "parameters": {"type": "object", "properties": {"give_answer": {"type": "string"}}, "required": ["give_answer"]}}
91
+ ]"""
92
+
93
+
94
+ def load_model(threads=4):
95
+ """Load model and tokenizer for CPU inference."""
96
+ torch.set_num_threads(threads)
97
+ print(f"Using {threads} CPU threads")
98
+
99
+ print("Loading tokenizer...")
100
+ sp = spm.SentencePieceProcessor()
101
+ sp.Load("HybriKo_tok.model")
102
+
103
+ print("Loading model (CPU mode)...")
104
+ config = HybriKoConfig(
105
+ d_model=768, n_layers=12, vocab_size=32000,
106
+ n_heads=12, n_kv_heads=3, ff_mult=3, max_seq_len=6144
107
+ )
108
+ model = HybriKoModel(config)
109
+ checkpoint = torch.load("pytorch_model.pt", map_location="cpu", weights_only=False)
110
+ model.load_state_dict(checkpoint["model_state_dict"])
111
+ model.eval()
112
+
113
+ print(f"✅ Model loaded on CPU ({sum(p.numel() for p in model.parameters()) / 1e6:.1f}M params)\n")
114
+ return model, sp
115
+
116
+
117
+ @torch.inference_mode()
118
+ def generate(model, tokenizer, prompt, max_new_tokens=150):
119
+ """Generate response with CPU-optimized inference."""
120
+ input_ids = tokenizer.EncodeAsIds(prompt)
121
+ input_tensor = torch.tensor([input_ids], dtype=torch.long)
122
+ prompt_len = len(input_ids)
123
+
124
+ generated = input_tensor
125
+ for step in range(max_new_tokens):
126
+ outputs = model(generated)
127
+ logits = outputs["logits"] if isinstance(outputs, dict) else outputs.logits
128
+ next_token_logits = logits[:, -1, :]
129
+ next_token = torch.argmax(next_token_logits, dim=-1, keepdim=True)
130
+ generated = torch.cat([generated, next_token], dim=1)
131
+
132
+ # Progress indicator every 10 tokens
133
+ if (step + 1) % 10 == 0:
134
+ print(".", end="", flush=True)
135
+
136
+ # Stop when we have complete Action Input
137
+ new_tokens = generated[0, prompt_len:].tolist()
138
+ new_text = tokenizer.DecodeIds(new_tokens)
139
+
140
+ if "<|im_end|>" in new_text:
141
+ break
142
+
143
+ if "Action Input:" in new_text:
144
+ ai_idx = new_text.find("Action Input:")
145
+ after_ai = new_text[ai_idx + 13:].strip()
146
+ if after_ai.startswith("{"):
147
+ brace_count = 0
148
+ for i, c in enumerate(after_ai):
149
+ if c == "{":
150
+ brace_count += 1
151
+ elif c == "}":
152
+ brace_count -= 1
153
+ if brace_count == 0:
154
+ print() # newline after progress dots
155
+ return new_text
156
+
157
+ print() # newline after progress dots
158
+ return tokenizer.DecodeIds(generated[0, prompt_len:].tolist())
159
+
160
+
161
+ def create_prompt(user_input):
162
+ """Create ChatML format prompt."""
163
+ return f"<|im_start|>system\n{SYSTEM_PROMPT}<|im_end|>\n<|im_start|>user\n{user_input}<|im_end|>\n<|im_start|>assistant\n"
164
+
165
+
166
+ def parse_response(response):
167
+ """Parse response into components."""
168
+ if "<|im_end|>" in response:
169
+ response = response.split("<|im_end|>")[0]
170
+ if "<|im_start|>" in response:
171
+ response = response.split("<|im_start|>")[0]
172
+
173
+ result = {"thought": None, "action": None, "action_input": None, "raw": response}
174
+
175
+ thought_match = re.search(r"Thought:\s*(.+?)(?=\s*Action:|\s*$)", response, re.DOTALL)
176
+ if thought_match:
177
+ result["thought"] = thought_match.group(1).strip()
178
+
179
+ action_match = re.search(r"Action:\s*(\w+)", response)
180
+ if action_match:
181
+ result["action"] = action_match.group(1)
182
+
183
+ input_match = re.search(r"Action Input:\s*(\{[^}]+\})", response, re.DOTALL)
184
+ if input_match:
185
+ try:
186
+ result["action_input"] = json.loads(input_match.group(1))
187
+ except:
188
+ result["action_input"] = input_match.group(1)
189
+
190
+ return result
191
+
192
+
193
+ def run_single(model, tokenizer, user_input):
194
+ """Run single inference."""
195
+ prompt = create_prompt(user_input)
196
+ print("Generating", end="", flush=True)
197
+ response = generate(model, tokenizer, prompt)
198
+ return parse_response(response)
199
+
200
+
201
+ def main():
202
+ parser = argparse.ArgumentParser(description="HybriKo Linux FC Demo (CPU)")
203
+ parser.add_argument("--query", type=str, help="Single query mode")
204
+ parser.add_argument("--threads", type=int, default=4, help="Number of CPU threads")
205
+ args = parser.parse_args()
206
+
207
+ print("=" * 60)
208
+ print(" HybriKo-117M Linux Function Calling Demo (CPU)")
209
+ print("=" * 60)
210
+
211
+ model, tokenizer = load_model(args.threads)
212
+
213
+ if args.query:
214
+ print(f"Input: {args.query}")
215
+ print("-" * 40)
216
+ result = run_single(model, tokenizer, args.query)
217
+ if result["thought"]:
218
+ print(f"Thought: {result['thought']}")
219
+ if result["action"]:
220
+ print(f"Action: {result['action']}")
221
+ if result["action_input"]:
222
+ print(f"Input: {json.dumps(result['action_input'], ensure_ascii=False)}")
223
+ if not result["thought"] and not result["action"]:
224
+ print(f"[Raw]: {result['raw'][:300]}")
225
+ print("-" * 40)
226
+ else:
227
+ # Interactive mode
228
+ print("Supported: ls, cd, mkdir, rm, cp, mv, find, cat, grep, head, tail, wc, ps, df, du, top, ping, curl, chmod, tar")
229
+ print("Type 'quit' to exit\n")
230
+
231
+ while True:
232
+ try:
233
+ user_input = input("[User] ").strip()
234
+ if not user_input:
235
+ continue
236
+ if user_input.lower() in ["quit", "exit", "q"]:
237
+ break
238
+
239
+ result = run_single(model, tokenizer, user_input)
240
+ print("\n[HybriKo]")
241
+ print("-" * 40)
242
+ if result["thought"]:
243
+ print(f"Thought: {result['thought']}")
244
+ if result["action"]:
245
+ print(f"Action: {result['action']}")
246
+ if result["action_input"]:
247
+ print(f"Input: {json.dumps(result['action_input'], ensure_ascii=False)}")
248
+ if not result["thought"] and not result["action"]:
249
+ print(f"[Raw]: {result['raw'][:300]}")
250
+ print("-" * 40 + "\n")
251
+
252
+ except KeyboardInterrupt:
253
+ break
254
+ except EOFError:
255
+ break
256
+
257
+ print("Goodbye!")
258
+
259
+
260
+ if __name__ == "__main__":
261
+ main()