cstr commited on
Commit
9144903
·
verified ·
1 Parent(s): 3e6631d

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +234 -436
app.py CHANGED
@@ -10,6 +10,7 @@ import time
10
  # Get API key from environment variable for security
11
  OPENROUTER_API_KEY = os.environ.get("OPENROUTER_API_KEY", "")
12
 
 
13
  # Model information
14
  free_models = [
15
  ("Google: Gemini Pro 2.0 Experimental (free)", "google/gemini-2.0-pro-exp-02-05:free", 0, 0, 2000000),
@@ -71,33 +72,7 @@ free_models = [
71
  ("MythoMax 13B (free)", "gryphe/mythomax-l2-13b:free", 0, 0, 4096),
72
  ]
73
 
74
- # Filter for vision models
75
- vision_model_ids = [
76
- "meta-llama/llama-3.2-11b-vision-instruct:free",
77
- "qwen/qwen2.5-vl-72b-instruct:free",
78
- "qwen/qwen2.5-vl-3b-instruct:free",
79
- "qwen/qwen2.5-vl-32b-instruct:free",
80
- "qwen/qwen-2.5-vl-7b-instruct:free",
81
- "google/gemini-2.0-pro-exp-02-05:free",
82
- "google/gemini-2.5-pro-exp-03-25:free"
83
- ]
84
-
85
- # Format model names to include context size
86
- def format_model_name(name, context_size):
87
- if context_size >= 1000000:
88
- context_str = f"{context_size/1000000:.1f}M tokens"
89
- else:
90
- context_str = f"{context_size/1000:.0f}K tokens"
91
- return f"{name} ({context_str})"
92
-
93
- # Prefilter vision models
94
- vision_models = [(format_model_name(name, context_size), model_id, context_size)
95
- for name, model_id, _, _, context_size in free_models
96
- if model_id in vision_model_ids]
97
-
98
- text_models = [(format_model_name(name, context_size), model_id, context_size)
99
- for name, model_id, _, _, context_size in free_models]
100
-
101
  def encode_image(image):
102
  """Convert PIL Image to base64 string"""
103
  buffered = BytesIO()
@@ -112,79 +87,16 @@ def encode_file(file_path):
112
  except Exception as e:
113
  return f"Error reading file: {str(e)}"
114
 
115
- def process_message_stream(message, chat_history, model_name, uploaded_image=None, uploaded_file=None,
116
- temperature=0.7, top_p=1.0, max_tokens=None, stream=True):
117
- """Process message and stream the model response"""
118
- # Extract model_id from the display name
119
- model_id = model_name.split(' ')[1] if len(model_name.split(' ')) > 1 else model_name
120
-
121
- # Check if API key is set
122
- if not OPENROUTER_API_KEY:
123
- yield "Please set your OpenRouter API key in the environment variables.", chat_history
124
- return
125
-
126
- # Setup headers and URL
127
  headers = {
128
  "Content-Type": "application/json",
129
  "Authorization": f"Bearer {OPENROUTER_API_KEY}",
130
- "HTTP-Referer": "https://huggingface.co/spaces", # Replace with your actual space URL in production
131
  }
132
 
133
  url = "https://openrouter.ai/api/v1/chat/completions"
134
 
135
- # Build message content
136
- messages = []
137
-
138
- # Add chat history
139
- for item in chat_history:
140
- if isinstance(item, tuple):
141
- # Old format compatibility
142
- human_msg, ai_msg = item
143
- messages.append({"role": "user", "content": human_msg})
144
- messages.append({"role": "assistant", "content": ai_msg})
145
- else:
146
- # New message format
147
- messages.append(item)
148
-
149
- # Add current message with any attachments
150
- if uploaded_image:
151
- # Image processing for vision models
152
- base64_image = encode_image(uploaded_image)
153
- content = [
154
- {"type": "text", "text": message}
155
- ]
156
-
157
- # Add text from file if provided
158
- if uploaded_file:
159
- file_content = encode_file(uploaded_file)
160
- content[0]["text"] = f"{message}\n\nFile content:\n```\n{file_content}\n```"
161
-
162
- # Add image
163
- content.append({
164
- "type": "image_url",
165
- "image_url": {
166
- "url": f"data:image/jpeg;base64,{base64_image}"
167
- }
168
- })
169
-
170
- messages.append({"role": "user", "content": content})
171
- else:
172
- if uploaded_file:
173
- file_content = encode_file(uploaded_file)
174
- content = f"{message}\n\nFile content:\n```\n{file_content}\n```"
175
- messages.append({"role": "user", "content": content})
176
- else:
177
- messages.append({"role": "user", "content": message})
178
-
179
- # Get context length for the model
180
- context_length = next((context for _, model_id, context in text_models if model_id == model_id), 4096)
181
-
182
- # Calculate default max tokens if not specified
183
- if not max_tokens:
184
- # Use 25% of context length as a reasonable default
185
- max_tokens = min(4000, int(context_length * 0.25))
186
-
187
- # Build request data
188
  data = {
189
  "model": model_id,
190
  "messages": messages,
@@ -194,298 +106,244 @@ def process_message_stream(message, chat_history, model_name, uploaded_image=Non
194
  "max_tokens": max_tokens
195
  }
196
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
197
  try:
198
- # Create a new message pair in the chat history
199
- user_msg = {"role": "user", "content": message}
200
- ai_msg = {"role": "assistant", "content": ""}
201
- chat_history.append(user_msg)
202
- chat_history.append(ai_msg)
203
-
204
- full_response = ""
205
-
206
- if stream:
207
- # Make streaming API call
208
- with requests.post(url, headers=headers, json=data, stream=True) as response:
209
- response.raise_for_status()
210
- buffer = ""
211
-
212
- for chunk in response.iter_content(chunk_size=1024, decode_unicode=False):
213
- if chunk:
214
- buffer += chunk.decode('utf-8')
 
215
 
216
- while True:
217
- line_end = buffer.find('\n')
218
- if line_end == -1:
219
  break
220
 
221
- line = buffer[:line_end].strip()
222
- buffer = buffer[line_end + 1:]
223
-
224
- if line.startswith('data: '):
225
- data = line[6:]
226
- if data == '[DONE]':
227
- break
228
-
229
- try:
230
- data_obj = json.loads(data)
231
- delta_content = data_obj["choices"][0]["delta"].get("content", "")
232
- if delta_content:
233
- full_response += delta_content
234
- # Update the last assistant message
235
- chat_history[-1]["content"] = full_response
236
- yield chat_history
237
- except json.JSONDecodeError:
238
- pass
239
  else:
240
- # Non-streaming API call
241
- response = requests.post(url, headers=headers, json=data)
242
  response.raise_for_status()
243
  result = response.json()
244
- full_response = result.get("choices", [{}])[0].get("message", {}).get("content", "No response")
245
- chat_history[-1]["content"] = full_response
 
246
  yield chat_history
247
-
248
- return chat_history
249
 
250
  except Exception as e:
251
  error_msg = f"Error: {str(e)}"
252
  chat_history[-1]["content"] = error_msg
253
  yield chat_history
254
 
255
- # Create a nice CSS theme
256
- css = """
257
- .gradio-container {
258
- font-family: 'Inter', -apple-system, BlinkMacSystemFont, sans-serif;
259
- }
260
- .chat-message {
261
- padding: 15px;
262
- border-radius: 10px;
263
- margin-bottom: 10px;
264
- }
265
- .user-message {
266
- background-color: #f0f4f8;
267
- }
268
- .assistant-message {
269
- background-color: #e9f5ff;
270
- }
271
- #chat-container {
272
- height: 600px;
273
- overflow-y: auto;
274
- }
275
- #chat-input {
276
- min-height: 120px;
277
- border-radius: 8px;
278
- padding: 10px;
279
- }
280
- #model-select-container {
281
- border-radius: 8px;
282
- padding: 15px;
283
- background-color: #f8fafc;
284
- }
285
- .app-header {
286
- text-align: center;
287
- margin-bottom: 20px;
288
- }
289
- .app-header h1 {
290
- font-weight: 700;
291
- color: #2C3E50;
292
- margin-bottom: 5px;
293
- }
294
- .app-header p {
295
- color: #7F8C8D;
296
- margin-top: 0;
297
- }
298
- .parameter-container {
299
- background-color: #f8fafc;
300
- padding: 10px;
301
- border-radius: 8px;
302
- margin-top: 10px;
303
- }
304
- .file-upload-container {
305
- margin-top: 10px;
306
- }
307
- """
308
-
309
- with gr.Blocks(css=css, theme=gr.themes.Soft()) as demo:
310
- gr.HTML("""
311
- <div class="app-header">
312
- <h1>🔆 CrispChat</h1>
313
- <p>Chat with free OpenRouter AI models - supports text, images, and files</p>
314
- </div>
315
- """)
316
 
317
  with gr.Row():
318
  with gr.Column(scale=4):
319
  chatbot = gr.Chatbot(
320
- height=600,
321
  show_copy_button=True,
322
  show_share_button=False,
323
- elem_id="chatbot",
324
  layout="bubble",
325
  avatar_images=("👤", "🤖"),
326
- bubble_full_width=False,
327
- type="messages" # Use new message format
328
  )
329
 
330
  with gr.Row():
331
- with gr.Column(scale=10):
332
- user_message = gr.Textbox(
333
- placeholder="Type your message here...",
334
- show_label=False,
335
- elem_id="chat-input",
336
- lines=3
 
 
 
 
 
 
 
 
 
 
 
 
337
  )
338
-
339
- with gr.Row():
340
- image_upload = gr.Image(
341
- type="pil",
342
- label="Image (optional)",
343
- show_label=True,
344
- scale=1
345
- )
346
-
347
- file_upload = gr.File(
348
- label="Text File (optional)",
349
- file_types=[".txt", ".md", ".py", ".js", ".html", ".css", ".json"],
350
- scale=1
351
- )
352
-
353
- submit_btn = gr.Button("Send", scale=1, variant="primary")
354
-
355
- with gr.Column(scale=2):
356
- with gr.Accordion("Model Selection", open=True):
357
- using_vision = gr.Checkbox(label="Using image", value=False)
358
 
 
 
 
 
 
359
  model_selector = gr.Dropdown(
360
- choices=[name for name, _, _ in text_models],
361
- value=text_models[0][0],
362
- label="Select Model",
363
- elem_id="model-selector"
364
  )
365
 
366
- context_info = gr.Markdown(value=f"Context: {text_models[0][2]:,} tokens")
 
 
 
 
 
 
367
 
368
- with gr.Accordion("Parameters", open=False):
369
- with gr.Group():
370
- temperature = gr.Slider(
371
- minimum=0.0,
372
- maximum=2.0,
373
- value=0.7,
374
- step=0.1,
375
- label="Temperature",
376
- info="Higher = more creative, Lower = more deterministic"
377
- )
378
-
379
- top_p = gr.Slider(
380
- minimum=0.1,
381
- maximum=1.0,
382
- value=1.0,
383
- step=0.1,
384
- label="Top P",
385
- info="Controls token diversity"
386
- )
387
-
388
- max_tokens = gr.Slider(
389
- minimum=100,
390
- maximum=8000,
391
- value=1000,
392
- step=100,
393
- label="Max Tokens",
394
- info="Maximum length of the response"
395
- )
396
-
397
- use_streaming = gr.Checkbox(
398
- label="Stream Response",
399
- value=True,
400
- info="Show response as it's generated"
401
- )
402
-
403
- with gr.Accordion("Tips", open=False):
404
- gr.Markdown("""
405
- * Select a vision-capable model for images
406
- * Upload text files to include their content
407
- * Check model context window sizes
408
- * Adjust temperature for creativity level
409
- * Top P controls diversity of responses
410
- """)
411
-
412
- # Define events
413
- def update_model_selector(use_vision):
414
- if use_vision:
415
- return (
416
- gr.Dropdown(choices=[name for name, _, _ in vision_models], value=vision_models[0][0]),
417
- f"Context: {vision_models[0][2]:,} tokens"
418
- )
419
- else:
420
- return (
421
- gr.Dropdown(choices=[name for name, _, _ in text_models], value=text_models[0][0]),
422
- f"Context: {text_models[0][2]:,} tokens"
423
- )
424
-
425
- def update_context_info(model_name):
426
- # Extract context size from model name
427
- for name, _, context_size in text_models:
428
- if name == model_name:
429
- return f"Context: {context_size:,} tokens"
430
- for name, _, context_size in vision_models:
431
- if name == model_name:
432
- return f"Context: {context_size:,} tokens"
433
- return "Context size unknown"
434
 
435
- using_vision.change(
436
- fn=update_model_selector,
437
- inputs=using_vision,
438
- outputs=[model_selector, context_info]
 
 
 
 
 
 
 
 
 
 
 
439
  )
440
 
441
- model_selector.change(
442
- fn=update_context_info,
443
- inputs=model_selector,
444
- outputs=context_info
 
 
 
 
 
 
 
 
 
 
445
  )
446
 
447
- # Submit function
448
- def on_submit(message, history, model, image, file, temp, top_p_val, max_tok, stream):
449
- if not message and not image and not file:
450
- return "", history
451
- return "", process_message_stream(
452
- message,
453
- history,
454
- model,
455
- image,
456
- file.name if file else None,
457
- temperature=temp,
458
- top_p=top_p_val,
459
- max_tokens=max_tok,
460
- stream=stream
461
- )
462
 
463
- # Set up submission events
464
- submit_btn.click(
465
- on_submit,
466
- inputs=[
467
- user_message, chatbot, model_selector,
468
- image_upload, file_upload,
469
- temperature, top_p, max_tokens, use_streaming
470
- ],
471
- outputs=[user_message, chatbot]
472
  )
473
 
474
- user_message.submit(
475
- on_submit,
476
- inputs=[
477
- user_message, chatbot, model_selector,
478
- image_upload, file_upload,
479
- temperature, top_p, max_tokens, use_streaming
480
- ],
481
- outputs=[user_message, chatbot]
482
  )
483
 
484
- # Define FastAPI endpoint
485
- from fastapi import FastAPI, Request, HTTPException
486
- from fastapi.responses import JSONResponse
487
  from pydantic import BaseModel
488
- from fastapi.middleware.cors import CORSMiddleware
489
 
490
  app = FastAPI()
491
 
@@ -498,108 +356,48 @@ class GenerateRequest(BaseModel):
498
  async def api_generate(request: GenerateRequest):
499
  """API endpoint for generating responses"""
500
  try:
501
- message = request.message
502
- model_name = request.model
503
- image_data = request.image_data
504
 
505
- # Process image if provided
506
- image = None
507
- if image_data:
508
  try:
509
- # Decode base64 image
510
- image_bytes = base64.b64decode(image_data)
511
  image = Image.open(BytesIO(image_bytes))
512
- except Exception as e:
513
- return JSONResponse(
514
- status_code=400,
515
- content={"error": f"Image processing error: {str(e)}"}
516
- )
517
-
518
- # Generate response
519
- try:
520
- # Setup headers and URL
521
- headers = {
522
- "Content-Type": "application/json",
523
- "Authorization": f"Bearer {OPENROUTER_API_KEY}",
524
- "HTTP-Referer": "https://huggingface.co/spaces",
525
- }
526
-
527
- url = "https://openrouter.ai/api/v1/chat/completions"
528
-
529
- # Get model_id from model_name
530
- model_id = None
531
- if model_name:
532
- for _, mid, _ in text_models + vision_models:
533
- if model_name in mid or model_name == mid:
534
- model_id = mid
535
- break
536
-
537
- if not model_id:
538
- model_id = text_models[0][1]
539
-
540
- # Build messages
541
- messages = []
542
-
543
- if image:
544
- # Image processing for vision models
545
  base64_image = encode_image(image)
546
- content = [
547
- {"type": "text", "text": message},
548
- {
549
- "type": "image_url",
550
- "image_url": {
551
- "url": f"data:image/jpeg;base64,{base64_image}"
 
 
 
 
552
  }
553
- }
554
- ]
555
- messages.append({"role": "user", "content": content})
556
- else:
557
- messages.append({"role": "user", "content": message})
558
-
559
- # Build request data
560
- data = {
561
- "model": model_id,
562
- "messages": messages,
563
- "temperature": 0.7
564
- }
565
-
566
- # Make API call
567
- response = requests.post(url, headers=headers, json=data)
568
- response.raise_for_status()
569
-
570
- # Parse response
571
- result = response.json()
572
- reply = result.get("choices", [{}])[0].get("message", {}).get("content", "No response")
573
-
574
- return {"response": reply}
575
 
576
- except Exception as e:
577
- return JSONResponse(
578
- status_code=500,
579
- content={"error": f"Error generating response: {str(e)}"}
580
- )
581
-
582
  except Exception as e:
583
- return JSONResponse(
584
- status_code=500,
585
- content={"error": f"Server error: {str(e)}"}
586
- )
587
-
588
- # Add CORS middleware to allow cross-origin requests
589
- app.add_middleware(
590
- CORSMiddleware,
591
- allow_origins=["*"],
592
- allow_credentials=True,
593
- allow_methods=["*"],
594
- allow_headers=["*"],
595
- )
596
 
597
  # Mount Gradio app
598
- import gradio as gr
599
  app = gr.mount_gradio_app(app, demo, path="/")
600
 
601
- # Start the app
602
  if __name__ == "__main__":
603
- # Use 'uvicorn' directly in HF Spaces
604
- import uvicorn
605
- uvicorn.run(app, host="0.0.0.0", port=7860)
 
10
  # Get API key from environment variable for security
11
  OPENROUTER_API_KEY = os.environ.get("OPENROUTER_API_KEY", "")
12
 
13
+
14
  # Model information
15
  free_models = [
16
  ("Google: Gemini Pro 2.0 Experimental (free)", "google/gemini-2.0-pro-exp-02-05:free", 0, 0, 2000000),
 
72
  ("MythoMax 13B (free)", "gryphe/mythomax-l2-13b:free", 0, 0, 4096),
73
  ]
74
 
75
+ # Helper functions
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
76
  def encode_image(image):
77
  """Convert PIL Image to base64 string"""
78
  buffered = BytesIO()
 
87
  except Exception as e:
88
  return f"Error reading file: {str(e)}"
89
 
90
+ def process_api_call(messages, model_id, temperature=0.7, top_p=1.0, max_tokens=1000, stream=False):
91
+ """Make API call to OpenRouter"""
 
 
 
 
 
 
 
 
 
 
92
  headers = {
93
  "Content-Type": "application/json",
94
  "Authorization": f"Bearer {OPENROUTER_API_KEY}",
95
+ "HTTP-Referer": "https://huggingface.co/spaces",
96
  }
97
 
98
  url = "https://openrouter.ai/api/v1/chat/completions"
99
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
100
  data = {
101
  "model": model_id,
102
  "messages": messages,
 
106
  "max_tokens": max_tokens
107
  }
108
 
109
+ return requests.post(url, headers=headers, json=data, stream=stream)
110
+
111
+ def update_conversation(message, chat_history, model_choice, uploaded_image=None, uploaded_file=None,
112
+ temp=0.7, top_p=1.0, max_tokens=1000, stream_response=False):
113
+ """Update conversation with new message"""
114
+ # Get model ID from model_choice
115
+ model_id = None
116
+ for name, model_id_value, *_ in free_models:
117
+ if name == model_choice or model_id_value == model_choice:
118
+ model_id = model_id_value
119
+ break
120
+
121
+ if not model_id:
122
+ # Fallback to a default model
123
+ model_id = "google/gemini-2.0-pro-exp-02-05:free"
124
+
125
+ # Build messages array from chat history
126
+ messages = []
127
+ for msg in chat_history:
128
+ if isinstance(msg, dict):
129
+ messages.append(msg)
130
+ elif isinstance(msg, tuple) and len(msg) == 2:
131
+ # Handle legacy tuple format
132
+ user_msg, ai_msg = msg
133
+ messages.append({"role": "user", "content": user_msg})
134
+ messages.append({"role": "assistant", "content": ai_msg})
135
+
136
+ # Prepare the new user message
137
+ content = message
138
+
139
+ # Handle file attachment
140
+ if uploaded_file:
141
+ file_content = encode_file(uploaded_file)
142
+ content = f"{message}\n\nFile content:\n```\n{file_content}\n```"
143
+
144
+ # Handle image
145
+ if uploaded_image:
146
+ base64_image = encode_image(uploaded_image)
147
+ image_content = [
148
+ {"type": "text", "text": content},
149
+ {
150
+ "type": "image_url",
151
+ "image_url": {
152
+ "url": f"data:image/jpeg;base64,{base64_image}"
153
+ }
154
+ }
155
+ ]
156
+ messages.append({"role": "user", "content": image_content})
157
+ else:
158
+ messages.append({"role": "user", "content": content})
159
+
160
+ # Add message to chat history
161
+ user_message = {"role": "user", "content": content}
162
+ assistant_message = {"role": "assistant", "content": ""}
163
+ chat_history.append(user_message)
164
+ chat_history.append(assistant_message)
165
+
166
  try:
167
+ if stream_response:
168
+ # Handle streaming response
169
+ response = process_api_call(messages, model_id, temp, top_p, max_tokens, stream=True)
170
+
171
+ full_response = ""
172
+ buffer = ""
173
+
174
+ for chunk in response.iter_content(chunk_size=1024, decode_unicode=False):
175
+ if chunk:
176
+ buffer += chunk.decode('utf-8')
177
+
178
+ while True:
179
+ line_end = buffer.find('\n')
180
+ if line_end == -1:
181
+ break
182
+
183
+ line = buffer[:line_end].strip()
184
+ buffer = buffer[line_end + 1:]
185
 
186
+ if line.startswith('data: '):
187
+ data = line[6:]
188
+ if data == '[DONE]':
189
  break
190
 
191
+ try:
192
+ data_obj = json.loads(data)
193
+ delta_content = data_obj["choices"][0]["delta"].get("content", "")
194
+ if delta_content:
195
+ full_response += delta_content
196
+ # Update the assistant message
197
+ chat_history[-1]["content"] = full_response
198
+ yield chat_history
199
+ except json.JSONDecodeError:
200
+ pass
 
 
 
 
 
 
 
 
201
  else:
202
+ # Handle non-streaming response
203
+ response = process_api_call(messages, model_id, temp, top_p, max_tokens, stream=False)
204
  response.raise_for_status()
205
  result = response.json()
206
+
207
+ reply = result.get("choices", [{}])[0].get("message", {}).get("content", "No response")
208
+ chat_history[-1]["content"] = reply
209
  yield chat_history
 
 
210
 
211
  except Exception as e:
212
  error_msg = f"Error: {str(e)}"
213
  chat_history[-1]["content"] = error_msg
214
  yield chat_history
215
 
216
+ # Create simpler UI
217
+ with gr.Blocks(theme=gr.themes.Soft()) as demo:
218
+ gr.Markdown("# 🔆 CrispChat - OpenRouter AI Models")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
219
 
220
  with gr.Row():
221
  with gr.Column(scale=4):
222
  chatbot = gr.Chatbot(
223
+ height=500,
224
  show_copy_button=True,
225
  show_share_button=False,
 
226
  layout="bubble",
227
  avatar_images=("👤", "🤖"),
228
+ type="messages"
 
229
  )
230
 
231
  with gr.Row():
232
+ user_message = gr.Textbox(
233
+ placeholder="Type your message here...",
234
+ show_label=False,
235
+ lines=3
236
+ )
237
+
238
+ with gr.Row():
239
+ with gr.Column(scale=1):
240
+ image_upload = gr.Image(
241
+ type="pil",
242
+ label="Upload Image",
243
+ show_label=True
244
+ )
245
+
246
+ with gr.Column(scale=1):
247
+ file_upload = gr.File(
248
+ label="Upload Text File",
249
+ file_types=[".txt", ".md", ".py", ".js", ".html", ".css", ".json"]
250
  )
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
251
 
252
+ with gr.Column(scale=1):
253
+ submit_btn = gr.Button("Send", variant="primary")
254
+
255
+ with gr.Column(scale=2):
256
+ with gr.Accordion("Model Settings", open=True):
257
  model_selector = gr.Dropdown(
258
+ choices=[name for name, _ in free_models],
259
+ value=free_models[0][0],
260
+ label="Select Model"
 
261
  )
262
 
263
+ temperature = gr.Slider(
264
+ minimum=0.1,
265
+ maximum=2.0,
266
+ value=0.7,
267
+ step=0.1,
268
+ label="Temperature"
269
+ )
270
 
271
+ top_p = gr.Slider(
272
+ minimum=0.1,
273
+ maximum=1.0,
274
+ value=1.0,
275
+ step=0.1,
276
+ label="Top P"
277
+ )
278
+
279
+ max_tokens = gr.Slider(
280
+ minimum=100,
281
+ maximum=4000,
282
+ value=1000,
283
+ step=100,
284
+ label="Max Tokens"
285
+ )
286
+
287
+ streaming = gr.Checkbox(
288
+ label="Enable Streaming",
289
+ value=True
290
+ )
291
+
292
+ clear_btn = gr.Button("Clear Chat")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
293
 
294
+ # Set up event handlers
295
+ msg_submit_event = user_message.submit(
296
+ fn=update_conversation,
297
+ inputs=[
298
+ user_message,
299
+ chatbot,
300
+ model_selector,
301
+ image_upload,
302
+ file_upload,
303
+ temperature,
304
+ top_p,
305
+ max_tokens,
306
+ streaming
307
+ ],
308
+ outputs=chatbot
309
  )
310
 
311
+ btn_submit_event = submit_btn.click(
312
+ fn=update_conversation,
313
+ inputs=[
314
+ user_message,
315
+ chatbot,
316
+ model_selector,
317
+ image_upload,
318
+ file_upload,
319
+ temperature,
320
+ top_p,
321
+ max_tokens,
322
+ streaming
323
+ ],
324
+ outputs=chatbot
325
  )
326
 
327
+ # Clear chat
328
+ clear_btn.click(
329
+ fn=lambda: [],
330
+ outputs=[chatbot]
331
+ )
 
 
 
 
 
 
 
 
 
 
332
 
333
+ # Clear input after submission
334
+ msg_submit_event.then(
335
+ fn=lambda: "",
336
+ outputs=[user_message]
 
 
 
 
 
337
  )
338
 
339
+ btn_submit_event.then(
340
+ fn=lambda: "",
341
+ outputs=[user_message]
 
 
 
 
 
342
  )
343
 
344
+ # Mount FastAPI for external access
345
+ from fastapi import FastAPI
 
346
  from pydantic import BaseModel
 
347
 
348
  app = FastAPI()
349
 
 
356
  async def api_generate(request: GenerateRequest):
357
  """API endpoint for generating responses"""
358
  try:
359
+ # Process request
360
+ messages = [{"role": "user", "content": request.message}]
 
361
 
362
+ # Handle image if provided
363
+ if request.image_data:
 
364
  try:
365
+ image_bytes = base64.b64decode(request.image_data)
 
366
  image = Image.open(BytesIO(image_bytes))
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
367
  base64_image = encode_image(image)
368
+
369
+ messages = [{
370
+ "role": "user",
371
+ "content": [
372
+ {"type": "text", "text": request.message},
373
+ {
374
+ "type": "image_url",
375
+ "image_url": {
376
+ "url": f"data:image/jpeg;base64,{base64_image}"
377
+ }
378
  }
379
+ ]
380
+ }]
381
+ except Exception as e:
382
+ return {"error": f"Image processing error: {str(e)}"}
383
+
384
+ # Get model
385
+ model_id = request.model or free_models[0][1]
386
+
387
+ # Make API call
388
+ response = process_api_call(messages, model_id, stream=False)
389
+ response.raise_for_status()
390
+ result = response.json()
391
+
392
+ reply = result.get("choices", [{}])[0].get("message", {}).get("content", "No response")
393
+ return {"response": reply}
 
 
 
 
 
 
 
394
 
 
 
 
 
 
 
395
  except Exception as e:
396
+ return {"error": f"Error: {str(e)}"}
 
 
 
 
 
 
 
 
 
 
 
 
397
 
398
  # Mount Gradio app
 
399
  app = gr.mount_gradio_app(app, demo, path="/")
400
 
401
+ # Launch the app
402
  if __name__ == "__main__":
403
+ demo.launch()