elismasilva commited on
Commit
b022028
·
1 Parent(s): ed804aa

first commit

Browse files
Files changed (4) hide show
  1. .gitignore +4 -0
  2. README.md +2 -2
  3. app.py +177 -4
  4. requirements.txt +12 -0
.gitignore ADDED
@@ -0,0 +1,4 @@
 
 
 
 
 
1
+ .env
2
+ .vscode
3
+ __pycache__
4
+ models/
README.md CHANGED
@@ -1,11 +1,11 @@
1
  ---
2
- title: Dod Llm Server
3
  emoji: 🔥
4
  colorFrom: pink
5
  colorTo: gray
6
  sdk: gradio
7
  sdk_version: 6.17.3
8
- python_version: '3.12'
9
  app_file: app.py
10
  pinned: false
11
  license: mit
 
1
  ---
2
+ title: DOD LLM Server
3
  emoji: 🔥
4
  colorFrom: pink
5
  colorTo: gray
6
  sdk: gradio
7
  sdk_version: 6.17.3
8
+ python_version: '3.10'
9
  app_file: app.py
10
  pinned: false
11
  license: mit
app.py CHANGED
@@ -1,7 +1,180 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
  import gradio as gr
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2
 
3
- def greet(name):
4
- return "Hello " + name + "!!"
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
5
 
6
- demo = gr.Interface(fn=greet, inputs="text", outputs="text")
7
- demo.launch()
 
1
+ # Dynamic import & Mock system for Hugging Face 'spaces' package
2
+ try:
3
+ import spaces
4
+ has_spaces = True
5
+ except ImportError:
6
+ has_spaces = False
7
+ class spaces:
8
+ @staticmethod
9
+ def GPU(duration=None):
10
+ def decorator(f):
11
+ return f
12
+ return decorator
13
+ import os
14
+ import json
15
+ import multiprocessing
16
+
17
+ # Load .env locally if present
18
+ from dotenv import load_dotenv
19
+ load_dotenv()
20
+
21
  import gradio as gr
