Files changed (1) hide show
  1. app.py +85 -136
app.py CHANGED
@@ -1,154 +1,103 @@
1
  import gradio as gr
2
  from openai import OpenAI
3
- import requests
4
- import io
5
  import base64
 
6
  import os
7
  from PIL import Image
8
 
 
9
  # ---------------- KONFIGURATION ----------------
10
- # POD_ID = "jyuqoe0kierp04"
11
  POD_ID = os.getenv("POD_ID", "platzhalter_id")
12
- TEXT_URL = f"https://{POD_ID}-8000.proxy.runpod.net/v1"
13
- IMAGE_URL = f"https://{POD_ID}-8005.proxy.runpod.net/generate"
14
 
15
- client = OpenAI(api_key="EMPTY", base_url=TEXT_URL)
 
16
  # -----------------------------------------------
17
 
18
- # --- LOGIK (Bleibt gleich) ---
19
- def master_process(message, history, is_image_mode):
 
 
 
 
 
 
 
 
20
  history = history or []
21
-
22
- if is_image_mode:
23
- # BILD MODUS
24
- history.append({"role": "user", "content": message})
25
- history.append({"role": "assistant", "content": "🎨 **Bild-Modus aktiv:** Starte Flux..."})
26
- yield history, None
27
-
28
- try:
29
- res = requests.get(IMAGE_URL, params={"prompt": message}, timeout=300)
30
- if res.status_code == 200:
31
- data = res.json()
32
- b64 = data.get("image_base64")
33
- if b64:
34
- img_data = base64.b64decode(b64)
35
- img = Image.open(io.BytesIO(img_data))
36
- img.load()
37
- history[-1]["content"] = "Hier ist dein Kunstwerk! 👇"
38
- yield history, img
39
- else:
40
- history[-1]["content"] = "⚠️ Fehler: Leere Daten."
41
- yield history, None
42
- else:
43
- history[-1]["content"] = f"⚠️ Server Fehler: {res.status_code}"
44
- yield history, None
45
- except Exception as e:
46
- history[-1]["content"] = f"❌ Verbindung fehlgeschlagen: {str(e)}"
47
- yield history, None
48
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
49
  else:
50
- # TEXT MODUS
51
  history.append({"role": "user", "content": message})
52
- history.append({"role": "assistant", "content": ""})
53
- yield history, None
54
-
55
- messages_for_llm = [msg for msg in history if isinstance(msg['content'], str) and msg['content']]
56
- try:
57
- stream = client.chat.completions.create(
58
- model="Qwen/Qwen2.5-14B-Instruct-AWQ",
59
- messages=messages_for_llm,
60
- stream=True,
61
- max_tokens=2048
62
- )
63
- partial_text = ""
64
- for chunk in stream:
65
- if chunk.choices[0].delta.content:
66
- partial_text += chunk.choices[0].delta.content
67
- history[-1]["content"] = partial_text
68
- yield history, None
69
- except Exception as e:
70
- history[-1]["content"] = f"LLM Fehler: {str(e)}"
71
- yield history, None
72
-
73
- # --- DESIGN & LAYOUT (Bereinigt) ---
74
-
75
- my_theme = gr.themes.Ocean(
76
- primary_hue="indigo",
77
- secondary_hue="blue",
78
- neutral_hue="slate",
79
- ).set(
80
- body_background_fill="*neutral_50",
81
- block_background_fill="white",
82
- input_background_fill="*neutral_100"
83
- )
84
-
85
- my_css = """
86
- footer {visibility: hidden}
87
- .gradio-container {min-height: 0px !important}
88
- """
89
-
90
- # Wir lassen theme/css hier drin.
91
- # Die "UserWarning" (gelber Text) im Log ignorieren wir einfach.
92
- # Hauptsache kein roter "TypeError".
93
- with gr.Blocks(title="AI Studio") as demo:
94
-
95
  with gr.Row():
96
- gr.Markdown("## 🤖 AI Cockpit: Qwen & Flux")
97
-
98
- with gr.Row(equal_height=True):
99
- # Links: Chat
100
- with gr.Column(scale=3, min_width=400):
101
- chatbot = gr.Chatbot(
102
- height=550,
103
- # type="messages", # WICHTIG: Muss drin bleiben!
104
- label="Chat Verlauf"
105
- # ALLE Knöpfe entfernt, um Absturz zu verhindern
106
- )
107
-
108
- # Rechts: Bild
109
- with gr.Column(scale=2, min_width=300):
110
- image_display = gr.Image(
111
- label="Ergebnis",
112
- show_label=True,
113
- type="pil",
114
- height=500,
115
- interactive=False
116
- )
117
-
118
- # --- INPUT BEREICH ---
119
- gr.Markdown("---")
120
-
121
- with gr.Accordion("⚙️ Additional Tools / Einstellungen", open=False):
122
- with gr.Row():
123
- mode_check = gr.Checkbox(
124
- label="🎨 Bild-Generierung aktivieren",
125
- value=False,
126
- info="Wenn aktiviert, wird Flux statt Qwen genutzt."
127
- )
128
-
129
- with gr.Group():
130
- with gr.Row():
131
- msg = gr.Textbox(
132
- scale=5,
133
- show_label=False,
134
- placeholder="Nachricht eingeben...",
135
- container=False,
136
- autofocus=True
137
- )
138
- btn = gr.Button("Senden ➤", variant="primary", scale=1)
139
-
140
- # --- EVENTS ---
141
- msg.submit(
142
- fn=master_process,
143
- inputs=[msg, chatbot, mode_check],
144
- outputs=[chatbot, image_display]
145
- ).then(lambda: "", None, msg)
146
-
147
  btn.click(
148
- fn=master_process,
149
- inputs=[msg, chatbot, mode_check],
150
- outputs=[chatbot, image_display]
151
- ).then(lambda: "", None, msg)
 
 
 
 
 
 
152
 
