tommytracx commited on
Commit
a14636b
·
verified ·
1 Parent(s): e0c7c62

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +226 -39
app.py CHANGED
@@ -1,3 +1,4 @@
 
1
  from flask import Flask, request, jsonify, render_template_string
2
  import os
3
  import requests
@@ -10,32 +11,33 @@ app = Flask(__name__)
10
  logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s')
11
 
12
  # Configuration
13
- OLLAMA_API_URL = os.getenv('OLLAMA_API_URL', 'https://huggingface.co/spaces/tommytracx/ollama-api')
14
- DEFAULT_MODEL = os.getenv('DEFAULT_MODEL', 'llama2,llama2:13b,llama2:70b,codellama,neural-chat,gemma-3-270m').split(',')
15
  MAX_TOKENS = int(os.getenv('MAX_TOKENS', '2048'))
16
  TEMPERATURE = float(os.getenv('TEMPERATURE', '0.7'))
17
 
18
- class OllamaClient:
19
- def __init__(self, api_url: str):
20
- self.api_url = api_url.rstrip('/')
21
- self.available_models = DEFAULT_MODEL # Initialize with default models
22
  self.refresh_models()
23
 
24
  def refresh_models(self) -> None:
25
- """Refresh the list of available models from the API, falling back to defaults on failure."""
26
  try:
27
- response = requests.get(f"{self.api_url}/api/models", timeout=10)
28
  response.raise_for_status()
29
  data = response.json()
30
- if data.get('status') == 'success' and isinstance(data.get('models'), list):
31
- self.available_models = data['models']
32
- logging.info(f"Successfully fetched models: {self.available_models}")
33
- else:
34
- logging.warning(f"Invalid response format from API: {data}")
35
- self.available_models = DEFAULT_MODEL
 
36
  except Exception as e:
37
  logging.error(f"Error refreshing models: {e}")
38
- self.available_models = DEFAULT_MODEL
39
 
40
  def list_models(self) -> List[str]:
41
  """Return the list of available models."""
@@ -53,18 +55,15 @@ class OllamaClient:
53
  "stream": False,
54
  **kwargs
55
  }
56
- response = requests.post(f"{self.api_url}/api/generate", json=payload, timeout=120)
57
  response.raise_for_status()
58
  data = response.json()
59
-
60
- if data.get('status') == 'success':
61
- return {
62
- "status": "success",
63
- "response": data.get('response', ''),
64
- "model": model_name,
65
- "usage": data.get('usage', {})
66
- }
67
- return {"status": "error", "message": data.get('message', 'Unknown error')}
68
  except Exception as e:
69
  logging.error(f"Error generating response: {e}")
70
  return {"status": "error", "message": str(e)}
@@ -72,15 +71,15 @@ class OllamaClient:
72
  def health_check(self) -> Dict[str, Any]:
73
  """Check the health of the Ollama API."""
74
  try:
75
- response = requests.get(f"{self.api_url}/health", timeout=10)
76
  response.raise_for_status()
77
- return response.json()
78
  except Exception as e:
79
  logging.error(f"Health check failed: {e}")
80
  return {"status": "unhealthy", "error": str(e)}
81
 
82
- # Initialize Ollama client
83
- ollama_client = OllamaClient(OLLAMA_API_URL)
84
 
85
  # HTML template for the chat interface
86
  HTML_TEMPLATE = '''
@@ -91,7 +90,195 @@ HTML_TEMPLATE = '''
91
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
92
  <title>OpenWebUI - Ollama Chat</title>
93
  <style>
94
- /* [Previous CSS unchanged] */
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
95
  </style>
96
  </head>
97
  <body>
@@ -173,7 +360,7 @@ HTML_TEMPLATE = '''
173
  const option = document.createElement('option');
174
  option.value = model;
175
  option.textContent = model;
176
- if (model === '{{ default_model[0] }}') {
177
  option.selected = true;
178
  }
179
  modelSelect.appendChild(option);
@@ -230,7 +417,7 @@ HTML_TEMPLATE = '''
230
  const response = await fetch('/api/chat', {
231
  method: 'POST',
232
  headers: { 'Content-Type': 'application/json' },
233
- body: JSON.stringify({ model, message, temperature, max_tokens: maxTokens })
234
  });
