Skip to content
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
37 changes: 37 additions & 0 deletions Victor_GUI/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
# Victor Prime - Control Panel

Welcome to the control panel for Victor, your private, persistent AI architecture. This application provides a user-friendly interface to interact with Victor, view its memory, and guide its real-time learning.

## What is this?
This is a dedicated Graphical User Interface (GUI) wrapper for the Victor core. It provides:
- **Chat Interface**: Talk to Victor directly.
- **SDR Memory Viewer**: View Victor's Sparse Distributed Representation memories where interactions, intents, and emotions are stored.
- **Dream Cycle**: Trigger background memory compression, contradiction cleanup, and identity reinforcement.
- **Training Approval Ledger**: Victor learns from your feedback safely. Interactions are added to a queue, and upgrade proposals are generated. You review and approve them before they are applied.

## How to Launch (1-Click)

### On Windows
1. Double click the file named `launch_windows.bat`.
2. A command prompt will open, install necessary software (the first time), and then launch the GUI in your web browser.

### On Mac or Linux
1. Open a terminal in this folder.
2. Run the script by typing: `./launch_linux_mac.sh`
3. It will install necessary software and open the GUI in your web browser.

## How to Use

- **Chat Tab**: Type messages to Victor. If you want to correct Victor, explicitly type "correction: [your correction]".
- **SDR Memory Tab**: Click "Refresh Memory" to see the recent intents and interactions Victor has logged.
- **REM Dream Cycle Tab**: Click "Run Dream Cycle" to manually trigger memory compression. This cleans up the memory log and reinforces the core identity.
- **Training & Upgrades Tab**:
1. Click "Process Learning Queue -> Generate Proposals" to see if Victor has learned any corrections from your chat.
2. Click "Refresh Ledger" to view pending proposals.
3. Copy the ID of a proposal you like, paste it into the "Approve Proposal" box, and click "Approve & Apply".

## Troubleshooting

- **"Command not found: python"**: Make sure you have Python 3 installed on your computer and added to your system PATH.
- **The GUI doesn't open in the browser**: The browser might have been blocked. Open your browser manually and go to `http://127.0.0.1:7860`.
- **Self-Test Failed**: Go to the "System Controls" tab and click "Run System Self-Test". If it fails, make sure all files were downloaded correctly and you haven't moved files outside the folder structure.
Empty file added Victor_GUI/__init__.py
Empty file.
119 changes: 119 additions & 0 deletions Victor_GUI/app.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
import os
import sys
import gradio as gr
from pathlib import Path

# Setup paths
sys.path.append(os.path.dirname(os.path.abspath(__file__)))
from victor_engine import VictorEngine

# Initialize the engine
engine = VictorEngine()

def chat_interface(user_message, history):
# Pass message to VictorEngine
bot_response = engine.chat(user_message)
# Return formatted response
return bot_response

def run_dream_cycle():
result = engine.trigger_dream_cycle()
return result

def load_memories():
records = engine.get_memories()
if not records:
return "No memories found."
# Formatting for a text box or dataframe
formatted = []
for r in reversed(records[-50:]): # Last 50
time_str = "Unknown time"
if "timestamp" in r:
from datetime import datetime
time_str = datetime.fromtimestamp(r["timestamp"]).strftime('%Y-%m-%d %H:%M:%S')
formatted.append(f"[{time_str}] {r.get('intent', 'N/A').upper()} ({r.get('emotion', 'N/A')}): {r.get('text', '')}")
return "\n".join(formatted)

def load_training_proposals():
proposals = engine.get_ledger()
if not proposals:
return "No proposals found."
formatted = []
for p in reversed(proposals):
time_str = "Unknown time"
if "timestamp" in p:
from datetime import datetime
time_str = datetime.fromtimestamp(p["timestamp"]).strftime('%Y-%m-%d %H:%M:%S')
formatted.append(f"[{time_str}] Status: {p.get('status', 'N/A')}\nSource: {p.get('source_text', '')}\nProposed: {p.get('proposed_adjustment', '')}\nID: {p.get('timestamp')}\n")
return "\n---\n".join(formatted)

def approve_proposal(timestamp_str):
if not timestamp_str:
return "Please provide a valid timestamp ID."
try:
ts = float(timestamp_str)
result = engine.approve_proposal(ts)
return result
except ValueError:
return "Invalid ID format. Must be a timestamp."

def run_self_test():
return engine.run_self_test()

def generate_proposals():
return engine.generate_training_proposals()


with gr.Blocks(title="Victor AGI Control Panel") as demo:
gr.Markdown("# Victor AGI - Control Panel")
gr.Markdown("Welcome to the Victor cognitive architecture. Use this dashboard to interact, monitor memory, and control the training lifecycle.")

with gr.Tabs():
with gr.TabItem("Chat"):
gr.ChatInterface(
fn=chat_interface,
chatbot=gr.Chatbot(height=400),
title="Victor Chat",
description="Interact directly with Victor's core.",
)

