Skip to content

Added an ai assistant tab #226

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 6 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 29 additions & 0 deletions config/data.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,20 @@
NOTIF_POS_KEY = "notif_pos"
NOTIF_POS_DEFAULT = "Top"

# AI API Keys
AI_OPENAI_KEY = "ai_openai_key"
AI_GEMINI_KEY = "ai_gemini_key"
AI_CLAUDE_KEY = "ai_claude_key"
AI_GROK_KEY = "ai_grok_key"
AI_DEEPSEEK_KEY = "ai_deepseek_key"

# AI Model Selections
AI_OPENAI_MODEL = "ai_openai_model"
AI_GEMINI_MODEL = "ai_gemini_model"
AI_CLAUDE_MODEL = "ai_claude_model"
AI_GROK_MODEL = "ai_grok_model"
AI_DEEPSEEK_MODEL = "ai_deepseek_model"

CACHE_DIR = str(GLib.get_user_cache_dir()) + f"/{APP_NAME}"

USERNAME = os.getlogin()
Expand Down Expand Up @@ -106,6 +120,20 @@ def load_config():
METRICS_SMALL_VISIBLE = config.get(
"metrics_small_visible", {"cpu": True, "ram": True, "disk": True, "gpu": True}
)

# AI API Keys
AI_OPENAI_API_KEY = config.get(AI_OPENAI_KEY, "")
AI_GEMINI_API_KEY = config.get(AI_GEMINI_KEY, "")
AI_CLAUDE_API_KEY = config.get(AI_CLAUDE_KEY, "")
AI_GROK_API_KEY = config.get(AI_GROK_KEY, "")
AI_DEEPSEEK_API_KEY = config.get(AI_DEEPSEEK_KEY, "")

# AI Model Selections (with backward compatibility for old format)
AI_OPENAI_MODEL = config.get(AI_OPENAI_MODEL, config.get("gpt-3.5-turbo", "gpt-3.5-turbo"))
AI_GEMINI_MODEL = config.get(AI_GEMINI_MODEL, config.get("gemini-1.5-pro", "gemini-1.5-pro"))
AI_CLAUDE_MODEL = config.get(AI_CLAUDE_MODEL, config.get("claude-3-sonnet-20240229", "claude-3-sonnet-20240229"))
AI_GROK_MODEL = config.get(AI_GROK_MODEL, config.get("grok-beta", "grok-beta"))
AI_DEEPSEEK_MODEL = config.get(AI_DEEPSEEK_MODEL, config.get("deepseek-chat", "deepseek-chat"))
else:
WALLPAPERS_DIR = WALLPAPERS_DIR_DEFAULT
BAR_POSITION = "Left"
Expand All @@ -126,6 +154,7 @@ def load_config():
PANEL_POSITION = PANEL_POSITION_DEFAULT
NOTIF_POS = NOTIF_POS_DEFAULT


BAR_COMPONENTS_VISIBILITY = {
"button_apps": True,
"systray": True,
Expand Down
2 changes: 2 additions & 0 deletions config/settings_constants.py
Original file line number Diff line number Diff line change
Expand Up @@ -101,4 +101,6 @@
},
"limited_apps_history": ["Spotify"],
"history_ignored_apps": ["Hyprshot"],
'prefix_ai': "SUPER SHIFT",
'suffix_ai': "A",
}
137 changes: 137 additions & 0 deletions config/settings_gui.py
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@ def __init__(self, show_lock_checkbox: bool, show_idle_checkbox: bool, **kwargs)
self.key_bindings_tab_content = self.create_key_bindings_tab()
self.appearance_tab_content = self.create_appearance_tab()
self.system_tab_content = self.create_system_tab()
self.ai_tab_content = self.create_ai_tab()
self.about_tab_content = self.create_about_tab()

self.tab_stack.add_titled(
Expand All @@ -72,6 +73,7 @@ def __init__(self, show_lock_checkbox: bool, show_idle_checkbox: bool, **kwargs)
self.appearance_tab_content, "appearance", "Appearance"
)
self.tab_stack.add_titled(self.system_tab_content, "system", "System")
self.tab_stack.add_titled(self.ai_tab_content, "ai", "AI")
self.tab_stack.add_titled(self.about_tab_content, "about", "About")