22
+ from llama_cpp import Llama
23
+ from huggingface_hub import hf_hub_download
24
+
25
+ # Download GGUF Model on startup from Hugging Face Hub
26
+ def download_nemotron_gguf():
27
+ repo_id = "nvidia/NVIDIA-Nemotron-3-Nano-4B-GGUF"
28
+ filename = "NVIDIA-Nemotron3-Nano-4B-Q4_K_M.gguf"
29
+ local_dir = "./models"
30
+ os.makedirs(local_dir, exist_ok=True)
31
+ return hf_hub_download(repo_id=repo_id, filename=filename, local_dir=local_dir, local_dir_use_symlinks=False)
32
+
33
+ MODEL_PATH = download_nemotron_gguf()
34
+
35
+ # Smart Environment Detection for GPU layers offloading
36
+ if os.environ.get("SPACE_ID"):
37
+ GPU_LAYERS = -1
38
+ PORT=7860
39
+ CPU_THREADS = 2
40
+ else:
41
+ GPU_LAYERS = -1 if os.name == "nt" else 0
42
+ PORT=7880
43
+ IP_ADDRESS="0.0.0.0"
44
+ # Local Windows/Linux uses half of available CPU cores
45
+ CPU_THREADS = max(1, (multiprocessing.cpu_count() or 4) // 2)
46
+
47
+ global_llm = None
48
+
49
+ # Default sandbox prompt templates
50
+ SANDBOX_SYS_PROMPT = """You are "DOD-UNO-BOT", an AI game agent playing a software engineering themed UNO game.
51
+ Analyze the active card, hand, and server metrics (Resolution and Panic) to decide your next strategic move."""
52
+
53
+ SANDBOX_USER_PAYLOAD = '{"active_card": {"stack": "red"}, "metrics": {"resolution": 40, "panic": 20}, "hand": [{"index": 0, "stack": "red", "playable": true}]}'
54
+
55
+ # --- SECURE GPU RUNNER METHOD ---
56
+ @spaces.GPU(duration=60)
57
+ def gpu_inference_runner(system_prompt, user_payload, temperature, max_tokens, grammar_schema=None):
58
+ global global_llm
59
+
60
+ if global_llm is None:
61
+ print(f"Loading DOD LLM Engine: {MODEL_PATH} (GPU Layers: {GPU_LAYERS}, Threads: {CPU_THREADS})", flush=True)
62
+ global_llm = Llama(
63
+ model_path=MODEL_PATH,
64
+ n_gpu_layers=GPU_LAYERS,
65
+ verbose=True,
66
+
67
+ # Context and Batch Tuning
68
+ n_ctx=3072, # Optimized context window
69
+ n_batch=512, # Standard batch size for high-speed prompt ingestion
70
+
71
+ # Thread Mapping (Optimized dynamically to match physical environment cores)
72
+ n_threads=CPU_THREADS,
73
+ n_threads_batch=CPU_THREADS,
74
+
75
+ # Memory Safeguards
76
+ use_mlock=False,
77
+ use_mmap=True, # FIX: Must be True on cloud filesystems to prevent heavy I/O disk bottlenecks!
78
+ flash_attn=True,
79
+
80
+ # Advanced KV Cache Quantization
81
+ # 8 represents GGML_TYPE_Q8_0 (8-bit quantization for Key/Value cache)
82
+ type_k=8, # Quantize Key cache to 8-bit, reducing bandwidth pressure by 50%
83
+ type_v=8,
84
+ )
85
+
86
+ try:
87
+ kwargs = {
88
+ "messages": [
89
+ {"role": "system", "content": system_prompt},
90
+ {"role": "user", "content": user_payload}
91
+ ],
92
+ "temperature": float(temperature),
93
+ "max_tokens": int(max_tokens)
94
+ }
95
+ if grammar_schema:
96
+ kwargs["response_format"] = {
97
+ "type": "json_object",
98
+ "schema": grammar_schema
99
+ }
100
+
101
+ response = global_llm.create_chat_completion(**kwargs)
102
+ return response["choices"][0]["message"]["content"]
103
+ except Exception as e:
104
+ raise RuntimeError(f"Llama engine crash: {str(e)}")
105
+
106
+ # --- MANUAL TEST BENCH INTERFACES ---
107
+ def ui_test_inference(api_key, system_prompt, user_payload, temperature, grammar_schema=None):
108
+ """Gradio handler to manually test the GPU model, verifying the secret key entered on the screen."""
109
+ expected_token = os.environ.get("LLM_API_KEY")
110
+ if expected_token and api_key != expected_token:
111
+ return "❌ Error: Unauthorized. The LLM_API_KEY token you entered is invalid!"
112
+
113
+ parsed_schema = None
114
+ if grammar_schema:
115
+ try:
116
+ if isinstance(grammar_schema, str):
117
+ parsed_schema = json.loads(grammar_schema)
118
+ else:
119
+ parsed_schema = grammar_schema
120
+ except Exception:
121
+ pass
122
+
123
+ try:
124
+ result = gpu_inference_runner(system_prompt, user_payload, temperature, 200, parsed_schema)
125
+ return result
126
+ except Exception as e:
127
+ return f"❌ Execution Error: {str(e)}"
128
 
129
+ # Define the local UI elements
130
+ with gr.Blocks() as demo:
131
+ gr.Markdown("# 🚀 DOD UNO - Dedicated GPU Inference Node")
132
+ gr.Markdown("Secure, hardware-accelerated serverless API endpoint backing DOD UNO Game Server.")
133
+
134
+ with gr.Tab("🔧 API Test Bench"):
135
+ gr.Markdown("### Validate the GPU Model manually by entering the secret API key:")
136
+ grammar_input = gr.Textbox(visible=False, value="")
137
+ with gr.Row():
138
+ api_key_input = gr.Textbox(
139
+ label="LLM_API_KEY (Token)",
140
+ type="password",
141
+ placeholder="Paste your secret handshake key here..."
142
+ )
143
+
144
+ with gr.Row():
145
+ sys_prompt_input = gr.Textbox(
146
+ label="System Prompt",
147
+ value=SANDBOX_SYS_PROMPT,
148
+ lines=4
149
+ )
150
+ user_payload_input = gr.Textbox(
151
+ label="User Payload (JSON / Text)",
152
+ value=SANDBOX_USER_PAYLOAD,
153
+ lines=4
154
+ )
155
+
156
+ with gr.Row():
157
+ temp_slider = gr.Slider(
158
+ minimum=0.1,
159
+ maximum=1.0,
160
+ value=0.1,
161
+ step=0.1,
162
+ label="Temperature"
163
+ )
164
+ test_btn = gr.Button("⚡ Run GPU Inference", variant="primary")
165
+
166
+ output_box = gr.Textbox(
167
+ label="Inference Result (JSON Output)",
168
+ lines=6,
169
+ placeholder="Result will appear here..."
170
+ )
171
+
172
+ test_btn.click(
173
+ fn=ui_test_inference,
174
+ inputs=[api_key_input, sys_prompt_input, user_payload_input, temp_slider, grammar_input],
175
+ outputs=[output_box],
176
+ api_name="generate_inference"
177
+ )
178
 
179
+ # Launch instance
180
+ demo.launch(server_name="0.0.0.0", server_port=PORT)
requirements.txt ADDED
@@ -0,0 +1,12 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ --extra-index-url https://download.pytorch.org/whl/cu126
2
+ torch==2.8.0
3
+
4
+ gradio
5
+ huggingface_hub
6
+
7
+ --extra-index-url https://abetlen.github.io/llama-cpp-python/whl/cu125
8
+ llama-cpp-python==0.3.27
9
+
10
+ hf_xet
11
+ flash-attn @ https://huggingface.co/DEVAIEXP/wheels/resolve/main/flash_attn-2.8.2-cp310-cp310-win_amd64.whl ; sys_platform == 'win32'
12
+ flash-attn @ https://huggingface.co/DEVAIEXP/wheels/resolve/main/flash_attn-2.8.2-cp310-cp310-linux_x86_64.whl ; sys_platform == 'linux'