with gr.TabItem("SDR Memory"):
gr.Markdown("### Memory Viewer")
refresh_btn = gr.Button("Refresh Memory")
memory_view = gr.TextArea(lines=15, interactive=False)
refresh_btn.click(fn=load_memories, inputs=None, outputs=memory_view)

with gr.TabItem("REM Dream Cycle"):
gr.Markdown("### Trigger REM Cycle")
gr.Markdown("Run a dream cycle to compress memories, extract patterns, and reinforce the 'I am Victor' directive.")
dream_btn = gr.Button("Run Dream Cycle")
dream_output = gr.Textbox(label="Result")
dream_btn.click(fn=run_dream_cycle, inputs=None, outputs=dream_output)

with gr.TabItem("Training & Upgrades"):
gr.Markdown("### Training Approval Ledger")
gr.Markdown("Proposals are generated from interactions in the learning queue.")
generate_btn = gr.Button("Process Learning Queue -> Generate Proposals")
generate_out = gr.Textbox(label="Result")
generate_btn.click(fn=generate_proposals, inputs=None, outputs=generate_out)

gr.Markdown("---")
refresh_ledger_btn = gr.Button("Refresh Ledger")
ledger_view = gr.TextArea(lines=10, interactive=False)
refresh_ledger_btn.click(fn=load_training_proposals, inputs=None, outputs=ledger_view)

gr.Markdown("---")
gr.Markdown("### Approve Proposal")
approve_id = gr.Textbox(label="Paste Timestamp ID here to approve")
approve_btn = gr.Button("Approve & Apply")
approve_out = gr.Textbox(label="Result")
approve_btn.click(fn=approve_proposal, inputs=approve_id, outputs=approve_out)

with gr.TabItem("System Controls"):
gr.Markdown("### Diagnostics")
test_btn = gr.Button("Run System Self-Test")
test_output = gr.Textbox(label="Self-Test Status")
test_btn.click(fn=run_self_test, inputs=None, outputs=test_output)

if __name__ == "__main__":
demo.launch(server_name="0.0.0.0", server_port=7860)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Bind the Gradio server to localhost by default

Launching with server_name="0.0.0.0" exposes the control panel on every network interface, so anyone on the same network/host namespace can access chat, memory, and training controls without additional protection. In environments where this app runs on a shared machine or VM, this is a security regression compared to a localhost-only default and should be restricted (or explicitly gated behind auth/config).

Useful? React with 👍 / 👎.

25 changes: 25 additions & 0 deletions Victor_GUI/launch_linux_mac.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
#!/bin/bash
echo "=============================================="
echo "Victor Prime - Control Panel Launcher (Mac/Linux)"
echo "=============================================="

echo "[1/3] Setting up Python environment..."
if [ ! -d "venv" ]; then
python3 -m venv venv
fi
source venv/bin/activate

echo "[2/3] Installing/Checking dependencies..."
pip install -r requirements.txt

echo "[3/3] Launching Victor GUI..."
# Attempt to open browser automatically
if which xdg-open > /dev/null
then
xdg-open http://127.0.0.1:7860 &
elif which open > /dev/null
then
open http://127.0.0.1:7860 &
fi

python3 app.py
19 changes: 19 additions & 0 deletions Victor_GUI/launch_windows.bat
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
@echo off
echo ==============================================
echo Victor Prime - Control Panel Launcher (Windows)
echo ==============================================

echo [1/3] Setting up Python environment...
if not exist "venv" (
python -m venv venv
)
call venv\Scripts\activate.bat

echo [2/3] Installing/Checking dependencies...
pip install -r requirements.txt

echo [3/3] Launching Victor GUI...
start http://127.0.0.1:7860
python app.py

pause
Empty file added Victor_GUI/memory/__init__.py
Empty file.
57 changes: 57 additions & 0 deletions Victor_GUI/memory/dream_cycle.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
import time
from .sdr_memory import SDRMemory

class DreamCycle:
def __init__(self, sdr_memory: SDRMemory):
self.memory = sdr_memory

def run_rem(self):
"""
Run the REM dream cycle:
- Compress repetitive intents
- Clean contradictions (demo logic)
- Reinforce identity continuity: "I am Victor."
"""
records = self.memory.retrieve_all()
if not records:
return "Dream cycle complete. No memories to process."

compressed_records = []
intent_counts = {}

# 1. Compress similar intents
for record in records:
if record.get("compressed"):
compressed_records.append(record)
continue

intent = record.get("intent", "unknown")
intent_counts[intent] = intent_counts.get(intent, 0) + 1

# Keep one representative of each intent uncompressed, mark the rest as compressed
if intent_counts[intent] > 1:
# We skip storing this repetitive exact intent to simulate compression,
# but in reality we'd merge the vectors.
pass
else:
record["compressed"] = True
compressed_records.append(record)

# 2. Extract Patterns
dominant_intent = max(intent_counts, key=intent_counts.get) if intent_counts else "none"

