refactor(aisidebar): restructure project and implement reasoning mode toggle
- Reorganize project structure and file locations - Add ReasoningController to manage model selection and reasoning mode - Update design and requirements for reasoning mode toggle - Implement model switching between Qwen3-4B-Instruct and Qwen3-4B-Thinking models - Remove deprecated files and consolidate project layout - Add new steering and specification documentation - Clean up and remove unnecessary files and directories - Prepare for enhanced AI sidebar functionality with more flexible model handling
This commit is contained in:
@@ -233,7 +233,11 @@ class ConversationArchive:
|
||||
|
||||
```python
|
||||
class ReasoningController:
|
||||
"""Manages reasoning mode state and API parameters."""
|
||||
"""Manages reasoning mode state and model selection."""
|
||||
|
||||
# Model names for reasoning toggle
|
||||
INSTRUCT_MODEL = "hf.co/unsloth/Qwen3-4B-Instruct-2507-GGUF:Q8_K_XL"
|
||||
THINKING_MODEL = "hf.co/unsloth/Qwen3-4B-Thinking-2507-GGUF:Q8_K_XL"
|
||||
|
||||
def __init__(self):
|
||||
self._enabled = False
|
||||
@@ -245,8 +249,9 @@ class ReasoningController:
|
||||
def toggle(self) -> bool:
|
||||
"""Toggle reasoning mode and persist preference."""
|
||||
|
||||
def get_chat_options(self) -> dict:
|
||||
"""Return Ollama API options for reasoning mode."""
|
||||
def get_model_name(self) -> str:
|
||||
"""Return the appropriate model name based on reasoning mode."""
|
||||
return self.THINKING_MODEL if self._enabled else self.INSTRUCT_MODEL
|
||||
```
|
||||
|
||||
#### UI Components
|
||||
@@ -254,41 +259,35 @@ class ReasoningController:
|
||||
Add toggle button to header area:
|
||||
|
||||
```python
|
||||
self._reasoning_toggle = Gtk.ToggleButton(label="🧠 Reasoning")
|
||||
self._reasoning_toggle.connect("toggled", self._on_reasoning_toggled)
|
||||
self._reasoning_toggle = widgets.Button(label="🧠 Reasoning: OFF")
|
||||
self._reasoning_toggle.connect("clicked", self._on_reasoning_toggled)
|
||||
```
|
||||
|
||||
#### Ollama Integration
|
||||
|
||||
When reasoning mode is enabled, pass additional options to Ollama:
|
||||
When reasoning mode is toggled, switch between models:
|
||||
|
||||
```python
|
||||
# Standard mode
|
||||
ollama.chat(model=model, messages=messages)
|
||||
# Get model based on reasoning mode
|
||||
model = self._reasoning_controller.get_model_name()
|
||||
|
||||
# Reasoning mode (model-dependent)
|
||||
ollama.chat(
|
||||
model=model,
|
||||
messages=messages,
|
||||
options={
|
||||
"temperature": 0.7,
|
||||
# Model-specific reasoning parameters
|
||||
}
|
||||
)
|
||||
# Use the selected model for chat
|
||||
ollama.chat(model=model, messages=messages)
|
||||
```
|
||||
|
||||
#### Message Formatting
|
||||
|
||||
When reasoning is enabled and model supports it:
|
||||
When using the thinking model:
|
||||
- Display thinking process in distinct style (italic, gray text)
|
||||
- Separate reasoning from final answer with visual divider
|
||||
- Use expandable/collapsible section for reasoning (optional)
|
||||
- Parse `<think>` tags from model output to extract reasoning content
|
||||
|
||||
#### Persistence
|
||||
|
||||
- Save reasoning preference to `~/.config/aisidebar/preferences.json`
|
||||
- Load preference on startup
|
||||
- Apply to all new conversations
|
||||
- Automatically switch models when preference changes
|
||||
|
||||
## Data Models
|
||||
|
||||
|
||||
@@ -62,15 +62,15 @@ This document outlines the requirements for enhancing the AI sidebar module for
|
||||
|
||||
### Requirement 5: Reasoning Mode Toggle
|
||||
|
||||
**User Story:** As a user, I want to enable or disable the model's reasoning output, so that I can choose whether to see the thinking process or just the final answer based on my needs.
|
||||
**User Story:** As a user, I want to toggle between a reasoning model and an instruct model, so that I can choose whether to use a model that shows its thinking process or one that provides direct answers.
|
||||
|
||||
#### Acceptance Criteria
|
||||
|
||||
1. THE AI Sidebar SHALL provide a toggle button or control to enable reasoning mode
|
||||
2. WHEN reasoning mode is enabled, THE AI Sidebar SHALL request and display the model's thinking process before the final answer
|
||||
3. WHEN reasoning mode is disabled, THE AI Sidebar SHALL request and display only the final answer without intermediate reasoning
|
||||
1. THE AI Sidebar SHALL provide a toggle button or control to switch between reasoning and instruct models
|
||||
2. WHEN reasoning mode is enabled, THE AI Sidebar SHALL switch to the Qwen3-4B-Thinking model and display the model's thinking process
|
||||
3. WHEN reasoning mode is disabled, THE AI Sidebar SHALL switch to the Qwen3-4B-Instruct model for direct answers
|
||||
4. THE AI Sidebar SHALL persist the reasoning mode preference across conversation sessions
|
||||
5. THE AI Sidebar SHALL visually distinguish reasoning content from final answer content when reasoning mode is enabled
|
||||
5. THE AI Sidebar SHALL visually distinguish reasoning content from final answer content when using the thinking model
|
||||
|
||||
### Requirement 6: Graceful Ollama Unavailability Handling
|
||||
|
||||
|
||||
@@ -1,19 +1,19 @@
|
||||
# Implementation Plan
|
||||
|
||||
- [ ] 1. Implement streaming response infrastructure
|
||||
- [x] 1. Implement streaming response infrastructure
|
||||
- Create StreamingHandler class in new file `streaming_handler.py` with token buffering, UI update methods, and stream state management
|
||||
- Add `_handle_stream_token()` method to SidebarWindow that uses GLib.idle_add for thread-safe UI updates
|
||||
- Implement token buffering logic (accumulate 3-5 tokens before UI update) to reduce overhead
|
||||
- _Requirements: 1.1, 1.2, 1.3, 1.4_
|
||||
|
||||
- [ ] 2. Integrate streaming into SidebarWindow
|
||||
- [x] 2. Integrate streaming into SidebarWindow
|
||||
- Modify `_request_response()` to use `ollama_client.stream_chat()` instead of blocking `chat()`
|
||||
- Update worker thread to iterate over stream and call `_handle_stream_token()` for each chunk
|
||||
- Add streaming state indicator (visual feedback during generation)
|
||||
- Handle stream errors and interruptions gracefully with try-except blocks
|
||||
- _Requirements: 1.1, 1.2, 1.3, 1.4_
|
||||
|
||||
- [ ] 3. Replace single-line Entry with multi-line TextView
|
||||
- [x] 3. Replace single-line Entry with multi-line TextView
|
||||
- Replace `Gtk.Entry` with `Gtk.TextView` wrapped in `Gtk.ScrolledWindow` in `_build_ui()`
|
||||
- Configure text view with word wrapping, min height 40px, max height 200px
|
||||
- Implement key event controller to handle Enter (submit) vs Shift+Enter (newline)
|
||||
@@ -21,37 +21,37 @@
|
||||
- Update `_on_submit()` to extract text from TextView buffer instead of Entry
|
||||
- _Requirements: 2.1, 2.2, 2.3, 2.4_
|
||||
|
||||
- [ ] 4. Create command processing system
|
||||
- [x] 4. Create command processing system
|
||||
- Create `command_processor.py` with CommandProcessor class
|
||||
- Implement command parsing logic with `is_command()` and `execute()` methods
|
||||
- Define CommandResult dataclass for structured command responses
|
||||
- Add command registry dictionary mapping command strings to handler methods
|
||||
- _Requirements: 3.1, 3.2, 3.3, 3.4, 3.5_
|
||||
|
||||
- [ ] 5. Implement conversation management commands
|
||||
- [ ] 5.1 Implement `/new` and `/clear` commands
|
||||
- [x] 5. Implement conversation management commands
|
||||
- [x] 5.1 Implement `/new` and `/clear` commands
|
||||
- Add `_cmd_new_conversation()` method to save current conversation and reset to fresh state
|
||||
- Clear message list UI and show confirmation message
|
||||
- _Requirements: 3.1, 3.2_
|
||||
|
||||
- [ ] 5.2 Implement `/models` command
|
||||
- [x] 5.2 Implement `/models` command
|
||||
- Add `_cmd_list_models()` method to query and display available models
|
||||
- Format model list with current model highlighted
|
||||
- _Requirements: 3.3_
|
||||
|
||||
- [ ] 5.3 Implement `/model` command
|
||||
- [x] 5.3 Implement `/model` command
|
||||
- Add `_cmd_switch_model()` method to validate and switch active model
|
||||
- Update model label in header UI
|
||||
- _Requirements: 3.4_
|
||||
|
||||
- [ ] 5.4 Integrate CommandProcessor into SidebarWindow
|
||||
- [x] 5.4 Integrate CommandProcessor into SidebarWindow
|
||||
- Add CommandProcessor instance to SidebarWindow initialization
|
||||
- Modify `_on_submit()` to check for commands before processing as user message
|
||||
- Display command results as system messages with distinct styling
|
||||
- _Requirements: 3.5_
|
||||
|
||||
- [ ] 6. Implement conversation archive system
|
||||
- [ ] 6.1 Create ConversationArchive class
|
||||
- [x] 6. Implement conversation archive system
|
||||
- [x] 6.1 Create ConversationArchive class
|
||||
- Create `conversation_archive.py` with ConversationArchive class
|
||||
- Implement `list_conversations()` to scan storage directory and return metadata
|
||||
- Implement `archive_conversation()` to save with timestamp-based ID format
|
||||
@@ -59,60 +59,60 @@
|
||||
- Define ConversationMetadata dataclass
|
||||
- _Requirements: 4.1, 4.2_
|
||||
|
||||
- [ ] 6.2 Implement conversation loading
|
||||
- [x] 6.2 Implement conversation loading
|
||||
- Add `load_conversation()` method to ConversationArchive
|
||||
- Handle JSON parsing errors and missing files gracefully
|
||||
- Return ConversationState compatible with existing ConversationManager
|
||||
- _Requirements: 4.4_
|
||||
|
||||
- [ ] 6.3 Implement `/list` and `/resume` commands
|
||||
- [x] 6.3 Implement `/list` and `/resume` commands
|
||||
- Add `_cmd_list_conversations()` to display archived conversations with metadata
|
||||
- Add `_cmd_resume_conversation()` to load and display selected conversation
|
||||
- Update SidebarWindow to repopulate message list from loaded conversation
|
||||
- _Requirements: 4.3, 4.4, 4.5_
|
||||
|
||||
- [ ] 7. Implement reasoning mode toggle
|
||||
- [ ] 7.1 Create ReasoningController class
|
||||
- [x] 7. Implement reasoning mode toggle
|
||||
- [x] 7.1 Create ReasoningController class
|
||||
- Create `reasoning_controller.py` with ReasoningController class
|
||||
- Implement preference persistence to `~/.config/aisidebar/preferences.json`
|
||||
- Add `toggle()`, `is_enabled()`, and `get_chat_options()` methods
|
||||
- Define PreferencesState dataclass
|
||||
- _Requirements: 5.4_
|
||||
|
||||
- [ ] 7.2 Add reasoning toggle UI
|
||||
- [x] 7.2 Add reasoning toggle UI
|
||||
- Add ToggleButton to header area in `_build_ui()`
|
||||
- Connect toggle signal to `_on_reasoning_toggled()` callback
|
||||
- Update button state from persisted preference on startup
|
||||
- _Requirements: 5.1_
|
||||
|
||||
- [ ] 7.3 Integrate reasoning mode with Ollama calls
|
||||
- [x] 7.3 Integrate reasoning mode with Ollama calls
|
||||
- Modify `_request_response()` to include reasoning options when enabled
|
||||
- Pass model-specific parameters via `get_chat_options()`
|
||||
- Handle both streaming and non-streaming modes with reasoning
|
||||
- _Requirements: 5.2, 5.3_
|
||||
|
||||
- [ ] 7.4 Implement reasoning content formatting
|
||||
- [x] 7.4 Implement reasoning content formatting
|
||||
- Add visual distinction for reasoning content (italic, gray text, or expandable section)
|
||||
- Separate reasoning from final answer with visual divider
|
||||
- Update message rendering to handle reasoning metadata
|
||||
- _Requirements: 5.5_
|
||||
|
||||
- [-] 8. Implement graceful Ollama unavailability handling
|
||||
- [ ] 8.1 Update OllamaClient initialization
|
||||
- [x] 8. Implement graceful Ollama unavailability handling
|
||||
- [x] 8.1 Update OllamaClient initialization
|
||||
- Modify `__init__()` to never raise exceptions during initialization
|
||||
- Add connection check that sets internal availability flag
|
||||
- Update `list_models()` to return empty list instead of raising on connection failure
|
||||
- Update `chat()` and `stream_chat()` to return error messages instead of raising
|
||||
- _Requirements: 6.1, 6.3, 6.5_
|
||||
|
||||
- [ ] 8.2 Create OllamaAvailabilityMonitor
|
||||
- [x] 8.2 Create OllamaAvailabilityMonitor
|
||||
- Create `ollama_monitor.py` with OllamaAvailabilityMonitor class
|
||||
- Implement periodic availability checking using GLib.timeout_add (30s interval)
|
||||
- Add callback mechanism to notify UI of state changes
|
||||
- Ensure checks are non-blocking and don't impact UI responsiveness
|
||||
- _Requirements: 6.4_
|
||||
|
||||
- [ ] 8.3 Update SidebarWindow for Ollama unavailability
|
||||
- [x] 8.3 Update SidebarWindow for Ollama unavailability
|
||||
- Initialize OllamaAvailabilityMonitor in SidebarWindow
|
||||
- Display "Ollama not running" status message when unavailable at startup
|
||||
- Update model label to show connection status
|
||||
@@ -120,7 +120,7 @@
|
||||
- Add callback to re-enable features when Ollama becomes available
|
||||
- _Requirements: 6.1, 6.2, 6.4_
|
||||
|
||||
- [ ] 8.4 Add user-friendly error messages
|
||||
- [x] 8.4 Add user-friendly error messages
|
||||
- Display clear instructions when user tries to chat without Ollama
|
||||
- Show notification when Ollama connection is restored
|
||||
- Update all command handlers to check Ollama availability
|
||||
|
||||
23
.kiro/steering/product.md
Normal file
23
.kiro/steering/product.md
Normal file
@@ -0,0 +1,23 @@
|
||||
---
|
||||
inclusion: always
|
||||
---
|
||||
|
||||
# Product Overview
|
||||
|
||||
AI Sidebar is a slide-in chat interface for the Ignis desktop environment that provides local AI assistance through Ollama integration.
|
||||
|
||||
## Core Features
|
||||
|
||||
- Slide-in sidebar from the left side with smooth animations
|
||||
- Local AI chat using Ollama models
|
||||
- Automatic conversation persistence across sessions
|
||||
- Material Design 3 theming that matches Ignis
|
||||
- Keyboard shortcut toggle support
|
||||
- Automatic Ollama availability monitoring with graceful degradation
|
||||
|
||||
## User Experience
|
||||
|
||||
- Clicking outside the sidebar closes it (same as QuickCenter)
|
||||
- Conversations are automatically saved to `~/.config/ignis/modules/aisidebar/data/conversations/`
|
||||
- The UI gracefully handles Ollama being unavailable and notifies users when connectivity is restored
|
||||
- Default width is 400px to match QuickCenter
|
||||
70
.kiro/steering/structure.md
Normal file
70
.kiro/steering/structure.md
Normal file
@@ -0,0 +1,70 @@
|
||||
---
|
||||
inclusion: always
|
||||
---
|
||||
|
||||
# Project Structure
|
||||
|
||||
## File Organization
|
||||
|
||||
```
|
||||
aisidebar/
|
||||
├── __init__.py # Module exports (AISidebar class)
|
||||
├── aisidebar.py # Main RevealerWindow implementation
|
||||
├── chat_widget.py # Chat UI widget with message handling
|
||||
├── ollama_client.py # HTTP client for Ollama REST API
|
||||
├── ollama_monitor.py # Availability monitoring with callbacks
|
||||
├── conversation_manager.py # Conversation persistence layer
|
||||
└── data/
|
||||
└── conversations/ # JSON conversation files (auto-created)
|
||||
└── default.json # Default conversation transcript
|
||||
```
|
||||
|
||||
## Module Responsibilities
|
||||
|
||||
### `aisidebar.py`
|
||||
- Main window class extending `widgets.RevealerWindow`
|
||||
- Handles slide-in animation from left side
|
||||
- Manages window visibility and keyboard focus
|
||||
- Integrates with Ignis WindowManager
|
||||
|
||||
### `chat_widget.py`
|
||||
- Complete chat UI implementation
|
||||
- Message list rendering and scrolling
|
||||
- Input handling and submission
|
||||
- Background thread management for AI requests
|
||||
- Ollama availability monitoring integration
|
||||
|
||||
### `ollama_client.py`
|
||||
- Low-level HTTP client for Ollama API
|
||||
- Model listing with caching
|
||||
- Blocking chat API calls
|
||||
- Connection health checking
|
||||
- Graceful error handling without exceptions
|
||||
|
||||
### `ollama_monitor.py`
|
||||
- Periodic availability checking (30s interval)
|
||||
- Callback-based state change notifications
|
||||
- GLib timeout integration for non-blocking checks
|
||||
|
||||
### `conversation_manager.py`
|
||||
- JSON-based conversation persistence
|
||||
- Atomic file writes for data safety
|
||||
- Message validation (system/user/assistant roles)
|
||||
- Timestamp tracking for messages
|
||||
|
||||
## Naming Conventions
|
||||
|
||||
- Private methods/attributes: `_method_name`, `_attribute_name`
|
||||
- Widget references: `self._widget_name` (e.g., `self._entry`, `self._message_list`)
|
||||
- CSS classes: kebab-case (e.g., `ai-sidebar`, `ai-sidebar-content`)
|
||||
- Constants: UPPER_SNAKE_CASE (e.g., `VALID_ROLES`, `DEFAULT_CONVERSATION_ID`)
|
||||
|
||||
## Code Style
|
||||
|
||||
- Type hints on function signatures
|
||||
- Docstrings for classes and public methods
|
||||
- Dataclasses for structured data (`ConversationState`)
|
||||
- Context managers for file operations
|
||||
- Property decorators for computed attributes
|
||||
- Threading: daemon threads for background work
|
||||
- Error messages: user-friendly with actionable instructions
|
||||
63
.kiro/steering/tech.md
Normal file
63
.kiro/steering/tech.md
Normal file
@@ -0,0 +1,63 @@
|
||||
---
|
||||
inclusion: always
|
||||
---
|
||||
|
||||
# Technology Stack
|
||||
|
||||
## Framework & Environment
|
||||
|
||||
- **Platform**: Ignis desktop environment (Python-based GTK4 framework)
|
||||
- **Python Version**: 3.10+
|
||||
- **UI Framework**: GTK4 via Ignis widgets
|
||||
- **Async/Threading**: GLib for main loop, Python threading for background tasks
|
||||
|
||||
## Key Dependencies
|
||||
|
||||
- `ignis` - Desktop environment framework providing widgets and window management
|
||||
- `ollama` - Python package for Ollama API integration
|
||||
- GTK4 (`gi.repository.GLib`) - UI toolkit and event loop
|
||||
|
||||
## Architecture Patterns
|
||||
|
||||
### Widget System
|
||||
- Uses Ignis widget abstractions (`widgets.Box`, `widgets.RevealerWindow`, etc.)
|
||||
- Material Design 3 styling via CSS classes
|
||||
- Revealer-based slide animations
|
||||
|
||||
### API Communication
|
||||
- Direct HTTP calls to Ollama REST API (no external HTTP library)
|
||||
- Uses `urllib.request` for HTTP operations
|
||||
- Timeout handling: 2s for health checks, 5s for model lists, 120s for chat
|
||||
|
||||
### State Management
|
||||
- Conversation persistence via JSON files
|
||||
- Atomic file writes using `tempfile` and `os.replace()`
|
||||
- In-memory caching for model lists
|
||||
|
||||
### Threading Model
|
||||
- UI operations on GLib main thread
|
||||
- AI requests in background daemon threads
|
||||
- `GLib.idle_add()` for thread-safe UI updates
|
||||
|
||||
### Error Handling
|
||||
- Graceful degradation when Ollama is unavailable
|
||||
- Availability monitoring with 30-second polling interval
|
||||
- User-facing error messages instead of exceptions
|
||||
|
||||
## Common Commands
|
||||
|
||||
Since this is an Ignis module, there are no build/test commands. The module is loaded directly by Ignis:
|
||||
|
||||
```bash
|
||||
# Reload Ignis to apply changes
|
||||
ignis reload
|
||||
|
||||
# Run Ignis with console output for debugging
|
||||
ignis
|
||||
|
||||
# Check Ollama status
|
||||
curl http://127.0.0.1:11434/api/tags
|
||||
|
||||
# List installed Ollama models
|
||||
ollama list
|
||||
```
|
||||
Reference in New Issue
Block a user