235
  const data = await response.json();
236
 
@@ -297,22 +484,22 @@ HTML_TEMPLATE = '''
297
  @app.route('/')
298
  def home():
299
  """Main chat interface."""
300
- return render_template_string(HTML_TEMPLATE, ollama_api_url=OLLAMA_API_URL, default_model=DEFAULT_MODEL)
301
 
302
  @app.route('/api/chat', methods=['POST'])
303
  def chat():
304
  """Chat API endpoint."""
305
  try:
306
  data = request.get_json()
307
- if not data or 'message' not in data or 'model' not in data:
308
- return jsonify({"status": "error", "message": "Message and model are required"}), 400
309
 
310
- message = data['message']
311
  model = data['model']
312
  temperature = data.get('temperature', TEMPERATURE)
313
  max_tokens = data.get('max_tokens', MAX_TOKENS)
314
 
315
- result = ollama_client.generate(model, message, temperature=temperature, max_tokens=max_tokens)
316
  return jsonify(result), 200 if result["status"] == "success" else 500
317
  except Exception as e:
318
  logging.error(f"Chat endpoint error: {e}")
@@ -322,7 +509,7 @@ def chat():
322
  def get_models():
323
  """Get available models."""
324
  try:
325
- models = ollama_client.list_models()
326
  return jsonify({
327
  "status": "success",
328
  "models": models,
@@ -336,7 +523,7 @@ def get_models():
336
  def health_check():
337
  """Health check endpoint."""
338
  try:
339
- ollama_health = ollama_client.health_check()
340
  return jsonify({
341
  "status": "healthy",
342
  "ollama_api": ollama_health,
 
1
+ # app.py
2
  from flask import Flask, request, jsonify, render_template_string
3
  import os
4
  import requests
 
11
  logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s')
12
 
13
  # Configuration
14
+ OLLAMA_BASE_URL = os.getenv('OLLAMA_BASE_URL', 'http://localhost:11434')
15
+ ALLOWED_MODELS = os.getenv('ALLOWED_MODELS', 'llama2,llama2:13b,llama2:70b,codellama,neural-chat,gemma-3-270m').split(',')
16
  MAX_TOKENS = int(os.getenv('MAX_TOKENS', '2048'))
17
  TEMPERATURE = float(os.getenv('TEMPERATURE', '0.7'))
18
 
19
+ class OllamaManager:
20
+ def __init__(self, base_url: str):
21
+ self.base_url = base_url.rstrip('/')
22
+ self.available_models = ALLOWED_MODELS # Initialize with allowed models
23
  self.refresh_models()
24
 
25
  def refresh_models(self) -> None:
26
+ """Refresh the list of available models from Ollama API, falling back to allowed models."""
27
  try:
28
+ response = requests.get(f"{self.base_url}/api/tags", timeout=10)
29
  response.raise_for_status()
30
  data = response.json()
31
+ models = [model['name'] for model in data.get('models', [])]
32
+ # Filter models to only include those in ALLOWED_MODELS
33
+ self.available_models = [model for model in models if model in ALLOWED_MODELS]
34
+ if not self.available_models:
35
+ self.available_models = ALLOWED_MODELS
36
+ logging.warning("No allowed models found in API response, using ALLOWED_MODELS")
37
+ logging.info(f"Available models: {self.available_models}")
38
  except Exception as e:
39
  logging.error(f"Error refreshing models: {e}")
40
+ self.available_models = ALLOWED_MODELS
41
 
42
  def list_models(self) -> List[str]:
43
  """Return the list of available models."""
 
55
  "stream": False,
56
  **kwargs
57
  }
58
+ response = requests.post(f"{self.base_url}/api/generate", json=payload, timeout=120)
59
  response.raise_for_status()
60
  data = response.json()
61
+ return {
62
+ "status": "success",
63
+ "response": data.get('response', ''),
64
+ "model": model_name,
65
+ "usage": data.get('usage', {})
66
+ }
 
 
 
67
  except Exception as e:
68
  logging.error(f"Error generating response: {e}")
69
  return {"status": "error", "message": str(e)}
 
71
  def health_check(self) -> Dict[str, Any]:
72
  """Check the health of the Ollama API."""
73
  try:
74
+ response = requests.get(f"{self.base_url}/api/tags", timeout=10)
75
  response.raise_for_status()
76
+ return {"status": "healthy", "available_models": len(self.available_models)}
77
  except Exception as e:
78
  logging.error(f"Health check failed: {e}")
79
  return {"status": "unhealthy", "error": str(e)}
80
 
81
+ # Initialize Ollama manager
82
+ ollama_manager = OllamaManager(OLLAMA_BASE_URL)
83
 
84
  # HTML template for the chat interface
85
  HTML_TEMPLATE = '''
 