tab_switcher = Gtk.StackSwitcher()
Expand Down Expand Up @@ -153,6 +155,7 @@ def create_key_bindings_tab(self):
"prefix_restart_inspector",
"suffix_restart_inspector",
),
("AI", 'prefix_ai', 'suffix_ai'),
]

for i, (label_text, prefix_key, suffix_key) in enumerate(bindings):
Expand Down Expand Up @@ -878,6 +881,132 @@ def _add_disk_entry_widget(self, path):
self.disk_entries.add(bar)
self.disk_entries.show_all()


def create_ai_tab(self):
scrolled_window = ScrolledWindow(
h_scrollbar_policy="never",
v_scrollbar_policy="automatic",
h_expand=True,
v_expand=True,
propagate_width=False,
propagate_height=False
)

main_vbox = Box(orientation="v", spacing=15, style="margin: 20px;")
scrolled_window.add(main_vbox)

# Title
title_label = Label(markup="<b>AI API Keys</b>", h_align="start", style="font-size: 1.2em; margin-bottom: 10px;")
main_vbox.add(title_label)

# Description
desc_label = Label(
label="Enter your API keys for the AI models you want to use. Leave empty if you don't have a key for a specific model.",
h_align="start",
style="margin-bottom: 20px; color: #888;"
)
main_vbox.add(desc_label)

# API Keys Grid
api_grid = Gtk.Grid()
api_grid.set_column_spacing(15)
api_grid.set_row_spacing(10)
api_grid.set_margin_start(5)
api_grid.set_margin_end(5)

# Headers
model_label = Label(markup="<b>AI Service</b>", h_align="start")
key_label = Label(markup="<b>API Key</b>", h_align="start")
model_select_label = Label(markup="<b>Model</b>", h_align="start")
api_grid.attach(model_label, 0, 0, 1, 1)
api_grid.attach(key_label, 1, 0, 1, 1)
api_grid.attach(model_select_label, 2, 0, 1, 1)

# Import data module to get current API keys and models
from .data import (
AI_OPENAI_API_KEY, AI_GEMINI_API_KEY, AI_CLAUDE_API_KEY,
AI_GROK_API_KEY, AI_DEEPSEEK_API_KEY,
AI_OPENAI_KEY, AI_GEMINI_KEY, AI_CLAUDE_KEY,
AI_GROK_KEY, AI_DEEPSEEK_KEY,
AI_OPENAI_MODEL, AI_GEMINI_MODEL, AI_CLAUDE_MODEL,
AI_GROK_MODEL, AI_DEEPSEEK_MODEL
)

# AI Models and their API key entries
self.ai_entries = []
self.ai_model_combos = {}

# Get the actual model values from the imported constants
# These are the actual model names like "gpt-3.5-turbo", "gemini-1.5-pro", etc.
from .data import (
AI_OPENAI_MODEL as OPENAI_MODEL_VALUE,
AI_GEMINI_MODEL as GEMINI_MODEL_VALUE,
AI_CLAUDE_MODEL as CLAUDE_MODEL_VALUE,
AI_GROK_MODEL as GROK_MODEL_VALUE,
AI_DEEPSEEK_MODEL as DEEPSEEK_MODEL_VALUE
)

# Create a mapping of model keys to their current values
model_values = {
AI_OPENAI_MODEL: OPENAI_MODEL_VALUE,
AI_GEMINI_MODEL: GEMINI_MODEL_VALUE,
AI_CLAUDE_MODEL: CLAUDE_MODEL_VALUE,
AI_GROK_MODEL: GROK_MODEL_VALUE,
AI_DEEPSEEK_MODEL: DEEPSEEK_MODEL_VALUE,
}

