Reaperxxxx commited on
Commit
eef786b
·
verified ·
1 Parent(s): 42da973

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +23 -517
app.py CHANGED
@@ -1,522 +1,28 @@
1
- from flask import Flask, request, jsonify
2
- from mistral_inference.transformer import Transformer
3
- from mistral_inference.generate import generate
4
- from mistral_common.tokens.tokenizers.mistral import MistralTokenizer
5
- from mistral_common.protocol.instruct.messages import UserMessage, AssistantMessage, ToolMessage
6
- from mistral_common.protocol.instruct.request import ChatCompletionRequest
7
- from mistral_common.protocol.instruct.tool_calls import Function, Tool
8
- import requests
9
- import json
10
- import os
11
- import threading
12
- import queue
13
- import time
14
- from datetime import datetime
15
- from pathlib import Path
16
- import logging
17
-
18
- # Configure logging
19
- logging.basicConfig(
20
- level=logging.INFO,
21
- format='%(asctime)s - %(name)s - %(levelname)s - %(message)s'
22
  )
23
- logger = logging.getLogger(__name__)
24
-
25
- app = Flask(__name__)
26
-
27
- # Global variables
28
- model = None
29
- tokenizer = None
30
- MODEL_PATH = "/app/models"
31
- MEMORY_FILE = "/app/memory.json"
32
- MAX_MEMORY_ENTRIES_TOTAL = 500 # Total shared memory entries for the group
33
- MAX_MEMORY_CONTEXT = 20 # Number of recent messages to include in context
34
-
35
- # Request queue for handling concurrent requests
36
- request_queue = queue.Queue(maxsize=100)
37
- processing_lock = threading.Lock()
38
-
39
- class MemoryManager:
40
- """Manages shared group memories with size limits"""
41
-
42
- def __init__(self, memory_file):
43
- self.memory_file = memory_file
44
- self.memory_lock = threading.Lock()
45
- self.load_memory()
46
-
47
- def load_memory(self):
48
- """Load memory from file"""
49
- try:
50
- if os.path.exists(self.memory_file):
51
- with open(self.memory_file, 'r') as f:
52
- data = json.load(f)
53
- # Support both old format and new shared format
54
- if isinstance(data, dict) and "entries" in data:
55
- self.memory = data
56
- else:
57
- # Convert old format to new
58
- self.memory = {"entries": [], "users": {}}
59
- else:
60
- self.memory = {"entries": [], "users": {}}
61
- except Exception as e:
62
- logger.error(f"Error loading memory: {e}")
63
- self.memory = {"entries": [], "users": {}}
64
-
65
- def save_memory(self):
66
- """Save memory to file"""
67
- try:
68
- with open(self.memory_file, 'w') as f:
69
- json.dump(self.memory, f, indent=2)
70
- except Exception as e:
71
- logger.error(f"Error saving memory: {e}")
72
-
73
- def add_memory(self, chat_id, username, name, message):
74
- """Add a memory entry to shared group memory"""
75
- with self.memory_lock:
76
- chat_key = str(chat_id)
77
-
78
- # Track user info
79
- if chat_key not in self.memory["users"]:
80
- self.memory["users"][chat_key] = {
81
- "username": username,
82
- "name": name,
83
- "first_seen": datetime.now().isoformat(),
84
- "message_count": 0
85
- }
86
-
87
- # Update user info
88
- self.memory["users"][chat_key]["username"] = username
89
- self.memory["users"][chat_key]["name"] = name
90
- self.memory["users"][chat_key]["message_count"] += 1
91
- self.memory["users"][chat_key]["last_seen"] = datetime.now().isoformat()
92
-
93
- # Add new entry to shared memory
94
- entry = {
95
- "chat_id": chat_key,
96
- "message": message,
97
- "timestamp": datetime.now().isoformat(),
98
- "username": username,
99
- "name": name
100
- }
101
-
102
- self.memory["entries"].append(entry)
103
-
104
- # Trim if too large - keep most recent entries
105
- if len(self.memory["entries"]) > MAX_MEMORY_ENTRIES_TOTAL:
106
- # Keep first 50 (important early context) and last 450 (recent context)
107
- important = self.memory["entries"][:50]
108
- recent = self.memory["entries"][-(MAX_MEMORY_ENTRIES_TOTAL-50):]
109
- self.memory["entries"] = important + recent
110
-
111
- self.save_memory()
112
-
113
- return {
114
- "status": "success",
115
- "total_entries": len(self.memory["entries"]),
116
- "user_message_count": self.memory["users"][chat_key]["message_count"]
117
- }
118
-
119
- def get_memory(self, chat_id):
120
- """Get memory for a specific chat (for compatibility)"""
121
- with self.memory_lock:
122
- chat_key = str(chat_id)
123
- if chat_key in self.memory["users"]:
124
- # Return user-specific view
125
- user_entries = [e for e in self.memory["entries"] if e.get("chat_id") == chat_key]
126
- return {
127
- "username": self.memory["users"][chat_key]["username"],
128
- "name": self.memory["users"][chat_key]["name"],
129
- "entries": user_entries
130
- }
131
- return None
132
-
133
- def get_all_memory(self):
134
- """Get all shared group memory"""
135
- with self.memory_lock:
136
- return self.memory
137
-
138
- def get_memory_summary(self, chat_id=None):
139
- """Get a condensed summary of shared group memories for context"""
140
- with self.memory_lock:
141
- if not self.memory.get("entries"):
142
- return ""
143
-
144
- # Get recent entries for context
145
- recent_entries = self.memory["entries"][-MAX_MEMORY_CONTEXT:]
146
-
147
- summary = f"\n--- Shared Group Memory Context ---\n"
148
- summary += f"Group has {len(self.memory['users'])} members\n"
149
- summary += f"Recent conversation history:\n"
150
-
151
- for entry in recent_entries:
152
- timestamp = entry.get('timestamp', '')[:16] # Date + time
153
- name = entry.get('name', 'Unknown')
154
- username = entry.get('username', 'unknown')
155
- msg = entry.get('message', '')[:150] # Truncate long messages
156
- summary += f"- [{timestamp}] {name} (@{username}): {msg}\n"
157
-
158
- summary += "--- End Memory Context ---\n"
159
- return summary
160
-
161
- def search_memory(self, keyword, chat_id=None):
162
- """Search for specific information in memory"""
163
- with self.memory_lock:
164
- results = []
165
- for entry in self.memory["entries"]:
166
- # If chat_id provided, filter to that user
167
- if chat_id and entry.get("chat_id") != str(chat_id):
168
- continue
169
-
170
- if keyword.lower() in entry["message"].lower():
171
- results.append(entry)
172
-
173
- return results
174
-
175
- def get_user_info(self, chat_id):
176
- """Get information about a specific user"""
177
- with self.memory_lock:
178
- chat_key = str(chat_id)
179
- return self.memory["users"].get(chat_key)
180
 
