From 171117b0fd507df975830a0f98f7b3290fb97213 Mon Sep 17 00:00:00 2001 From: FURK4NGG <105324908+FURK4NGG@users.noreply.github.com> Date: Fri, 13 Feb 2026 23:42:36 +0300 Subject: [PATCH] Add files via upload --- ai.py | 99 ++++++++++++++++++++++++++++++ ai_controller.sh | 14 +++++ ui.py | 156 +++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 269 insertions(+) create mode 100644 ai.py create mode 100644 ai_controller.sh create mode 100644 ui.py diff --git a/ai.py b/ai.py new file mode 100644 index 0000000..359b74c --- /dev/null +++ b/ai.py @@ -0,0 +1,99 @@ +#!/usr/bin/env python3 +import os +import sys +import base64 +import requests +import time + +# --- DEFAULT FALLBACK MODELS --- +DEFAULT_MODELS = [ + "qwen/qwen-2.5-vl-7b-instruct:free", + "meta-llama/llama-3.2-11b-vision-instruct", + "google/gemini-2.5-flash-image", +] + +# --- ARGUMENTS --- +if len(sys.argv) < 3: + print("Kullanım: ai.py [model1,model2,...]") + sys.exit(1) + +image_path = sys.argv[1] +prompt = sys.argv[2] + +# --- MODELS FROM UI (OPTIONAL) --- +if len(sys.argv) >= 4 and sys.argv[3].strip(): + MODELS = [m.strip() for m in sys.argv[3].split(",")] +else: + MODELS = DEFAULT_MODELS + +# --- API KEY --- +API_KEY = os.environ.get("OPENROUTER_API_KEY") +if not API_KEY: + print("HATA: OPENROUTER_API_KEY not set") + sys.exit(1) + +# --- IMAGE LOAD --- +if not os.path.exists(image_path): + print(f"HATA: Resim bulunamadı → {image_path}") + sys.exit(1) + +with open(image_path, "rb") as f: + image_b64 = base64.b64encode(f.read()).decode("utf-8") + +# --- REQUEST CONFIG --- +URL = "https://openrouter.ai/api/v1/chat/completions" + +HEADERS = { + "Authorization": f"Bearer {API_KEY}", + "Content-Type": "application/json", + "HTTP-Referer": "http://localhost", + "X-Title": "ai-capture", +} + +# --- MODEL TRY FUNCTION --- +def try_model(model_name): + payload = { + "model": model_name, + "messages": [ + { + "role": "user", + "content": [ + {"type": "text", "text": prompt}, + { + "type": "image_url", + "image_url": { + "url": f"data:image/png;base64,{image_b64}" + } + } + ], + } + ], + "max_tokens": 512, + } + + response = requests.post( + URL, + headers=HEADERS, + json=payload, + timeout=60, + ) + + if response.status_code == 200: + data = response.json() + return data["choices"][0]["message"]["content"] + + raise Exception(f"{model_name} → {response.status_code}: {response.text}") + + +# --- MAIN LOOP --- +for model in MODELS: + try: + answer = try_model(model) + print(answer) + sys.exit(0) + except Exception as e: + print(f"[FAIL] {e}", file=sys.stderr) + time.sleep(1) + +print("HATA: Tüm modeller başarısız") +sys.exit(1) diff --git a/ai_controller.sh b/ai_controller.sh new file mode 100644 index 0000000..325afb2 --- /dev/null +++ b/ai_controller.sh @@ -0,0 +1,14 @@ +#!/bin/bash +source ~/.config/ai_capture/env.sh + +set -e + +TMP="$HOME/.cache/ai-capture" +mkdir -p "$TMP" + +GEOM=$(slurp) || exit 1 +IMG="$TMP/capture_$(date +%s).png" + +grim -g "$GEOM" "$IMG" + +python ~/ai_capture/ui.py "$IMG" diff --git a/ui.py b/ui.py new file mode 100644 index 0000000..b515513 --- /dev/null +++ b/ui.py @@ -0,0 +1,156 @@ +#!/usr/bin/env python3 +import sys +import gi +import subprocess +import threading + +gi.require_version("Gtk", "4.0") +from gi.repository import Gtk, GLib + +AI_SCRIPT = "/home/bob/ai_capture/ai.py" + +MODELS = [ + "qwen/qwen-2.5-vl-7b-instruct:free", + "meta-llama/llama-3.2-11b-vision-instruct", + "google/gemini-2.5-flash-image", +] + +class App(Gtk.Application): + def __init__(self, image_path): + super().__init__(application_id="ai.capture.ui") + self.image_path = image_path + + def do_activate(self): + win = Gtk.ApplicationWindow(application=self) + win.set_title("AI Capture") + win.set_default_size(900, 800) + + root = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, spacing=8) + root.set_margin_top(10) + root.set_margin_bottom(10) + root.set_margin_start(10) + root.set_margin_end(10) + + # === IMAGE === + picture = Gtk.Picture.new_for_filename(self.image_path) + picture.set_hexpand(True) + picture.set_vexpand(True) + picture.set_content_fit(Gtk.ContentFit.CONTAIN) + + frame = Gtk.Frame() + frame.set_hexpand(True) + frame.set_vexpand(True) + frame.set_child(picture) + root.append(frame) + + # === INPUT ROW === + row = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL, spacing=6) + + self.entry = Gtk.Entry() + self.entry.set_placeholder_text("Bu görüntü hakkında sorunuzu yazın…") + self.entry.set_hexpand(True) + row.append(self.entry) + + self.ask_btn = Gtk.Button(label="Sor") + self.ask_btn.connect("clicked", self.on_ask) + row.append(self.ask_btn) + + menu_btn = Gtk.Button(label="⋮") + row.append(menu_btn) + + root.append(row) + + # === OUTPUT === + self.output = Gtk.TextView() + self.output.set_editable(False) + self.output.set_wrap_mode(Gtk.WrapMode.WORD_CHAR) + + out_scroll = Gtk.ScrolledWindow() + out_scroll.set_vexpand(True) + out_scroll.set_policy(Gtk.PolicyType.NEVER, Gtk.PolicyType.AUTOMATIC) + out_scroll.set_child(self.output) + + root.append(out_scroll) + + # === MODEL POPOVER === + popover = Gtk.Popover() + box = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, spacing=4) + box.set_margin_top(8) + box.set_margin_bottom(8) + box.set_margin_start(8) + box.set_margin_end(8) + + self.checks = {} + for m in MODELS: + cb = Gtk.CheckButton(label=m) + cb.set_active(m == MODELS[0]) + self.checks[m] = cb + box.append(cb) + + popover.set_child(box) + popover.set_parent(menu_btn) + menu_btn.connect("clicked", lambda *_: popover.popup()) + + win.set_child(root) + win.present() + + def on_ask(self, _): + prompt = self.entry.get_text().strip() + if not prompt: + return + + models = [m for m, cb in self.checks.items() if cb.get_active()] + if not models: + self.set_output("❌ En az bir model seçmelisiniz.") + return + + self.ask_btn.set_sensitive(False) + self.set_output("⏳ Yanıt alınıyor…") + + threading.Thread( + target=self.run_ai, + args=(prompt, models), + daemon=True + ).start() + + def run_ai(self, prompt, models): + for model in models: + try: + result = subprocess.run( + [ + "python", + AI_SCRIPT, + self.image_path, + prompt, + model + ], + capture_output=True, + text=True, + timeout=120 + ) + + if result.returncode == 0 and result.stdout.strip(): + GLib.idle_add(self.on_done, result.stdout.strip()) + return + except Exception as e: + last_error = str(e) + + GLib.idle_add(self.on_done, f"❌ Tüm modeller başarısız.\n{last_error}") + + def on_done(self, text): + self.set_output(text) + self.ask_btn.set_sensitive(True) + return False + + def set_output(self, text): + buf = self.output.get_buffer() + buf.set_text(text) + + +if __name__ == "__main__": + if len(sys.argv) != 2: + print("Kullanım: ui.py ") + sys.exit(1) + + app = App(sys.argv[1]) + app.run()