flexsds 0.2.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (62) hide show
  1. flexsds/__init__.py +4 -0
  2. flexsds/assets/ptit_logo.png +0 -0
  3. flexsds/cli/__init__.py +1 -0
  4. flexsds/cli/banner.py +84 -0
  5. flexsds/cli/main.py +330 -0
  6. flexsds/cli/output.py +69 -0
  7. flexsds/cli/task_runner.py +171 -0
  8. flexsds/cli/wizard.py +360 -0
  9. flexsds/client/__init__.py +4 -0
  10. flexsds/client/chat.py +137 -0
  11. flexsds/client/client.py +107 -0
  12. flexsds/client/monitor.py +95 -0
  13. flexsds/core/__init__.py +44 -0
  14. flexsds/core/compatibility.py +442 -0
  15. flexsds/core/controller.py +502 -0
  16. flexsds/core/cost_model.py +530 -0
  17. flexsds/core/credentials.py +111 -0
  18. flexsds/core/hardware.py +77 -0
  19. flexsds/core/tree_topology.py +296 -0
  20. flexsds/profiles/profile_sm75_t4.json +62 -0
  21. flexsds/profiles/profile_sm89_l4.json +194 -0
  22. flexsds/server/__init__.py +1 -0
  23. flexsds/server/colab_runner.py +62 -0
  24. flexsds/server/local_runner.py +208 -0
  25. flexsds/server/modal_app.py +50 -0
  26. flexsds/server/modal_runner.py +98 -0
  27. flexsds/server/vscode_cli.py +82 -0
  28. flexsds-0.2.0.dist-info/METADATA +204 -0
  29. flexsds-0.2.0.dist-info/RECORD +62 -0
  30. flexsds-0.2.0.dist-info/WHEEL +5 -0
  31. flexsds-0.2.0.dist-info/entry_points.txt +2 -0
  32. flexsds-0.2.0.dist-info/top_level.txt +4 -0
  33. pipeline/entity_eval.py +171 -0
  34. pipeline/examples/node1_extractor_input_m0_04_001.txt +9 -0
  35. pipeline/examples/node1_extractor_output_m0_04_001.json +90 -0
  36. pipeline/extractor.py +502 -0
  37. pipeline/invoice_models.py +163 -0
  38. pipeline/node2_questions.py +30 -0
  39. pipeline/openai_compatible.py +87 -0
  40. pipeline/prompts/README.md +18 -0
  41. pipeline/prompts/node1_extractor_system.md +92 -0
  42. pipeline/prompts/node3_report_user.md +5 -0
  43. pipeline/report_writer.py +143 -0
  44. pipeline/schemas/invoice_extraction.schema.json +469 -0
  45. pipeline/sql_guard.py +140 -0
  46. pipeline/sqlite/schema.sql +219 -0
  47. pipeline/text_to_sql.py +170 -0
  48. pipeline/validation.py +166 -0
  49. pipeline/w1_workload.py +139 -0
  50. proposer/__init__.py +31 -0
  51. proposer/adapter.py +153 -0
  52. proposer/controller.py +460 -0
  53. proposer/cost_model.py +216 -0
  54. proposer/tree_adaptive.py +466 -0
  55. server/__init__.py +3 -0
  56. server/adaptive_scheduler.py +215 -0
  57. server/budget_planner.py +287 -0
  58. server/lineage.py +290 -0
  59. server/lineage_diagnostics.py +38 -0
  60. server/lineage_executor.py +129 -0
  61. server/lineage_startup.py +47 -0
  62. server/t4_rowwise_attention.py +262 -0