181
- # Initialize memory manager
182
- memory_manager = MemoryManager(MEMORY_FILE)
183
-
184
- def initialize_model():
185
- """Initialize the Mistral model and tokenizer"""
186
- global model, tokenizer
 
 
187
 
188
- if model is None or tokenizer is None:
189
- logger.info("Loading Mistral model...")
190
- try:
191
- # Download model if not present
192
- tokenizer_path = f"{MODEL_PATH}/tokenizer.model.v3"
193
- if not os.path.exists(tokenizer_path):
194
- logger.info("Model not found. Downloading from HuggingFace...")
195
- logger.info("This will take several minutes on first run...")
196
-
197
- os.makedirs(MODEL_PATH, exist_ok=True)
198
-
199
- import subprocess
200
- result = subprocess.run(
201
- ["git", "clone", "https://huggingface.co/mistralai/Mistral-7B-Instruct-v0.3", MODEL_PATH],
202
- capture_output=True,
203
- text=True,
204
- timeout=1800 # 30 minute timeout
205
- )
206
-
207
- if result.returncode != 0:
208
- logger.error(f"Failed to download model: {result.stderr}")
209
- raise Exception(f"Model download failed: {result.stderr}")
210
-
211
- logger.info("Model downloaded successfully!")
212
-
213
- tokenizer = MistralTokenizer.from_file(tokenizer_path)
214
- model = Transformer.from_folder(MODEL_PATH)
215
- logger.info("Model loaded successfully")
216
- except Exception as e:
217
- logger.error(f"Error loading model: {e}")
218
- raise
219
-
220
- def check_health_api():
221
- """Call the health API"""
222
- try:
223
- response = requests.get("https://bridge.zone.id/api/health", timeout=5)
224
- return response.json()
225
- except Exception as e:
226
- logger.error(f"Health API error: {e}")
227
- return {"error": str(e), "status": "error"}
228
-
229
- def extract_tool_calls(text):
230
- """Extract tool calls from model output more robustly"""
231
- try:
232
- # Look for function call patterns
233
- if "[TOOL_CALLS]" in text or "check_health" in text.lower():
234
- return True
235
- return False
236
- except Exception as e:
237
- logger.error(f"Error extracting tool calls: {e}")
238
- return False
239
-
240
- def process_with_mistral(query, chat_id=None, use_tools=False, max_retries=2):
241
- """Process query with Mistral model with error handling"""
242
 