90
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
91
  <title>OpenWebUI - Ollama Chat</title>
92
  <style>
93
+ * {
94
+ margin: 0;
95
+ padding: 0;
96
+ box-sizing: border-box;
97
+ }
98
+ body {
99
+ font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
100
+ background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
101
+ min-height: 100vh;
102
+ padding: 20px;
103
+ }
104
+ .container {
105
+ max-width: 1200px;
106
+ margin: 0 auto;
107
+ background: white;
108
+ border-radius: 20px;
109
+ box-shadow: 0 20px 40px rgba(0,0,0,0.1);
110
+ overflow: hidden;
111
+ }
112
+ .header {
113
+ background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
114
+ color: white;
115
+ padding: 30px;
116
+ text-align: center;
117
+ }
118
+ .header h1 {
119
+ font-size: 2.5rem;
120
+ margin-bottom: 10px;
121
+ font-weight: 700;
122
+ }
123
+ .header p {
124
+ font-size: 1.1rem;
125
+ opacity: 0.9;
126
+ }
127
+ .controls {
128
+ padding: 20px 30px;
129
+ background: #f8f9fa;
130
+ border-bottom: 1px solid #e9ecef;
131
+ display: flex;
132
+ gap: 15px;
133
+ align-items: center;
134
+ flex-wrap: wrap;
135
+ }
136
+ .control-group {
137
+ display: flex;
138
+ align-items: center;
139
+ gap: 8px;
140
+ }
141
+ .control-group label {
142
+ font-weight: 600;
143
+ color: #495057;
144
+ min-width: 80px;
145
+ }
146
+ .control-group select,
147
+ .control-group input {
148
+ padding: 8px 12px;
149
+ border: 2px solid #e9ecef;
150
+ border-radius: 8px;
151
+ font-size: 14px;
152
+ transition: border-color 0.3s;
153
+ }
154
+ .control-group select:focus,
155
+ .control-group input:focus {
156
+ outline: none;
157
+ border-color: #667eea;
158
+ }
159
+ .chat-container {
160
+ height: 500px;
161
+ overflow-y: auto;
162
+ padding: 20px;
163
+ background: #fafbfc;
164
+ }
165
+ .message {
166
+ margin-bottom: 20px;
167
+ display: flex;
168
+ gap: 15px;
169
+ }
170
+ .message.user {
171
+ flex-direction: row-reverse;
172
+ }
173
+ .message-avatar {
174
+ width: 40px;
175
+ height: 40px;
176
+ border-radius: 50%;
177
+ display: flex;
178
+ align-items: center;
179
+ justify-content: center;
180
+ font-weight: bold;
181
+ color: white;
182
+ flex-shrink: 0;
183
+ }
184
+ .message.user .message-avatar {
185
+ background: #667eea;
186
+ }
187
+ .message.assistant .message-avatar {
188
+ background: #28a745;
189
+ }
190
+ .message-content {
191
+ background: white;
192
+ padding: 15px 20px;
193
+ border-radius: 18px;
194
+ max-width: 70%;
195
+ box-shadow: 0 2px 10px rgba(0,0,0,0.1);
196
+ line-height: 1.5;
197
+ }
198
+ .message.user .message-content {
199
+ background: #667eea;
200
+ color: white;
201
+ }
202
+ .message.assistant .message-content {
203
+ background: white;
204
+ color: #333;
205
+ }
206
+ .input-container {
207
+ padding: 20px 30px;
208
+ background: white;
209
+ border-top: 1px solid #e9ecef;
210
+ }
211
+ .input-form {
212
+ display: flex;
213
+ gap: 15px;
214
+ }
215
+ .input-field {
216
+ flex: 1;
217
+ padding: 15px 20px;
218
+ border: 2px solid #e9ecef;
219
+ border-radius: 25px;
220
+ font-size: 16px;
221
+ transition: border-color 0.3s;
222
+ resize: none;
223
+ min-height: 50px;
224
+ max-height: 120px;
225
+ }
226
+ .input-field:focus {
227
+ outline: none;
228
+ border-color: #667eea;
229
+ }
230
+ .send-button {
231
+ padding: 15px 30px;
232
+ background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
233
+ color: white;
234
+ border: none;
235
+ border-radius: 25px;
236
+ font-size: 16px;
237
+ font-weight: 600;
238
+ cursor: pointer;
239
+ transition: transform 0.2s;
240
+ min-width: 100px;
241
+ }
242
+ .send-button:hover {
243
+ transform: translateY(-2px);
244
+ }
245
+ .send-button:disabled {
246
+ opacity: 0.6;
247
+ cursor: not-allowed;
248
+ transform: none;
249
+ }
250
+ .status {
251
+ text-align: center;
252
+ padding: 10px;
253
+ font-size: 14px;
254
+ color: #6c757d;
255
+ }
256
+ .status.error {
257
+ color: #dc3545;
258
+ }
259
+ .status.success {
260
+ color: #28a745;
261
+ }
262
+ .typing-indicator {
263
+ display: none;
264
+ padding: 15px 20px;
265
+ background: white;
266
+ border-radius: 18px;
267
+ color: #6c757d;
268
+ font-style: italic;
269
+ }
270
+ @media (max-width: 768px) {
271
+ .controls {
272
+ flex-direction: column;
273
+ align-items: stretch;
274
+ }
275
+ .control-group {
276
+ justify-content: space-between;
277
+ }
278
+ .message-content {
279
+ max-width: 85%;
280
+ }
281
+ }
282
  </style>