@@ -0,0 +1,208 @@
1
+ """Local serving runner and zero-zombie process management."""
2
+ from __future__ import annotations
3
+
4
+ import json
5
+ import os
6
+ import signal
7
+ import socket
8
+ import subprocess
9
+ from pathlib import Path
10
+ from typing import Any, Dict, List, Optional
11
+
12
+ try:
13
+ from flexsds.cli.output import print_output as print
14
+ except ModuleNotFoundError:
15
+ from src.flexsds.cli.output import print_output as print
16
+
17
+
18
+ def finish_launch(proc, background: bool = False) -> int:
19
+ """Propagate startup/foreground failure without equating a process with readiness."""
20
+ if proc is None:
21
+ print("Launch failed; no serving readiness established.")
22
+ return 1
23
+ print("Serving process started; endpoint readiness is not verified.")
24
+ try:
25
+ code = proc.poll() if background else proc.wait()
26
+ return code if isinstance(code, int) else 0
27
+ except KeyboardInterrupt:
28
+ proc.terminate()
29
+ try:
30
+ proc.wait(timeout=15)
31
+ except subprocess.TimeoutExpired:
32
+ proc.kill()
33
+ proc.wait()
34
+ return 130
35
+
36
+
37
+ class LocalServingRunner:
38
+ """Khởi chạy và quản lý vòng đời máy chủ vLLM cục bộ với FlexSDS."""
39
+
40
+ def build_command(self, config: Dict[str, Any], port: Optional[int] = None) -> List[str]:
41
+ """Tạo danh sách đối số dòng lệnh chạy vLLM phục vụ mô hình."""
42
+ serving_cfg = config.get("serving", config)
43
+ target_model = serving_cfg.get("target_model", "Qwen/Qwen2.5-7B-Instruct-AWQ")
44
+ draft_model = serving_cfg.get("draft_model", "duckhoi/qwen2.5-7b-eagle3.1-speculator")
45
+ draft_method = serving_cfg.get("draft_method", "eagle3")
46
+ use_hetero = serving_cfg.get("use_heterogeneous_vocab", True)
47
+
48
+ effective_port = port if port is not None else serving_cfg.get("port", 8000)
49
+ target_lower = target_model.lower()
50
+
51
+ # Xác định quantization và dtype
52
+ if "awq" in target_lower:
53
+ quantization = "awq_marlin"
54
+ dtype = "bfloat16"
55
+ elif "gptq" in target_lower:
56
+ quantization = "gptq"
57
+ dtype = "float16"
58
+ else:
59
+ quantization = serving_cfg.get("quantization", "none")
60
+ dtype = serving_cfg.get("dtype", "bfloat16")
61
+
62
+ speculative_config: Dict[str, Any] = {
63
+ "method": draft_method,
64
+ "model": draft_model,
65
+ "num_speculative_tokens": 4,
66
+ "num_speculative_tokens_per_batch_size": [[1, 32, 4]],
67
+ "draft_sample_method": "greedy",
68
+ }
69
+ if use_hetero:
70
+ speculative_config["use_heterogeneous_vocab"] = True
71
+
72
+ cmd: List[str] = [
73
+ "vllm",
74
+ "serve",
75
+ target_model,
76
+ "--host",
77
+ "0.0.0.0",
78
+ "--port",
79
+ str(effective_port),
80
+ "--served-model-name",
81
+ serving_cfg.get("served_model_name", "qwen2.5-7b-adaptive"),
82
+ "--dtype",
83
+ dtype,
84
+ "--generation-config",
85
+ "vllm",
86
+ "--gpu-memory-utilization",
87
+ str(serving_cfg.get("gpu_memory_utilization", 0.85)),
88
+ "--max-model-len",
89
+ str(serving_cfg.get("max_model_len", 4096)),
90
+ "--max-num-seqs",
91
+ str(serving_cfg.get("max_num_seqs", 32)),
92
+ ]
93
+
94
+ if quantization != "none":
95
+ cmd.extend(["--quantization", quantization])
96
+
97
+ if serving_cfg.get("enable_adaptive", True):
98
+ cmd.extend([
99
+ "--speculative-config", json.dumps(speculative_config),
100
+ "--scheduler-cls", "src.server.adaptive_scheduler.AdaptiveAsyncScheduler",
101
+ "--distributed-executor-backend", "src.server.lineage_executor.LineageUniProcExecutor",
102
+ "--per-request-spec-decode-metrics", "summary",
103
+ ])
104
+ else:
105
+ # Static speculation uses vLLM's default scheduler/executor, not the adaptive pair.
106
+ speculative_config.pop("num_speculative_tokens_per_batch_size", None)
107
+ cmd.extend(["--speculative-config", json.dumps(speculative_config)])
108
+
109
+ return cmd
110
+
111
+ def check_port_available(self, port: int) -> bool:
112
+ """Kiểm tra xem cổng port có đang khả dụng không."""
113
+ with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
114
+ try:
115
+ s.bind(("", port))
116
+ return True
117
+ except OSError:
118
+ return False
119
+
120
+ def setup_environment(self, config: Dict[str, Any]) -> Dict[str, str]:
121
+ """Thiết lập các biến môi trường cho FlexSDS Adaptive Scheduler."""
122
+ serving_cfg = config.get("serving", config)
123
+ env = dict(os.environ)
124
+ project_root = str(Path(__file__).resolve().parents[3])
125
+ existing_pythonpath = env.get("PYTHONPATH", "")
126
+ if project_root not in existing_pythonpath:
127
+ env["PYTHONPATH"] = f"{project_root}{os.pathsep}{existing_pythonpath}" if existing_pythonpath else project_root
128
+ env["ADAPTIVE_ENABLE_VRAM_GUARD"] = "1"
129
+ env["ADAPTIVE_TELEMETRY_PATH"] = os.path.abspath(
130
+ serving_cfg.get("telemetry_path", "telemetry/serving_trace.jsonl")
131
+ )
132
+ env["ADAPTIVE_MAX_K"] = str(serving_cfg.get("max_k", 4))
133
+ env["ADAPTIVE_MIN_BATCH_K"] = "0"
134
+ env["ADAPTIVE_PER_REQUEST"] = "1"
135
+ return env
136
+
137
+ def launch(
138
+ self,
139
+ config: Dict[str, Any],
140
+ port: Optional[int] = None,
141
+ background: bool = False,
142
+ pid_file: str = ".flexsds.pid",
143
+ dry_run: bool = False,
144
+ ) -> Optional[subprocess.Popen]:
145
+ """Khởi chạy tiến trình máy chủ cục bộ."""
146
+ effective_port = port if port is not None else config.get("serving", config).get("port", 8000)
147
+ cmd = self.build_command(config, port=effective_port)
148
+ env = self.setup_environment(config)
149
+
150
+ if dry_run:
151
+ print("\n[FlexSDS] Dry-run execution command:")
152
+ print(" ".join(cmd))
153
+ print(f"Environment: ADAPTIVE_ENABLE_VRAM_GUARD={env.get('ADAPTIVE_ENABLE_VRAM_GUARD')}")
154
+ return None
155
+
156
+ if not self.check_port_available(effective_port):
157
+ print(f"\n\033[31m[Lỗi]\033[0m Cổng {effective_port} đang bị chiếm dụng bởi tiến trình khác.")
158
+ print(f" Gợi ý: Dùng cờ --port <cổng_khác> hoặc chạy: flexsds stop")
159
+ return None
160
+
161
+ if background and os.name == "nt":
162
+ print("Background process-group serving is unsupported on Windows; use foreground serving.")
163
+ return None
164
+
165
+ try:
166
+ if background:
167
+ log_path = "flexsds_server.log"
168
+ log_file = open(log_path, "a", encoding="utf-8")
169
+ proc = subprocess.Popen(
170
+ cmd,
171
+ env=env,
172
+ preexec_fn=os.setsid,
173
+ stdout=log_file,
174
+ stderr=subprocess.STDOUT,
175
+ )
176
+ with open(pid_file, "w", encoding="utf-8") as f:
177
+ f.write(f"{proc.pid}\n")
178
+ print(f"[FlexSDS] Server started in background (PID: {proc.pid}, Log: {log_path})")
179
+ return proc
180
+ else:
181
+ return subprocess.Popen(cmd, env=env)
182
+ except FileNotFoundError:
183
+ print("\n\033[31m[Error]\033[0m Server binary not found in PATH.")
184
+ return None
185
+
186
+ def stop(self, pid_file: str = ".flexsds.pid") -> bool:
187
+ """Dừng nhóm tiến trình máy chủ vLLM và dọn dẹp sạch tiến trình con."""
188
+ if os.name == "nt":
189
+ print("Local process-group stop is unsupported on Windows; stop the foreground session.")
190
+ return False
191
+ if not os.path.exists(pid_file):
192
+ return False
193
+
194
+ try:
195
+ with open(pid_file, "r", encoding="utf-8") as f:
196
+ content = f.read().strip()
197
+ if not content:
198
+ return False
199
+ pid = int(content)
200
+
201
+ os.killpg(pid, signal.SIGTERM)
202
+ except (ProcessLookupError, ValueError):
203
+ pass
204
+ finally:
205
+ if os.path.exists(pid_file):
206
+ os.remove(pid_file)
207
+
208
+ return True
@@ -0,0 +1,50 @@
1
+ """Modal HTTP app; configuration is passed by the CLI, never embedded credentials.
2
+
3
+ Development: modal serve. Persistent service: modal deploy.
4
+ The web_server decorator owns startup readiness and endpoint exposure.
5
+ """
6
+ from __future__ import annotations
7
+ import json
8
+ import os
9
+ import subprocess
10
+ from pathlib import Path
11
+
12
+ from flexsds.server.local_runner import LocalServingRunner
13
+
14
+ APP_NAME = "flexsds-serving"
15
+ SERVING_CONFIG = json.loads(os.environ.get("FLEXSDS_SERVING_CONFIG", "{}"))
16
+ PORT = int(SERVING_CONFIG.get("port", 8000))
17
+ GPU = SERVING_CONFIG.get("target_gpu", "L4")
18
+ if GPU not in {"L4", "T4", "A10G"} or not 1 <= PORT <= 65535:
19
+ raise ValueError("Unsupported Modal GPU or serving port")
20
+ PROJECT_ROOT = Path(__file__).resolve().parents[3]
21
+
22
+ try:
23
+ import modal
24
+ except ImportError:
25
+ modal = None
26
+
27
+ if modal is not None:
28
+ app = modal.App(APP_NAME)
29
+ image = (
30
+ modal.Image.debian_slim(python_version="3.11")
31
+ .pip_install("vllm==0.30.0", "fastapi", "psutil")
32
+ .env({"PYTHONPATH": "/root/project:/root/project/src",
33
+ "FLEXSDS_SERVING_CONFIG": json.dumps(SERVING_CONFIG)})
34
+ .add_local_dir(str(PROJECT_ROOT / "src"), remote_path="/root/project/src")
35
+ .add_local_file(str(PROJECT_ROOT / "experiments/scalar_vllm_source_manifest.json"),
36
+ remote_path="/root/project/experiments/scalar_vllm_source_manifest.json")
37
+ )
38
+ volume = modal.Volume.from_name("ptit-model-cache", create_if_missing=True)
39
+
40
+ @app.function(image=image, gpu=GPU,
41
+ volumes={"/root/.cache/huggingface": volume}, timeout=3600)
42
+ @modal.web_server(PORT, startup_timeout=600)
43
+ def serve():
44
+ runner = LocalServingRunner()
45
+ # Do not use the laptop's preflight here: the CLI evaluated the remote budget.
46
+ # vLLM and Modal's web-server startup check remain the runtime authorities.
47
+ subprocess.Popen(runner.build_command(SERVING_CONFIG),
48
+ env=runner.setup_environment(SERVING_CONFIG))
49
+ else:
50
+ app = None
@@ -0,0 +1,98 @@
1
+ """Modal cloud serving runner for FlexSDS (BYOC, ephemeral, and persistent deploy)."""
2
+ from __future__ import annotations
3
+
4
+ import os
5
+ import json
6
+ import subprocess
7
+ from pathlib import Path
8
+ from typing import Any, Dict, List, Optional
9
+
10
+ try:
11
+ from flexsds.cli.output import print_output as print
12
+ from flexsds.core.credentials import CredentialsManager
13
+ except ModuleNotFoundError:
14
+ from src.flexsds.cli.output import print_output as print
15
+ from src.flexsds.core.credentials import CredentialsManager
16
+
17
+
18
+ class ModalServingRunner:
19
+ """Quản lý triển khai FlexSDS Serverless trên nền tảng đám mây Modal.com."""
20
+
21
+ DEFAULT_APP_NAME = "flexsds-serving"
22
+
23
+ def __init__(self, credentials_manager: Optional[CredentialsManager] = None) -> None:
24
+ self.cred_mgr = credentials_manager or CredentialsManager()
25
+
26
+ def check_credentials(self) -> bool:
27
+ """Kiểm tra thông tin định danh Modal qua CredentialsManager, biến môi trường hoặc ~/.modal.toml."""
28
+ self.cred_mgr.inject_into_environ()
29
+
30
+ token_id = os.environ.get("MODAL_TOKEN_ID") or self.cred_mgr.get_credential("modal", "MODAL_TOKEN_ID")
31
+ token_secret = os.environ.get("MODAL_TOKEN_SECRET") or self.cred_mgr.get_credential("modal", "MODAL_TOKEN_SECRET")
32
+ if token_id and token_secret:
33
+ return True
34
+
35
+ toml_path = os.path.expanduser("~/.modal.toml")
36
+ if os.path.exists(toml_path):
37
+ return True
38
+
39
+ return False
40
+
41
+ def build_modal_command(self, config: Dict[str, Any], deploy: bool = False) -> List[str]:
42
+ """Tạo danh sách đối số dòng lệnh gọi công cụ CLI modal tới modal_app.py."""
43
+ app_path = str(Path(__file__).parent / "modal_app.py")
44
+ return ["modal", "deploy" if deploy else "serve", app_path]
45
+
46
+ def setup_environment(self, config: Dict[str, Any]) -> Dict[str, str]:
47
+ """Forward non-secret serving settings for app construction in either mode."""
48
+ cfg = config.get("serving", config)
49
+ keys = ("target_model", "draft_model", "draft_method", "use_heterogeneous_vocab",
50
+ "port", "enable_adaptive", "target_gpu", "max_model_len", "max_num_seqs")
51
+ env = dict(os.environ)
52
+ env["FLEXSDS_SERVING_CONFIG"] = json.dumps({k: cfg[k] for k in keys if k in cfg})
53
+ return env
54
+
55
+ def launch(
56
+ self,
57
+ config: Dict[str, Any],
58
+ deploy: bool = False,
59
+ dry_run: bool = False,
60
+ ) -> Optional[subprocess.Popen]:
61
+ """Khởi chạy ứng dụng Modal serverless."""
62
+ if not dry_run and not self.check_credentials():
63
+ print("\n\033[31m[Lỗi định danh]\033[0m Chưa cấu hình thông tin xác thực Modal.")
64
+ print(" Vui lòng chạy: flexsds auth login --provider modal")
65
+ print(" Hoặc cấu hình qua lệnh: modal setup")
66
+ return None
67
+
68
+ cmd = self.build_modal_command(config, deploy=deploy)
69
+
70
+ if dry_run:
71
+ mode_desc = "Persistent Deploy" if deploy else "Ephemeral Run"
72
+ print(f"\n[FlexSDS Modal] Dry-run command ({mode_desc}):")
73
+ print(" ".join(cmd))
74
+ print(f"Serving configuration: {self.setup_environment(config)['FLEXSDS_SERVING_CONFIG']}")
75
+ return None
76
+
77
+ try:
78
+ proc = subprocess.Popen(cmd, env=self.setup_environment(config))
79
+ return proc
80
+ except FileNotFoundError:
81
+ print("\n\033[31m[Lỗi]\033[0m Không tìm thấy công cụ CLI 'modal'. Cài đặt: pip install modal")
82
+ return None
83
+
84
+ def check_process_status(self, proc: subprocess.Popen) -> tuple[bool, Optional[int]]:
85
+ """Kiểm tra trạng thái tiến trình Modal; phát hiện sớm nếu ứng dụng dừng bất ngờ."""
86
+ code = proc.poll()
87
+ if code is None:
88
+ return True, None
89
+ return False, code
90
+
91
+ def stop(self, app_name: str = DEFAULT_APP_NAME) -> bool:
92
+ """Dừng ứng dụng đang chạy trên hạ tầng Modal Cloud."""
93
+ cmd = ["modal", "app", "stop", app_name]
94
+ try:
95
+ res = subprocess.run(cmd, capture_output=True, text=True)
96
+ return res.returncode == 0
97
+ except Exception:
98
+ return False
@@ -0,0 +1,82 @@
1
+ # -*- coding: utf-8 -*-
2
+ """vscode cli.ipynb
3
+
4
+ Automatically generated by Colab.
5
+
6
+ Original file is located at
7
+ https://colab.research.google.com/drive/1PYNYxzD2gBAY6QiMm5a9O_gHod0q5aOC
8
+ """
9
+
10
+ import os
11
+ from google.colab import drive
12
+
13
+ # CẤU HÌNH THÔNG TIN
14
+ # Public clone URL; Git handles any configured credentials.
15
+ REPO_OWNER = "theflyingkhui04"
16
+ REPO_NAME = "do-an-ptit-d22"
17
+ TARGET_DIR = f"/root/{REPO_NAME}"
18
+ CLONE_URL = f"https://github.com/{REPO_OWNER}/{REPO_NAME}.git"
19
+
20
+ VSCODE_DRIVE_BACKUP = "/content/drive/MyDrive/vllm_colab_backup"
21
+
22
+
23
+ # BƯỚC 1: KẾT NỐI GOOGLE DRIVE
24
+ print("\n" + "="*60)
25
+ print("==> BƯỚC 1/4: KẾT NỐI GOOGLE DRIVE")
26
+ print("="*60)
27
+ drive.mount('/content/drive', force_remount=False)
28
+ os.makedirs(f"{VSCODE_DRIVE_BACKUP}/.vscode-cli", exist_ok=True)
29
+
30
+
31
+ # BƯỚC 2: ĐỒNG BỘ MÃ NGUỒN REPO
32
+ print("\n" + "="*60)
33
+ print("==> BƯỚC 2/4: ĐỒNG BỘ MÃ NGUỒN REPO TỪ GITHUB")
34
+ print("="*60)
35
+ if not os.path.exists(TARGET_DIR):
36
+ !git clone {CLONE_URL} {TARGET_DIR}
37
+ else:
38
+ !git -C {TARGET_DIR} pull
39
+
40
+ !chmod +x {TARGET_DIR}/scripts/*.sh
41
+
42
+
43
+ # BƯỚC 3: CÀI ĐẶT MÔI TRƯỜNG vLLM
44
+ print("\n" + "="*60)
45
+ print("==> BƯỚC 3/4: CÀI ĐẶT vLLM TRÊN SSD COLAB")
46
+ print("="*60)
47
+ !pip install -r {TARGET_DIR}/requirements-server.txt
48
+
49
+ print("\n--> Đang gỡ bỏ torchaudio để tránh xung đột CUDA...")
50
+ !pip uninstall -y torchaudio
51
+
52
+
53
+
54
+ # BƯỚC 4: THIẾT LẬP VS CODE CLI & KHỞI CHẠY TUNNEL
55
+ print("\n" + "="*60)
56
+ print("==> BƯỚC 4/4: KHỞI TẠO VS CODE TUNNEL 'colab-t4'")
57
+ print("="*60)
58
+
59
+ # 1. Tải CLI nếu chưa có (Sử dụng Direct Link của Microsoft để tránh lỗi DNS/Redirect trên T4 GPU)
60
+ if os.path.exists("./code"):
61
+ print("--> Found existing VS Code CLI, removing for fresh install...")
62
+ !rm -rf ./code
63
+ if os.path.exists("vscode_cli.tar.gz"):
64
+ print("--> Found existing vscode_cli.tar.gz, removing for fresh download...")
65
+ !rm -f vscode_cli.tar.gz
66
+
67
+ print("--> Downloading and extracting VS Code CLI (Direct Link)...")
68
+ !wget -O vscode_cli.tar.gz "https://update.code.visualstudio.com/latest/cli-alpine-x64/stable" && tar -xf vscode_cli.tar.gz
69
+
70
+ # 2. Liên kết thư mục lưu trữ thông tin đăng nhập với Drive
71
+ os.makedirs(f"{VSCODE_DRIVE_BACKUP}/.vscode-cli", exist_ok=True)
72
+ !ln -sfn {VSCODE_DRIVE_BACKUP}/.vscode-cli /root/.vscode-cli
73
+
74
+ # 3. Đăng nhập với cờ cố định là GitHub
75
+ print("--> Kiểm tra đăng nhập GitHub:")
76
+ !./code tunnel user login --provider github
77
+
78
+ # 4. Bật Tunnel
79
+ print("\n" + "="*60)
80
+ print(" ĐANG KHỞI CHẠY TUNNEL 'colab-t4'...")
81
+ print("="*60 + "\n")
82
+ !./code tunnel --accept-server-license-terms --name colab-t4
@@ -0,0 +1,204 @@
1
+ Metadata-Version: 2.4
2
+ Name: flexsds
3
+ Version: 0.2.0
4
+ Summary: Flexible Speculative Decoding Serving Engine for LLMs
5
+ Author: Can Duc Khoi, Bui Duc Dai, Nguyen The Duong
6
+ Requires-Python: >=3.9
7
+ Description-Content-Type: text/markdown
8
+ Requires-Dist: psutil
9
+ Provides-Extra: dev
10
+ Requires-Dist: pytest>=8.0; extra == "dev"
11
+
12
+ <div align="center">
13
+
14
+ <img src="logo/flexsds_banner.gif" alt="PTIT - FlexSDS Banner" width="100%" />
15
+
16
+ # FlexSDS: Hệ thống phục vụ suy luận phỏng đoán thích ứng nhận biết phần cứng
17
+
18
+ **Bộ công cụ phục vụ suy luận phỏng đoán thông minh cho các mô hình ngôn ngữ lớn tự lưu trữ**
19
+ *Hardware-Aware Adaptive Speculative Decoding Serving Toolkit for Self-Hosted LLMs*
20
+
21
+ [![PTIT](https://img.shields.io/badge/PTIT-Đồ%20án%20tốt%20nghiệp%20D22-ED1C24?style=for-the-badge&logoColor=white)](https://portal.ptit.edu.vn/)
22
+ [![Python](https://img.shields.io/badge/Python-3.11%20%7C%203.13-3776AB?style=for-the-badge&logo=python&logoColor=white)](https://python.org)
23
+ [![vLLM](https://img.shields.io/badge/vLLM-0.30.0%20Core%20V1-blue?style=for-the-badge)](https://github.com/vllm-project/vllm)
24
+ [![Modal](https://img.shields.io/badge/Modal-GPU%20L4%20Serverless-00C7B7?style=for-the-badge)](https://modal.com)
25
+ [![Status](https://img.shields.io/badge/Trạng%20thái-Sprint%205%20%2F%20M4%20Freeze-brightgreen?style=for-the-badge)]()
26
+
27
+ <br/>
28
+
29
+ <picture>
30
+ <source media="(prefers-color-scheme: dark)" srcset="logo/icon_dark.webp">
31
+ <source media="(prefers-color-scheme: light)" srcset="logo/icon_white.webp">
32
+ <img alt="FlexSDS Logo" src="logo/icon_dark.webp" width="90" height="90">
33
+ </picture>
34
+
35
+ </div>
36
+
37
+ ---
38
+
39
+ ## 1. Tổng quan đề tài
40
+
41
+ Trong các hệ thống xử lý tài liệu tự động và chuỗi tác vụ đa bước (multi-step LLM pipeline), **độ trễ sinh từ trong pha giải mã (Decode Latency)** là nút thắt cổ chai lớn nhất và bị tích lũy lũy tiến qua từng lời gọi mô hình. Kỹ thuật suy luận phỏng đoán truyền thống (Static Speculative Decoding) sử dụng cấu hình nháp cố định thường gặp phải hai rào cản nghiêm trọng trong môi trường triển khai thực tế:
42
+ 1. **Hiện tượng tăng tốc âm (Negative Speedup):** Khi gặp các tác vụ suy luận tự do có entropy cao hoặc khi hệ thống chịu tải đồng thời lớn ($c \ge 16$), việc ép chạy mô hình nháp vô ích biến nó thành tải ký sinh, làm giảm tốc độ thực tế (chỉ đạt $0.67\times - 0.81\times$ so với suy luận tuần tự cơ sở).
43
+ 2. **Nguy cơ tràn bộ nhớ (Out-Of-Memory - OOM):** Việc đồng thời nạp cả mô hình đích, mô hình nháp và phân bổ bộ đệm KV dễ dẫn đến tràn VRAM trên các dòng GPU phổ thông (NVIDIA Tesla T4 16GB, RTX 4090 24GB).
44
+
45
+ **FlexSDS** (*Flexible Speculative Decoding Serving*) là giải pháp mã nguồn mở hoàn chỉnh do nhóm sinh viên PTIT nghiên cứu và phát triển. Hệ thống tích hợp lớp điều khiển thích ứng **TreeAdaptive**, tự động nhận diện phần cứng, nạp ma trận chi phí thực nghiệm và giải ngưỡng hòa vốn động $\alpha^*(N, B)$ theo thời gian thực để co giãn cấu trúc cây nháp hoặc ngắt về suy luận tuần tự cơ sở, **bảo đảm tăng tốc tối đa nhưng không bao giờ bị chậm hơn chế độ mặc định (Lossless Guarantee)**.
46
+
47
+ ---
48
+
49
+ ## 2. Ứng dụng thực nghiệm: Luồng đa tác vụ trên hóa đơn bán hàng
50
+
51
+ Để kiểm chứng năng lực thích ứng trong môi trường thực tế, FlexSDS được triển khai phục vụ quy trình xử lý và phân tích hóa đơn bán hàng tiếng Việt (bộ dữ liệu MC-OCR) với 3 tác vụ đại diện:
52
+
53
+ ```text
54
+ ┌────────────────────────────────────────────────────────────────────────┐
55
+ │ HỆ THỐNG ĐA TÁC VỤ TRÊN HÓA ĐƠN BÁN HÀNG │
56
+ ├────────────────────────────────────────────────────────────────────────┤
57
+ │ [Ảnh / Thư mục hóa đơn tải lên] │
58
+ │ │ │
59
+ │ ▼ │
60
+ │ ┌────────────────────────────────────────────────────────────────────┐ │
61
+ │ │ TÁC VỤ 1: TRÍCH XUẤT NỘI DUNG HÓA ĐƠN THÀNH JSON (MC-OCR W1) │ │
62
+ │ │ • Trích xuất 7 trường pháp lý: Người bán, MST, Ngày, Tiền, VAT... │ │
63
+ │ │ • Cú pháp JSON cố định, alpha > 90% -> Tăng tốc tối đa (1.35x-1.4x) │ │
64
+ │ └──────────────────────────────────┬─────────────────────────────────┘ │
65
+ │ │ │
66
+ │ ▼ │
67
+ │ ┌────────────────────────────────────────────────────────────────────┐ │
68
+ │ │ TÁC VỤ 2: SINH MÃ SQL TRUY VẤN DỮ LIỆU HÓA ĐƠN (TEXT-TO-SQL W2) │ │
69
+ │ │ • Chuyển câu hỏi quản trị kinh doanh tiếng Việt sang truy vấn SQL │ │
70
+ │ │ • Cú pháp SQL bán cấu trúc, alpha ~ 80% -> Tăng tốc ổn định 1.3x │ │
71
+ │ └──────────────────────────────────┬─────────────────────────────────┘ │
72
+ │ │ │
73
+ │ ▼ │
74
+ │ ┌────────────────────────────────────────────────────────────────────┐ │
75
+ │ │ TÁC VỤ 3: HỎI ĐÁP & PHÂN TÍCH TÀI CHÍNH THÔNG MINH (INVOICE CHAT) │ │
76
+ │ │ • Khung chat tương tác trực tiếp về các hóa đơn vừa xử lý │ │
77
+ │ │ • Văn bản tự do, alpha < 60% -> Tự ngắt nháp (k=0), tránh trễ âm │ │
78
+ │ └────────────────────────────────────────────────────────────────────┘ │
79
+ └────────────────────────────────────────────────────────────────────────┘
80
+ ```
81
+
82
+ ---
83
+
84
+ ## 3. Các đột phá kỹ thuật nổi bật
85
+
86
+ * **Lớp thích ứng nhận biết phần cứng (Hardware-Aware TreeAdaptive v0.2):** Tự động nhận diện kiến trúc GPU (`sm_89` NVIDIA L4, `sm_75` Tesla T4) và giải nghiệm phương trình hòa vốn theo thời gian thực:
87
+ $$\alpha^*(N, B) = \arg_\alpha \left( \frac{\mathbb{E}[\tau(N, \alpha)]}{C(N, B)} = \frac{1}{C(0, B)} \right)$$
88
+ * **Triệt tiêu hoàn toàn hiện tượng tăng tốc âm:** Khi tỷ lệ chấp nhận thực tế $\alpha < \alpha^*$ hoặc khi bộ đệm KV vượt ngưỡng $90\%$, hệ thống lập tức ngắt suy đoán về chế độ an toàn ($N=0$), bảo đảm tốc độ luôn $\ge 1.00\times$.
89
+ * **Bộ gác cổng an toàn tương thích (Pre-flight Guard):** Quét dung lượng VRAM thực tế, kiểm tra độ lệch từ vựng (Heterogeneous Vocabulary) giữa mô hình đích và mô hình nháp trước khi khởi động tiến trình serving.
90
+ * **Đa nền tảng phục vụ:** Hỗ trợ khởi chạy server vLLM cục bộ (Local Dedicated Runner) hoặc triển khai tức thì lên nền tảng đám mây không máy chủ (Modal Cloud GPU L4) với chuẩn giao tiếp OpenAI API `/v1/chat/completions`.
91
+ * **Giao diện dòng lệnh tương tác (Interactive CLI Wizard):** Menu hỏi–đáp từng bước có nhận diện thương hiệu PTIT bằng ký tự khối Unicode và mã màu ANSI TrueColor 24-bit.
92
+
93
+ ---
94
+
95
+ ## 4. Bốn chế độ suy luận đối chứng (4 Serving Lanes)
96
+
97
+ Hệ thống cung cấp cơ chế đo kiểm thực nghiệm chuẩn hóa giữa 4 làn phục vụ trên cùng một hạ tầng phần cứng:
98
+
99
+ 1. **Làn 1 (`vanilla`):** Giải mã tuần tự cơ sở của mô hình đích Qwen2.5-7B-Instruct (chuẩn đối chứng $1.00\times$).
100
+ 2. **Làn 2 (`static`):** Suy luận phỏng đoán tĩnh với mô hình nháp độc lập Qwen2.5-0.5B-Instruct ($k=3$ cố định).
101
+ 3. **Làn 3 (`adaptive`):** Suy luận phỏng đoán thích ứng động TreeAdaptive co giãn ngân sách nháp theo tải và $\alpha$.
102
+ 4. **Làn 4 (`eagle`):** Đầu nháp suy đoán chuyên biệt EAGLE-3.1 tự huấn luyện cho miền hóa đơn tiếng Việt (`duckhoi/qwen2.5-7b-eagle3.1-speculator`).
103
+
104
+ ---
105
+
106
+ ## 5. Hướng dẫn cài đặt và sử dụng nhanh
107
+
108
+ ### 5.1. Cài đặt package
109
+
110
+ ```bash
111
+ # Clone kho mã nguồn từ GitHub
112
+ git clone https://github.com/theflyingkhui04/do-an-ptit-d22.git
113
+ cd do-an-ptit-d22
114
+
115
+ # Cài đặt môi trường ở chế độ phát triển (Editable Mode)
116
+ pip install -e .
117
+ ```
118
+
119
+ ### 5.2. Khởi chạy giao diện dòng lệnh tương tác (Interactive Wizard)
120
+
121
+ Chỉ cần gõ lệnh `flexsds` trên terminal để khởi động trợ lý hướng dẫn từng bước:
122
+
123
+ ```bash
124
+ flexsds
125
+ ```
126
+
127
+ Trợ lý sẽ tự động:
128
+ 1. Quét thông số phần cứng GPU và hiển thị banner nhận diện thương hiệu PTIT.
129
+ 2. Hướng dẫn chọn tác vụ, mô hình đích (Target), mô hình nháp (Draft).
130
+ 3. Chạy Pre-flight Guard kiểm tra an toàn VRAM và xung đột bảng từ vựng.
131
+ 4. Tự động nạp ma trận chi phí thực nghiệm và khởi chạy server phục vụ.
132
+
133
+ ### 5.3. Sử dụng lệnh trực tiếp (Direct CLI Commands)
134
+
135
+ ```bash
136
+ # Quét nhanh phần cứng và trạng thái VRAM
137
+ flexsds detect
138
+
139
+ # Khởi chạy endpoint serving trên nền tảng Modal Cloud
140
+ flexsds serve --backend modal --target Qwen/Qwen2.5-7B-Instruct-AWQ --draft Qwen/Qwen2.5-0.5B-Instruct
141
+
142
+ # Khởi chạy server phục vụ cục bộ trên GPU máy chủ
143
+ flexsds serve --backend local --port 8000 --gpu-memory-utilization 0.90
144
+ ```
145
+
146
+ ---
147
+
148
+ ## 6. Web demo và bảng đo lường metrics đối chứng
149
+
150
+ Hệ thống tích hợp giao diện web demo trực quan hỗ trợ:
151
+ * Tải lên một hoặc nhiều ảnh hóa đơn (kéo thả cả thư mục hoặc chụp ảnh trực tiếp).
152
+ * Lựa chọn 3 tác vụ xử lý độc lập: Trích xuất JSON, Sinh câu truy vấn SQL, hoặc Chat hỏi đáp tài chính.
153
+ * **Thiết lập đa endpoint song song:** Cho phép thêm URL của nhiều server (Vanilla vs. Static vs. FlexSDS Adaptive) để chạy đồng thời cùng một dữ liệu đầu vào.
154
+ * **Bảng so sánh metrics thời gian thực:**
155
+ * **TPOT (Time per Output Token - ms/token):** Đo tốc độ sinh từ thực tế của từng endpoint.
156
+ * **Throughput (tokens/s):** Tốc độ xả token ra màn hình.
157
+ * **Tỉ lệ chấp nhận nháp $\alpha$ (%):** Phản ánh đặc trưng ngữ nghĩa của tác vụ.
158
+ * **Trạng thái thích ứng:** Huy hiệu hiển thị quyết định co giãn cây của FlexSDS (`[Boost: k=4]`, `[Balanced: k=2]`, hoặc `[Fallback: k=0]`).
159
+ * **Hệ số tăng tốc (Speedup vs. Vanilla):** Trực quan hóa hiện tượng tăng tốc âm của chế độ tĩnh và khả năng duy trì tốc độ an toàn của FlexSDS.
160
+
161
+ ---
162
+
163
+ ## 7. Cấu trúc kho mã nguồn
164
+
165
+ ```text
166
+ do-an-ptit-d22/
167
+ ├── logo/ # Tài nguyên hình ảnh, logo PTIT và banner GIF
168
+ │ ├── flexsds_banner.gif # Banner động độ phân giải cao hiển thị trên README
169
+ │ ├── flexsds_banner_static.webp # Banner tĩnh
170
+ │ ├── icon_dark.webp # Logo biểu tượng chế độ nền tối (Dark mode)
171
+ │ └── icon_white.webp # Logo biểu tượng chế độ nền sáng (Light mode)
172
+ ├── pyproject.toml # Cấu hình package Python và entrypoint dòng lệnh flexsds
173
+ ├── src/
174
+ │ ├── flexsds/ # Gói mã nguồn công cụ FlexSDS
175
+ │ │ ├── cli/ # Giao diện CLI, Interactive Wizard và banner ANSI
176
+ │ │ ├── core/ # Bộ quét phần cứng, bộ gác cổng an toàn & TreeAdaptive
177
+ │ │ ├── profiles/ # Danh mục ma trận chi phí thực nghiệm (GPU L4 & T4)
178
+ │ │ └── server/ # Bộ điều phối runner (Modal serverless & Local vLLM)
179
+ │ ├── server/ # Bộ lập lịch thích ứng vLLM AdaptiveAsyncScheduler
180
+ │ ├── proposer/ # Bộ điều khiển nháp và mô hình chi phí thực nghiệm
181
+ │ └── pipeline/ # Đường ống xử lý hóa đơn MC-OCR (JSON, SQL, Report)
182
+ ├── experiments/ # Bộ công cụ đo benchmark 4 làn và kịch bản thực nghiệm
183
+ ├── docs/ # Tài liệu kỹ thuật, quy định thiết kế và outline báo cáo
184
+ └── tests/ # Bộ kiểm thử tự động toàn diện (unit & regression tests)
185
+ ```
186
+
187
+ ---
188
+
189
+ ## 8. Đội ngũ thực hiện
190
+
191
+ Nhóm áp dụng mô hình **Ghép cặp Luân phiên 70/30 (Pair-Rotation System)**: Mỗi thành viên chủ trì (Lead) 70% mảng chuyên môn cốt lõi, đồng thời luân phiên đồng hành (Partner) 30% ở các mảng còn lại:
192
+
193
+ | Chuyên môn cốt lõi (Lead 70%) | Thành viên thực hiện | Vai trò trong dự án |
194
+ | :--- | :--- | :--- |
195
+ | **AI Systems & Core-Engine** | **Cấn Đức Khôi** ([@theflyingkhui04](https://github.com/theflyingkhui04)) | Lead Architecture & Core vLLM Scheduler |
196
+ | **MLOps & Infrastructure** | **Bùi Đức Đại** | Lead Benchmark Harness & GPU Telemetry |
197
+ | **AI Application & Data** | **Dương Nguyễn Thế** | Lead Pipeline MC-OCR & Invoices Analytics |
198
+ | **Giảng viên hướng dẫn** | *(Cập nhật theo quyết định của khoa)* | Cố vấn khoa học & Giám sát chất lượng |
199
+
200
+ ---
201
+
202
+ ## 9. Bản quyền
203
+
204
+ Dự án được phát triển phục vụ đồ án tốt nghiệp đại học tại **Học viện Công nghệ Bưu chính Viễn thông (PTIT)**, niên khóa 2022–2026. Mã nguồn được phát hành theo giấy phép mã nguồn mở Apache 2.0.