243
- for attempt in range(max_retries):
244
- try:
245
- initialize_model()
246
-
247
- # Prepare tools if needed
248
- tools = None
249
- if use_tools:
250
- tools = [
251
- Tool(
252
- function=Function(
253
- name="check_health",
254
- description="Check the health status of the bridge.zone.id API service",
255
- parameters={
256
- "type": "object",
257
- "properties": {},
258
- "required": [],
259
- },
260
- )
261
- )
262
- ]
263
-
264
- # Build context with shared group memory
265
- context = ""
266
- memory_context = memory_manager.get_memory_summary(chat_id)
267
- if memory_context:
268
- context = memory_context + "\n"
269
-
270
- # Create user message with context
271
- user_content = context + query if context else query
272
- messages = [UserMessage(content=user_content)]
273
-
274
- completion_request = ChatCompletionRequest(
275
- messages=messages,
276
- tools=tools if use_tools else None
277
- )
278
-
279
- tokens = tokenizer.encode_chat_completion(completion_request).tokens
280
-
281
- # Limit context to prevent overflow
282
- max_context_tokens = 4096 # Adjust based on your model
283
- if len(tokens) > max_context_tokens:
284
- tokens = tokens[-max_context_tokens:]
285
-
286
- with processing_lock: # Ensure single-threaded model access
287
- out_tokens, _ = generate(
288
- [tokens],
289
- model,
290
- max_tokens=512,
291
- temperature=0.7,
292
- eos_id=tokenizer.instruct_tokenizer.tokenizer.eos_id
293
- )
294
-
295
- result = tokenizer.instruct_tokenizer.tokenizer.decode(out_tokens[0])
296
-
297
- # Handle tool calls
298
- if use_tools and extract_tool_calls(result):
299
- logger.info("Tool call detected, executing health check")
300
- health_data = check_health_api()
301
-
302
- # Create follow-up request with tool result
303
- messages.append(AssistantMessage(content=result))
304
- messages.append(ToolMessage(
305
- content=json.dumps(health_data),
306
- tool_call_id="check_health_1"
307
- ))
308
-
309
- completion_request = ChatCompletionRequest(messages=messages)
310
- tokens = tokenizer.encode_chat_completion(completion_request).tokens
311
-
312
- if len(tokens) > max_context_tokens:
313
- tokens = tokens[-max_context_tokens:]
314
-
315
- with processing_lock:
316
- out_tokens, _ = generate(
317
- [tokens],
318
- model,
319
- max_tokens=512,
320
- temperature=0.7,
321
- eos_id=tokenizer.instruct_tokenizer.tokenizer.eos_id
322
- )
323
-
324
- result = tokenizer.instruct_tokenizer.tokenizer.decode(out_tokens[0])
325
-
326
- return {"success": True, "response": result}
327
-
328
- except Exception as e:
329
- logger.error(f"Attempt {attempt + 1} failed: {e}")
330
- if attempt == max_retries - 1:
331
- return {"success": False, "error": str(e)}
332
- time.sleep(1) # Brief pause before retry
333
-
334
- return {"success": False, "error": "Max retries exceeded"}
335
-
336
- @app.route('/api/chat', methods=['POST'])
337
- def chat():
338
- """Main chat endpoint with queue management"""
339
- try:
340
- data = request.get_json()
341
-
342
- if not data or 'query' not in data:
343
- return jsonify({
344
- "error": "Missing 'query' parameter"
345
- }), 400
346
-
347
- query = data['query'].strip()
348
- chat_id = data.get('chat_id')
349
-
350
- if not query:
351
- return jsonify({
352
- "error": "Empty query"
353
- }), 400
354
-
355
- # Check queue size
356
- if request_queue.qsize() >= 95:
357
- return jsonify({
358
- "error": "Server is busy, please try again later"
359
- }), 503
360
-
361
- # Determine if we should use function calling
362
- health_keywords = ['health', 'status', 'api status', 'bridge', 'check service', 'api check']
363
- use_tools = any(keyword in query.lower() for keyword in health_keywords)
364
-
365
- # Process with Mistral
366
- start_time = time.time()
367
- result = process_with_mistral(query, chat_id=chat_id, use_tools=use_tools)
368
- processing_time = time.time() - start_time
369
-
370
- if not result["success"]:
371
- return jsonify({
372
- "error": result.get("error", "Processing failed")
373
- }), 500
374
-
375
- logger.info(f"Processed query in {processing_time:.2f}s")
376
-
377
- return jsonify({
378
- "query": query,
379
- "response": result["response"],
380
- "used_tools": use_tools,
381
- "processing_time": processing_time,
382
- "has_memory": len(memory_manager.memory.get("entries", [])) > 0,
383
- "total_group_memories": len(memory_manager.memory.get("entries", []))
384
- })
385
-
386
- except Exception as e:
387
- logger.error(f"Chat endpoint error: {e}")
388
- return jsonify({
389
- "error": str(e)
390
- }), 500
391
-
392
- @app.route('/api/update-memory', methods=['POST'])
393
- def update_memory():
394
- """Update memory for a user"""
395
- try:
396
- data = request.get_json()
397
-
398
- required_fields = ['chat_id', 'username', 'name', 'message']
399
- if not all(field in data for field in required_fields):
400
- return jsonify({
401
- "error": f"Missing required fields: {required_fields}"
402
- }), 400
403
-
404
- result = memory_manager.add_memory(
405
- chat_id=data['chat_id'],
406
- username=data['username'],
407
- name=data['name'],
408
- message=data['message']
409
- )
410
-
411
- return jsonify(result)
412
-
413
- except Exception as e:
414
- logger.error(f"Update memory error: {e}")
415
- return jsonify({
416
- "error": str(e)
417
- }), 500
418
 