# Define available models for each service
ai_services = [
("Chat GPT (OpenAI)", AI_OPENAI_KEY, AI_OPENAI_API_KEY, AI_OPENAI_MODEL, [
"gpt-3.5-turbo", "gpt-3.5-turbo-16k", "gpt-4", "gpt-4-turbo", "gpt-4o", "gpt-4o-mini"
]),
("Gemini (Google)", AI_GEMINI_KEY, AI_GEMINI_API_KEY, AI_GEMINI_MODEL, [
"gemini-2.5-flash", "gemini-2.5-pro", "gemini-2.0-flash", "gemini-2.0-flash-exp",
"gemini-1.5-pro", "gemini-1.5-flash", "gemini-1.5-pro-latest", "gemini-1.5-flash-latest",
"gemini-1.0-pro", "gemini-1.0-pro-vision"
]),
("Claude (Anthropic)", AI_CLAUDE_KEY, AI_CLAUDE_API_KEY, AI_CLAUDE_MODEL, [
"claude-3-sonnet-20240229", "claude-3-opus-20240229", "claude-3-haiku-20240307",
"claude-3-5-sonnet-20241022", "claude-3-5-haiku-20241022"
]),
("Grok (xAI)", AI_GROK_KEY, AI_GROK_API_KEY, AI_GROK_MODEL, [
"grok-beta", "grok-2"
]),
("Deepseek", AI_DEEPSEEK_KEY, AI_DEEPSEEK_API_KEY, AI_DEEPSEEK_MODEL, [
"deepseek-chat", "deepseek-coder", "deepseek-llm-7b-chat"
]),
]

for i, (service_name, key_name, current_key, model_key, available_models) in enumerate(ai_services):
row = i + 1
model_label = Label(label=service_name, h_align="start")
key_entry = Entry(text=current_key, tooltip_text=f"Enter your {service_name} API key")
key_entry.set_visibility(False) # Hide the API key for security

# Create model selection combo box
model_combo = Gtk.ComboBoxText()
for model in available_models:
model_combo.append_text(model)

# Set current model
current_model = model_values[model_key]
model_combo.set_active(available_models.index(current_model) if current_model in available_models else 0)

api_grid.attach(model_label, 0, row, 1, 1)
api_grid.attach(key_entry, 1, row, 1, 1)
api_grid.attach(model_combo, 2, row, 1, 1)

self.ai_entries.append((key_name, key_entry))
self.ai_model_combos[model_key] = model_combo

main_vbox.add(api_grid)

# Add some spacing
main_vbox.add(Box(v_expand=True))

return scrolled_window


def create_about_tab(self):
vbox = Box(orientation="v", spacing=18, style="margin: 30px;")
vbox.add(
Expand Down Expand Up @@ -1043,6 +1172,14 @@ def on_accept(self, widget):
and isinstance(child.get_children()[0], Entry)
]

# Save AI API keys and model selections
for key_name, key_entry in self.ai_entries:
current_bind_vars_snapshot[key_name] = key_entry.get_text()

# Save AI model selections
for model_key, model_combo in self.ai_model_combos.items():
selected_model = model_combo.get_active_text()
current_bind_vars_snapshot[model_key] = selected_model
# Parse notification app lists
def parse_app_list(text):
"""Parse comma-separated app names with quotes"""
Expand Down
1 change: 1 addition & 0 deletions config/settings_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -271,6 +271,7 @@ def generate_hyprconf() -> str:
bind = {bind_vars.get("prefix_caffeine", "SUPER SHIFT")}, {bind_vars.get("suffix_caffeine", "M")}, exec, $fabricSend 'notch.dashboard.widgets.buttons.caffeine_button.toggle_inhibit(external=True)' # Toggle Caffeine
bind = {bind_vars.get("prefix_css", "SUPER SHIFT")}, {bind_vars.get("suffix_css", "B")}, exec, $fabricSend 'app.set_css()' # Reload CSS
bind = {bind_vars.get("prefix_restart_inspector", "SUPER CTRL ALT")}, {bind_vars.get("suffix_restart_inspector", "B")}, exec, killall {APP_NAME}; uwsm-app $(GTK_DEBUG=interactive python {home}/.config/{APP_NAME_CAP}/main.py) # Restart with inspector
bind = {bind_vars.get('prefix_ai', 'SUPER SHIFT')}, {bind_vars.get('suffix_ai', 'A')}, exec, $fabricSend 'notch.open_notch("ai")' # AI Panel

# Wallpapers directory: {bind_vars.get("wallpapers_dir", "~/.config/Ax-Shell/assets/wallpapers_example")}

Expand Down
4 changes: 3 additions & 1 deletion main.css
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
@import url("./styles/wallpapers.css");
@import url("./styles/systemprofiles.css");
@import url("./styles/workspaces.css");
@import url("./styles/ai.css");

* {
all: unset;
Expand Down Expand Up @@ -114,7 +115,7 @@
}

#later-button:active {
background-color: var(--surface-bright);
background-color: var(--primary);
}

#toggle-updater-button {
Expand All @@ -129,3 +130,4 @@
background-color: var(--outline);
border-radius: 8px;
}

Loading