283
  </head>
284
  <body>
 
360
  const option = document.createElement('option');
361
  option.value = model;
362
  option.textContent = model;
363
+ if (model === 'gemma-3-270m') {
364
  option.selected = true;
365
  }
366
  modelSelect.appendChild(option);
 
417
  const response = await fetch('/api/chat', {
418
  method: 'POST',
419
  headers: { 'Content-Type': 'application/json' },
420
+ body: JSON.stringify({ model, prompt: message, temperature, max_tokens: maxTokens })
421
  });
422
  const data = await response.json();
423
 
 
484
  @app.route('/')
485
  def home():
486
  """Main chat interface."""
487
+ return render_template_string(HTML_TEMPLATE, ollama_base_url=OLLAMA_BASE_URL, default_model=ALLOWED_MODELS)
488
 
489
  @app.route('/api/chat', methods=['POST'])
490
  def chat():
491
  """Chat API endpoint."""
492
  try:
493
  data = request.get_json()
494
+ if not data or 'prompt' not in data or 'model' not in data:
495
+ return jsonify({"status": "error", "message": "Prompt and model are required"}), 400
496
 
497
+ prompt = data['prompt']
498
  model = data['model']
499
  temperature = data.get('temperature', TEMPERATURE)
500
  max_tokens = data.get('max_tokens', MAX_TOKENS)
501
 
502
+ result = ollama_manager.generate(model, prompt, temperature=temperature, max_tokens=max_tokens)
503
  return jsonify(result), 200 if result["status"] == "success" else 500
504
  except Exception as e:
505
  logging.error(f"Chat endpoint error: {e}")
 
509
  def get_models():
510
  """Get available models."""
511
  try:
512
+ models = ollama_manager.list_models()
513
  return jsonify({
514
  "status": "success",
515
  "models": models,
 
523
  def health_check():
524
  """Health check endpoint."""
525
  try:
526
+ ollama_health = ollama_manager.health_check()
527
  return jsonify({
528
  "status": "healthy",
529
  "ollama_api": ollama_health,