419
- @app.route('/api/get-memory/<chat_id>', methods=['GET'])
420
- def get_memory(chat_id):
421
- """Get memory for a specific user"""
422
- try:
423
- memory_data = memory_manager.get_memory(chat_id)
424
-
425
- if memory_data is None:
426
- return jsonify({
427
- "error": "No memory found for this user"
428
- }), 404
429
-
430
- return jsonify(memory_data)
431
-
432
- except Exception as e:
433
- logger.error(f"Get memory error: {e}")
434
- return jsonify({
435
- "error": str(e)
436
- }), 500
437
-
438
- @app.route('/api/search-memory/<chat_id>', methods=['POST'])
439
- def search_memory(chat_id):
440
- """Search memory for specific information"""
441
- try:
442
- data = request.get_json()
443
-
444
- if not data or 'keyword' not in data:
445
- return jsonify({
446
- "error": "Missing 'keyword' parameter"
447
- }), 400
448
-
449
- # If chat_id is "all", search entire group memory
450
- search_chat_id = None if chat_id == "all" else chat_id
451
- results = memory_manager.search_memory(data['keyword'], chat_id=search_chat_id)
452
-
453
- return jsonify({
454
- "results": results,
455
- "count": len(results)
456
- })
457
-
458
- except Exception as e:
459
- logger.error(f"Search memory error: {e}")
460
- return jsonify({
461
- "error": str(e)
462
- }), 500
463
-
464
- @app.route('/api/get-all-memory', methods=['GET'])
465
- def get_all_memory():
466
- """Get all shared group memory"""
467
- try:
468
- memory_data = memory_manager.get_all_memory()
469
- return jsonify(memory_data)
470
-
471
- except Exception as e:
472
- logger.error(f"Get all memory error: {e}")
473
- return jsonify({
474
- "error": str(e)
475
- }), 500
476
-
477
- @app.route('/api/get-user-info/<chat_id>', methods=['GET'])
478
- def get_user_info(chat_id):
479
- """Get information about a specific user"""
480
- try:
481
- user_info = memory_manager.get_user_info(chat_id)
482
-
483
- if user_info is None:
484
- return jsonify({
485
- "error": "User not found"
486
- }), 404
487
-
488
- return jsonify(user_info)
489
-
490
- except Exception as e:
491
- logger.error(f"Get user info error: {e}")
492
- return jsonify({
493
- "error": str(e)
494
- }), 500
495
-
496
- @app.route('/health', methods=['GET'])
497
- def health():
498
- """Health check endpoint"""
499
- return jsonify({
500
- "status": "ok",
501
- "model_loaded": model is not None and tokenizer is not None,
502
- "queue_size": request_queue.qsize(),
503
- "total_group_members": len(memory_manager.memory.get("users", {})),
504
- "total_group_messages": len(memory_manager.memory.get("entries", []))
505
- })
506
-
507
- @app.route('/api/check-bridge-health', methods=['GET'])
508
- def check_bridge_health():
509
- """Direct endpoint to check bridge API health"""
510
- health_data = check_health_api()
511
- return jsonify(health_data)
512
-
513
- if __name__ == '__main__':
514
- # Initialize model on startup
515
- try:
516
- initialize_model()
517
- except Exception as e:
518
- logger.error(f"Warning: Could not initialize model on startup: {e}")
519
- logger.info("Model will be loaded on first request")
520
-
521
- # Run with threading enabled
522
- app.run(host='0.0.0.0', port=7860, debug=False, threaded=True)
 
1
+ from transformers import AutoModelForCausalLM, AutoTokenizer
2
+
3
+ # Load the model
4
+ model_name = "Qwen/Qwen2.5-0.5B-Instruct" # Or 1.5B, 3B, 7B
5
+ tokenizer = AutoTokenizer.from_pretrained(model_name)
6
+ model = AutoModelForCausalLM.from_pretrained(
7
+ model_name,
8
+ torch_dtype="auto",
9
+ device_map="auto"
 
 
 
 
 
 
 
 
 
 
 
 
10
  )
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
11
 
12
+ # Chat function
13
+ def chat(prompt):
14
+ messages = [{"role": "user", "content": prompt}]
15
+ text = tokenizer.apply_chat_template(
16
+ messages,
17
+ tokenize=False,
18
+ add_generation_prompt=True
19
+ )
20
 
21
+ inputs = tokenizer([text], return_tensors="pt").to(model.device)
22
+ outputs = model.generate(**inputs, max_new_tokens=512)
23
+ response = tokenizer.decode(outputs[0], skip_special_tokens=True)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
24
 
25
+ return response
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
26
 
27
+ # Test it
28
+ print(chat("Hello, how are you?"))