153
- # Starten
154
- demo.launch(debug=False, share=False, theme=my_theme, css=my_css)
 
1
  import gradio as gr
2
  from openai import OpenAI
 
 
3
  import base64
4
+ import io
5
  import os
6
  from PIL import Image
7
 
8
+ # https://7wsby0fs8hw67a-8000.proxy.runpod.net/
9
  # ---------------- KONFIGURATION ----------------
10
+ #POD_ID = "7wsby0fs8hw67a" # <--- DEINE ID (Prüfen!)
11
  POD_ID = os.getenv("POD_ID", "platzhalter_id")
12
+ API_URL = f"https://{POD_ID}-8000.proxy.runpod.net/v1"
13
+ API_KEY = os.getenv("API_KEY", "platzhalter_id")
14
 
15
+ # client = OpenAI(api_key="EMPTY", base_url=API_URL)
16
+ client = OpenAI(api_key=API_KEY, base_url=API_URL)
17
  # -----------------------------------------------
18
 
19
+ def encode_image_to_base64(pil_image):
20
+ """Wandelt ein PIL-Bild in einen Base64-String um."""
21
+ if pil_image is None:
22
+ return None
23
+ buffered = io.BytesIO()
24
+ # Wir konvertieren zu RGB (falls PNG Transparenz hat) und speichern als JPEG
25
+ pil_image.convert("RGB").save(buffered, format="JPEG", quality=85)
26
+ return base64.b64encode(buffered.getvalue()).decode('utf-8')
27
+
28
+ def chat_with_vision(message, history, uploaded_image):
29
  history = history or []
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
30
 
31
+ # 1. User-Nachricht vorbereiten
32
+ user_content = []
33
+
34
+ # Wenn ein Bild da ist, fügen wir es hinzu
35
+ if uploaded_image is not None:
36
+ base64_image = encode_image_to_base64(uploaded_image)
37
+ # Das spezielle Format für Vision-Modelle (OpenAI Kompatibel)
38
+ user_content.append({
39
+ "type": "image_url",
40
+ "image_url": {
41
+ "url": f"data:image/jpeg;base64,{base64_image}"
42
+ }
43
+ })
44
+ history.append({"role": "user", "content": f"[Bild hochgeladen] {message}"})
45
  else:
 
46
  history.append({"role": "user", "content": message})
47
+
48
+ # Text hinzufügen
49
+ user_content.append({"type": "text", "text": message})
50
+
51
+ # 2. Nachricht an API senden
52
+ # Wir bauen die Messages-Liste für die API (ohne die lokalen History-Platzhalter)
53
+ api_messages = [{"role": "user", "content": user_content}]
54
+
55
+ history.append({"role": "assistant", "content": ""})
56
+
57
+ try:
58
+ stream = client.chat.completions.create(
59
+ model="Qwen/Qwen2.5-VL-7B-Instruct", # Name muss exakt zum Startbefehl passen!
60
+ messages=api_messages,
61
+ max_tokens=1024,
62
+ temperature=0.6,
63
+ stream=True
64
+ )
65
+
66
+ partial_text = ""
67
+ for chunk in stream:
68
+ if chunk.choices[0].delta.content:
69
+ partial_text += chunk.choices[0].delta.content
70
+ history[-1]["content"] = partial_text
71
+ yield history, None # Bild-Input leeren wir erst am Ende (optional)
72
+
73
+ except Exception as e:
74
+ history[-1]["content"] = f"⚠️ Fehler: {str(e)}"
75
+ yield history, None
76
+
77
+ # --- UI DESIGN ---
78
+ with gr.Blocks(title="👁️ Qwen Vision Test") as demo:
79
+ gr.Markdown("## 👁️ Qwen2.5-VL: Zeig mir ein Bild!")
80
+
 
 
 
 
 
 
 
 
 
81
  with gr.Row():
82
+ with gr.Column(scale=1):
83
+ image_input = gr.Image(type="pil", label="Bild hochladen", height=300)
84
+ msg_input = gr.Textbox(placeholder="Was siehst du auf dem Bild?", label="Deine Frage")
85
+ btn = gr.Button("Senden", variant="primary")
86
+
87
+ with gr.Column(scale=2):
88
+ chatbot = gr.Chatbot(height=500, label="Antwort")
89
+
90
+ # Events
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
91
  btn.click(
92
+ fn=chat_with_vision,
93
+ inputs=[msg_input, chatbot, image_input],
94
+ outputs=[chatbot, image_input] # image_input hier, falls du es nach Senden löschen willst
95
+ )
96
+
97
+ msg_input.submit(
98
+ fn=chat_with_vision,
99
+ inputs=[msg_input, chatbot, image_input],
100
+ outputs=[chatbot, image_input]
101
+ )
102
 
103
+ demo.launch(debug=True, share=False)