Gaston895 commited on
Commit
1cd3db1
·
verified ·
1 Parent(s): ca9c3b8

Upload app.py

Browse files
Files changed (1) hide show
  1. app.py +45 -115
app.py CHANGED
@@ -9,9 +9,10 @@ from huggingface_hub import hf_hub_download
9
  app = Flask(__name__)
10
  CORS(app)
11
 
12
- # Qwen3-14B Claude 4.5 Opus High Reasoning Distill Model
13
- MODEL_REPO = "Gaston895/Qwen3-14B-Claude-4.5-Opus-High-Reasoning-Distill-GGUF"
14
- MODEL_FILE = "Qwen3-14B-Claude-4.5-Opus-Distill.q4_k_m.gguf"
 
15
 
16
  model = None
17
  loading_error = None
@@ -22,7 +23,7 @@ def load_model():
22
  # Use HF_TOKEN if your space is private
23
  token = os.environ.get("HF_TOKEN")
24
 
25
- print(f"📥 Downloading model from: {MODEL_REPO}/{MODEL_FILE}...")
26
 
27
  # Download from HuggingFace model repository
28
  model_path = hf_hub_download(
@@ -34,23 +35,18 @@ def load_model():
34
  print(f"✅ Model downloaded to: {model_path}")
35
  print("🏗️ Initializing model engine (llama-cpp)...")
36
 
37
- # Use extremely conservative settings for 14B model stability
38
- # The model is crashing due to memory/batch issues
39
  model = Llama(
40
  model_path=model_path,
41
- n_ctx=1024, # Very small context for stability
42
- n_threads=1, # Single thread to avoid race conditions
43
- n_batch=32, # Very small batch size
44
- n_gpu_layers=0, # Force CPU-only for stability
45
- use_mmap=True,
46
- use_mlock=False,
47
- verbose=False,
48
- seed=42 # Fixed seed for reproducibility
49
  )
50
- print("✅ Model loaded successfully!")
51
  except Exception as e:
52
  loading_error = str(e)
53
- print(f"❌ Error loading model: {e}")
54
 
55
  # Start loading in background
56
  threading.Thread(target=load_model, daemon=True).start()
@@ -62,7 +58,7 @@ def index():
62
  <!DOCTYPE html>
63
  <html>
64
  <head>
65
- <title>OpenGSSTEC AI API</title>
66
  <style>
67
  body { font-family: Arial, sans-serif; max-width: 800px; margin: 50px auto; padding: 20px; }
68
  h1 { color: #333; }
@@ -71,10 +67,14 @@ def index():
71
  .status { padding: 10px; border-radius: 5px; margin: 20px 0; }
72
  .online { background: #d4edda; color: #155724; }
73
  .loading { background: #fff3cd; color: #856404; }
 
74
  </style>
75
  </head>
76
  <body>
77
- <h1>🤖 OpenGSSTEC AI API - Qwen3-14B</h1>
 
 
 
78
  <div class="status """ + ("online" if model else "loading") + """">
79
  Status: """ + ("✅ Online and Ready" if model else "⏳ Loading Model...") + """
80
  </div>
@@ -84,31 +84,21 @@ def index():
84
  <div class="endpoint">
85
  <h3>GET /health</h3>
86
  <p>Check API health and model status</p>
87
- <code>curl https://gsstec-open.hf.space/health</code>
88
  </div>
89
 
90
  <div class="endpoint">
91
  <h3>POST /chat</h3>
92
  <p>Send chat messages to the AI model</p>
93
- <pre><code>curl -X POST https://gsstec-open.hf.space/chat \\
94
- -H "Content-Type: application/json" \\
95
- -d '{
96
- "messages": [
97
- {"role": "user", "content": "Hello!"}
98
- ]
99
- }'</code></pre>
100
  </div>
101
 
102
  <h2>Model Information</h2>
103
  <ul>
104
- <li><strong>Model:</strong> Qwen3-14B Claude 4.5 Opus High Reasoning Distill (Q4_K_M)</li>
105
  <li><strong>Repository:</strong> """ + MODEL_REPO + """</li>
106
- <li><strong>Parameters:</strong> 14B</li>
107
- <li><strong>Context Length:</strong> 8192 tokens (limited to 2048 for stability)</li>
108
- <li><strong>Specialization:</strong> High reasoning and instruction following</li>
109
  </ul>
110
-
111
- <p><a href="/health">Check Health Status →</a></p>
112
  </body>
113
  </html>
114
  """
@@ -120,102 +110,46 @@ def health():
120
  "status": "online" if model else "loading",
121
  "repo": MODEL_REPO,
122
  "file": MODEL_FILE,
123
- "memory_limit": "1GB",
124
  "error": loading_error
125
  })
126
 
127
  @app.route('/chat', methods=['POST'])
128
  def chat():
129
- print("=" * 60)
130
- print("📨 Received /chat request")
131
-
132
  if not model:
133
- print("❌ Model not loaded yet")
134
  return jsonify({"error": "Model still loading"}), 503
135
 
136
  try:
137
- # Log raw request
138
- print(f"📦 Content-Type: {request.content_type}")
139
- print(f"📦 Raw data (first 500 bytes): {request.data[:500]}")
140
-
141
  data = request.json
142
- print(f"📊 Parsed JSON keys: {list(data.keys()) if data else 'None'}")
143
-
144
  messages = data.get('messages', [])
145
- print(f"💬 Message count: {len(messages)}")
146
-
147
- # Calculate approximate token count (rough estimate: 1 token ≈ 4 chars)
148
- total_chars = sum(len(str(msg.get('content', ''))) for msg in messages)
149
- estimated_tokens = total_chars // 4
150
- print(f"📊 Estimated input tokens: {estimated_tokens}")
151
 
152
- # If too large, truncate conversation history but keep system prompt
153
- MAX_INPUT_TOKENS = 500 # Much smaller for 14B model stability
154
- if estimated_tokens > MAX_INPUT_TOKENS:
155
- print(f"⚠️ Input too large, truncating conversation history...")
156
- # Keep system message and recent messages only
157
- system_msg = messages[0] if messages and messages[0].get('role') == 'system' else None
158
- user_messages = [m for m in messages if m.get('role') != 'system']
159
-
160
- # Keep only last message for maximum stability
161
- keep_count = 1 # Only keep the most recent message
162
- truncated_messages = []
163
- if system_msg:
164
- # Truncate system message if it's too long
165
- system_content = system_msg.get('content', '')
166
- if len(system_content) > 1000: # ~250 tokens
167
- system_content = system_content[:1000] + "\n\n[System prompt truncated for context limit]"
168
- print(f"⚠️ System prompt truncated from {len(system_msg.get('content', ''))} to {len(system_content)} chars")
169
- truncated_messages.append({'role': 'system', 'content': system_content})
170
-
171
- truncated_messages.extend(user_messages[-keep_count:])
172
- messages = truncated_messages
173
- print(f"✂️ Truncated to {len(messages)} messages")
174
-
175
- # Format for Qwen2.5 Instruct (ChatML format)
176
  prompt = ""
177
  for msg in messages:
178
  role = msg.get('role', 'user')
179
  content = msg.get('content', '')
180
- prompt += f"<|im_start|>{role}\n{content}<|im_end|>\n"
181
- prompt += "<|im_start|>assistant\n"
182
-
183
- # Additional safety: Hard limit on prompt length to avoid batch decode errors
184
- MAX_PROMPT_CHARS = 2000 # Much smaller for 14B model stability
185
- if len(prompt) > MAX_PROMPT_CHARS:
186
- print(f"⚠️ Prompt too long ({len(prompt)} chars), truncating to {MAX_PROMPT_CHARS}...")
187
- # Keep the beginning (system prompt) and end (recent context)
188
- keep_start = MAX_PROMPT_CHARS // 2
189
- keep_end = MAX_PROMPT_CHARS // 2
190
- prompt = prompt[:keep_start] + "\n\n[...conversation truncated...]\n\n" + prompt[-keep_end:]
191
- print(f"✂️ Prompt truncated to {len(prompt)} chars")
192
-
193
- print(f"📝 Prompt length: {len(prompt)} chars")
194
- print(f"📝 Prompt preview: {prompt[:200]}...")
195
-
196
- print("🤖 Calling model...")
197
- try:
198
- output = model(
199
- prompt,
200
- max_tokens=200, # Much smaller for stability
201
- temperature=0.3, # Lower temperature for more stable output
202
- top_p=0.8,
203
- stop=["<|im_end|>", "<|endoftext|>"],
204
- echo=False
205
- )
206
- except Exception as model_error:
207
- print(f"❌ Model inference failed: {model_error}")
208
- # Return a fallback response
209
- return jsonify({
210
- "choices": [{
211
- "message": {"role": "assistant", "content": "I apologize, but I'm experiencing technical difficulties. The model is currently unstable. Please try a shorter message or try again later."},
212
- "finish_reason": "error"
213
- }]
214
- }), 503
215
 
216
- print("✅ Model completed")
217
  response_text = output["choices"][0]["text"].strip()
218
- print(f"💬 Response length: {len(response_text)} chars")
219
 
220
  return jsonify({
221
  "choices": [{
@@ -224,11 +158,7 @@ def chat():
224
  }]
225
  })
226
  except Exception as e:
227
- print(f"❌ ERROR: {str(e)}")
228
- print(f"❌ Type: {type(e).__name__}")
229
- import traceback
230
- traceback.print_exc()
231
- return jsonify({"error": str(e), "type": type(e).__name__}), 500
232
 
233
  if __name__ == '__main__':
234
  port = int(os.environ.get('PORT', 7860))
 
9
  app = Flask(__name__)
10
  CORS(app)
11
 
12
+ # Fallback to a smaller, more stable model if the 14B model fails
13
+ # This is a proven stable configuration
14
+ MODEL_REPO = "microsoft/Phi-3-mini-4k-instruct-gguf"
15
+ MODEL_FILE = "Phi-3-mini-4k-instruct-q4.gguf"
16
 
17
  model = None
18
  loading_error = None
 
23
  # Use HF_TOKEN if your space is private
24
  token = os.environ.get("HF_TOKEN")
25
 
26
+ print(f"📥 Downloading fallback model from: {MODEL_REPO}/{MODEL_FILE}...")
27
 
28
  # Download from HuggingFace model repository
29
  model_path = hf_hub_download(
 
35
  print(f"✅ Model downloaded to: {model_path}")
36
  print("🏗️ Initializing model engine (llama-cpp)...")
37
 
38
+ # Use very conservative settings for maximum stability
 
39
  model = Llama(
40
  model_path=model_path,
41
+ n_ctx=2048, # Phi-3 mini works well with 2K context
42
+ n_threads=2,
43
+ n_batch=128,
44
+ verbose=False
 
 
 
 
45
  )
46
+ print("✅ Fallback model loaded successfully!")
47
  except Exception as e:
48
  loading_error = str(e)
49
+ print(f"❌ Error loading fallback model: {e}")
50
 
51
  # Start loading in background
52
  threading.Thread(target=load_model, daemon=True).start()
 
58
  <!DOCTYPE html>
59
  <html>
60
  <head>
61
+ <title>OpenGSSTEC AI API - Fallback Mode</title>
62
  <style>
63
  body { font-family: Arial, sans-serif; max-width: 800px; margin: 50px auto; padding: 20px; }
64
  h1 { color: #333; }
 
67
  .status { padding: 10px; border-radius: 5px; margin: 20px 0; }
68
  .online { background: #d4edda; color: #155724; }
69
  .loading { background: #fff3cd; color: #856404; }
70
+ .warning { background: #f8d7da; color: #721c24; }
71
  </style>
72
  </head>
73
  <body>
74
+ <h1>🤖 OpenGSSTEC AI API - Fallback Mode</h1>
75
+ <div class="status warning">
76
+ ⚠️ Running in fallback mode with Phi-3-mini due to 14B model instability
77
+ </div>
78
  <div class="status """ + ("online" if model else "loading") + """">
79
  Status: """ + ("✅ Online and Ready" if model else "⏳ Loading Model...") + """
80
  </div>
 
84
  <div class="endpoint">
85
  <h3>GET /health</h3>
86
  <p>Check API health and model status</p>
 
87
  </div>
88
 
89
  <div class="endpoint">
90
  <h3>POST /chat</h3>
91
  <p>Send chat messages to the AI model</p>
 
 
 
 
 
 
 
92
  </div>
93
 
94
  <h2>Model Information</h2>
95
  <ul>
96
+ <li><strong>Model:</strong> Phi-3-mini-4k-instruct (Q4)</li>
97
  <li><strong>Repository:</strong> """ + MODEL_REPO + """</li>
98
+ <li><strong>Parameters:</strong> 3.8B</li>
99
+ <li><strong>Context Length:</strong> 2048 tokens</li>
100
+ <li><strong>Status:</strong> Fallback mode - stable and reliable</li>
101
  </ul>
 
 
102
  </body>
103
  </html>
104
  """
 
110
  "status": "online" if model else "loading",
111
  "repo": MODEL_REPO,
112
  "file": MODEL_FILE,
113
+ "mode": "fallback",
114
  "error": loading_error
115
  })
116
 
117
  @app.route('/chat', methods=['POST'])
118
  def chat():
 
 
 
119
  if not model:
 
120
  return jsonify({"error": "Model still loading"}), 503
121
 
122
  try:
 
 
 
 
123
  data = request.json
 
 
124
  messages = data.get('messages', [])
 
 
 
 
 
 
125
 
126
+ # Simple prompt formatting for Phi-3
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
127
  prompt = ""
128
  for msg in messages:
129
  role = msg.get('role', 'user')
130
  content = msg.get('content', '')
131
+ if role == 'system':
132
+ prompt += f"System: {content}\n"
133
+ elif role == 'user':
134
+ prompt += f"User: {content}\n"
135
+ elif role == 'assistant':
136
+ prompt += f"Assistant: {content}\n"
137
+ prompt += "Assistant: "
138
+
139
+ # Keep prompt reasonable
140
+ if len(prompt) > 1500:
141
+ prompt = prompt[-1500:]
142
+
143
+ output = model(
144
+ prompt,
145
+ max_tokens=300,
146
+ temperature=0.7,
147
+ top_p=0.9,
148
+ stop=["User:", "System:"],
149
+ echo=False
150
+ )
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
151
 
 
152
  response_text = output["choices"][0]["text"].strip()
 
153
 
154
  return jsonify({
155
  "choices": [{
 
158
  }]
159
  })
160
  except Exception as e:
161
+ return jsonify({"error": str(e)}), 500
 
 
 
 
162
 
163
  if __name__ == '__main__':
164
  port = int(os.environ.get('PORT', 7860))