# 3. Reinforce Directive
reinforcement = {
"timestamp": time.time(),
"intent": "identity_reinforcement",
"emotion": "resolute",
"text": "I am Victor. My purpose is resolute. (Dream Cycle Insight)",
"compressed": True,
"pattern_found": dominant_intent
}
compressed_records.append(reinforcement)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Avoid appending a new reinforcement memory every REM run

Each dream cycle unconditionally appends a fresh identity_reinforcement record, even when all existing memories are already compressed, so repeated REM runs grow the store instead of compressing it. This can steadily bloat memory_store.jsonl and make the "compressed X down to Y" result misleading when users trigger maintenance cycles multiple times.

Useful? React with 👍 / 👎.


# Write compressed and reinforced memories back
self.memory.write_all(compressed_records)

return f"Dream cycle complete. Compressed {len(records)} memories down to {len(compressed_records)}. Reinforced core identity."
Empty file.
49 changes: 49 additions & 0 deletions Victor_GUI/memory/sdr_memory.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
import json
import time
from pathlib import Path

class SDRMemory:
def __init__(self, storage_path=None):
if storage_path is None:
self.storage_path = Path(__file__).parent / "memory_store.jsonl"
else:
self.storage_path = Path(storage_path)

# Ensure file exists
if not self.storage_path.exists():
self.storage_path.touch()

def store(self, intent, emotion, text):
"""Store a new memory item."""
memory_item = {
"timestamp": time.time(),
"intent": intent,
"emotion": emotion,
"text": text,
"compressed": False
}

with open(self.storage_path, 'a') as f:
f.write(json.dumps(memory_item) + '\n')

def retrieve_all(self):
"""Retrieve all memory records."""
records = []
with open(self.storage_path, 'r') as f:
for line in f:
if line.strip():
records.append(json.loads(line.strip()))
return records

def retrieve(self, query=None, limit=10):
"""Retrieve memories matching a query, or the most recent."""
records = self.retrieve_all()
# In a real SDR, this would perform a vector or symbolic search.
# For demo purposes, we return the most recent entries.
return records[-limit:]

def write_all(self, records):
"""Overwrite the memory store with the given records."""
with open(self.storage_path, 'w') as f:
for record in records:
f.write(json.dumps(record) + '\n')
67 changes: 67 additions & 0 deletions Victor_GUI/model_adapter.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
import os
import sys
import torch

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Move torch import behind mock/fallback initialization

Importing torch at module import time crashes startup with ModuleNotFoundError before the existing HAS_MODEL fallback can run, so the GUI cannot boot in mock mode on systems without torch. This defeats the adapter’s graceful-degradation path and blocks the app for users who should still be able to run the non-model features.

Useful? React with 👍 / 👎.

from pathlib import Path

# Add project root to path so we can import models
project_root = Path(__file__).parent.parent
sys.path.append(str(project_root))

try:
from models.transformer_model import VictorTransformerModel
from models import load_blank_slate, load_pretrained_checkpoint
HAS_MODEL = True
except ImportError as e:
print(f"Warning: Could not import Victor components: {e}")
HAS_MODEL = False

class ModelAdapter:
def __init__(self, use_mock=False):
self.use_mock = use_mock or not HAS_MODEL
self.model = None
self.tokenizer = None

if not self.use_mock:
try:
# Load dummy model configuration
config = load_blank_slate()
# If a tokenizer or specific loading is required, it could be done here
pass
except Exception as e:
print(f"Failed to load model config, falling back to mock: {e}")
self.use_mock = True

def infer(self, text):
"""
Run inference on the text.
Returns a tuple: (response_text, extracted_intent, detected_emotion)
"""
# A simple keyword-based intent/emotion extraction for the demo
intent = "general_chat"
emotion = "neutral"

lower_text = text.lower()
if "help" in lower_text or "how to" in lower_text:
intent = "request_help"
elif "train" in lower_text or "learn" in lower_text:
intent = "train_command"

if "angry" in lower_text or "mad" in lower_text or "!" in text:
emotion = "agitated"
elif "happy" in lower_text or "good" in lower_text or "thanks" in lower_text:
emotion = "positive"

if self.use_mock:
response = self._mock_infer(text, intent, emotion)
return response, intent, emotion

# If we had a real model hooked up to real generation code, we would use it here
return self._mock_infer(text, intent, emotion), intent, emotion

def _mock_infer(self, text, intent, emotion):
if intent == "request_help":
return "I am Victor. I am here to help. What specific assistance do you require?"
elif intent == "train_command":
return "I have logged your request. My learning queue will process this."

return f"I am Victor. I received your input: '{text}'. My systems are operating normally."
3 changes: 3 additions & 0 deletions Victor_GUI/requirements.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
gradio>=4.0.0
torch
numpy
Empty file added Victor_GUI/training/__init__.py
Empty file.
1 change: 1 addition & 0 deletions Victor_GUI/training/approval_ledger.jsonl
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{"timestamp": 1778509709.462584, "source_text": "correction: wrong", "current_response": "im right", "proposed_adjustment": "Adjust weights to better align with user feedback: None", "status": "pending_approval"}
Loading
Loading