From 0061b1218c5a76a43bba2e319536d676431f5e8c Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Thu, 11 Dec 2025 16:06:03 +0000
Subject: [PATCH 1/3] Initial plan
From 075751e9e48ed9a52f1cc635c9d531f6a47bc083 Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Thu, 11 Dec 2025 16:13:51 +0000
Subject: [PATCH 2/3] Implement simplified white UI with debugging features
Co-authored-by: dokuczacz <204607319+dokuczacz@users.noreply.github.com>
---
streamlit_app.py | 279 ++++++++++++++++++++++++++++++-----------------
1 file changed, 176 insertions(+), 103 deletions(-)
diff --git a/streamlit_app.py b/streamlit_app.py
index 42e4843..34732df 100644
--- a/streamlit_app.py
+++ b/streamlit_app.py
@@ -3,155 +3,228 @@
import json
from datetime import datetime
import os
+import time
# === CONFIGURATION ===
BACKEND_URL = "https://agentbackendservice-dfcpcudzeah4b6ae.northeurope-01.azurewebsites.net/api"
FUNCTION_KEY = os.environ.get("AZURE_FUNCTION_KEY", "")
OPENAI_API_KEY = os.environ.get("OPENAI_API_KEY", "")
-# Set page config
+# Set page config with light theme
st.set_page_config(
page_title="OmniFlow Assistant",
page_icon="đ",
- layout="wide",
- initial_sidebar_state="expanded"
+ layout="centered",
+ initial_sidebar_state="collapsed"
)
-# === SIDEBAR CONFIGURATION ===
-st.sidebar.title("âī¸ Configuration")
+# === CUSTOM CSS FOR LIGHT THEME ===
+st.markdown("""
+
+""", unsafe_allow_html=True)
+
+# === SIMPLE SIDEBAR ===
+st.sidebar.title("âī¸ Settings")
-# User ID
+# User ID only
user_id = st.sidebar.text_input(
"User ID",
value="default_user",
help="Your unique user identifier"
)
-# Category selector
-categories = ["TM", "PS", "LO", "GEN", "ID", "PE", "UI", "ML", "SYS"]
-selected_category = st.sidebar.selectbox(
- "Knowledge Category",
- categories,
- help="Select knowledge domain"
-)
-
-# File selector
-st.sidebar.markdown("---")
-st.sidebar.subheader("đ File Management")
-
-# Quick actions
-col1, col2 = st.sidebar.columns(2)
-with col1:
- if st.button("â Add Task"):
- st.session_state.quick_action = "add_task"
-with col2:
- if st.button("đ View Tasks"):
- st.session_state.quick_action = "view_tasks"
-
# === HELPER FUNCTIONS ===
-def call_backend(endpoint: str, payload: dict) -> dict:
- """Call Azure backend with user context"""
+def call_backend(endpoint: str, payload: dict) -> tuple:
+ """Call Azure backend with user context and return response + timing"""
headers = {
"X-User-Id": user_id,
"Content-Type": "application/json"
}
+ start_time = time.time()
try:
# Include function key for authentication
url = f"{BACKEND_URL}/{endpoint}?code={FUNCTION_KEY}"
response = requests.post(url, json=payload, headers=headers, timeout=30)
response.raise_for_status()
- return response.json()
+ elapsed = time.time() - start_time
+ return response.json(), elapsed
except requests.exceptions.RequestException as e:
+ elapsed = time.time() - start_time
st.error(f"Backend error: {e}")
- return {"error": str(e)}
+ return {"error": str(e)}, elapsed
-def send_to_llm(messages: list) -> str:
- """Send messages to LLM via backend proxy"""
+def send_to_llm(messages: list) -> tuple:
+ """Send messages to LLM via backend proxy, return response and stats"""
payload = {
"message": messages[-1]["content"] if messages else "",
"user_id": user_id,
"thread_id": st.session_state.get("thread_id")
}
- result = call_backend("tool_call_handler", payload)
+ result, elapsed = call_backend("tool_call_handler", payload)
+
+ # Store response stats
+ stats = {
+ "response_time": elapsed,
+ "timestamp": datetime.now(),
+ "has_error": "error" in result
+ }
if "response" in result:
st.session_state.thread_id = result.get("thread_id")
- return result["response"]
- return "Error communicating with assistant"
+ return result["response"], stats
+ return "Error communicating with assistant", stats
-def get_file_stats():
- """Get statistics for all user files"""
- result = call_backend("list_blobs", {"user_id": user_id})
- return result.get("blobs", [])
+# === MAIN LAYOUT ===
+st.title("đ OmniFlow Assistant")
-def read_file_content(filename: str):
- """Read a specific file"""
- result = call_backend("read_blob_file", {"file_name": filename})
- return result.get("data", [])
+# Initialize session state
+if "messages" not in st.session_state:
+ st.session_state.messages = []
+if "thread_id" not in st.session_state:
+ st.session_state.thread_id = None
+if "stats_history" not in st.session_state:
+ st.session_state.stats_history = []
+if "debug_mode" not in st.session_state:
+ st.session_state.debug_mode = False
-# === MAIN LAYOUT ===
-# Main content area with two columns
-main_col1, main_col2 = st.columns([3, 1])
+# === DEBUG/STATISTICS PANEL ===
+st.markdown('
', unsafe_allow_html=True)
-with main_col1:
- st.title("đ OmniFlow Assistant")
- st.markdown(f"**User:** {user_id} | **Category:** {selected_category}")
-
- # Chat interface
- st.subheader("đŦ Chat")
-
- # Initialize session state
- if "messages" not in st.session_state:
+# Debug icons and controls
+col1, col2, col3, col4, col5 = st.columns([1, 1, 1, 1, 4])
+
+with col1:
+ if st.button("đ", help="Toggle Debug Mode"):
+ st.session_state.debug_mode = not st.session_state.debug_mode
+
+with col2:
+ st.markdown('
âąī¸', unsafe_allow_html=True)
+
+with col3:
+ st.markdown('
đ', unsafe_allow_html=True)
+
+with col4:
+ if st.button("đ", help="Clear Chat"):
st.session_state.messages = []
- if "thread_id" not in st.session_state:
- st.session_state.thread_id = None
-
- # Display chat history
- for message in st.session_state.messages:
- with st.chat_message(message["role"]):
- st.write(message["content"])
-
- # Chat input with unique key
- prompt = st.chat_input("Ask me anything...", key="chat_input_unique")
-
- if prompt:
- # Add user message
- st.session_state.messages.append({"role": "user", "content": prompt})
- with st.chat_message("user"):
- st.write(prompt)
-
- # Get LLM response
- with st.chat_message("assistant"):
- with st.spinner("Thinking..."):
- response = send_to_llm(st.session_state.messages)
- st.write(response)
-
- # Store assistant response
- st.session_state.messages.append({"role": "assistant", "content": response})
+ st.session_state.stats_history = []
st.rerun()
-with main_col2:
- st.subheader("đ Context")
-
- # File stats
- st.markdown("**đ Your Files:**")
- files = get_file_stats()
-
- if files:
- for file in files[:5]: # Show top 5
- st.caption(f"đ {file}")
- else:
- st.caption("No files yet")
+with col5:
+ # Display current stats
+ if st.session_state.stats_history:
+ last_stats = st.session_state.stats_history[-1]
+ avg_time = sum(s["response_time"] for s in st.session_state.stats_history) / len(st.session_state.stats_history)
+ st.markdown(
+ f'
'
+ f'Last: {last_stats["response_time"]:.2f}s'
+ f'Avg: {avg_time:.2f}s'
+ f'Total: {len(st.session_state.messages)//2} exchanges'
+ f'
',
+ unsafe_allow_html=True
+ )
+
+st.markdown('
', unsafe_allow_html=True)
+
+# Debug info panel (collapsible)
+if st.session_state.debug_mode:
+ with st.expander("đ Debug Information", expanded=True):
+ col_d1, col_d2, col_d3 = st.columns(3)
+ with col_d1:
+ st.metric("User ID", user_id)
+ st.metric("Thread ID", st.session_state.thread_id or "Not started")
+ with col_d2:
+ st.metric("Messages", len(st.session_state.messages))
+ if st.session_state.stats_history:
+ error_count = sum(1 for s in st.session_state.stats_history if s["has_error"])
+ st.metric("Errors", error_count)
+ with col_d3:
+ st.metric("Backend", "Azure Functions")
+ st.metric("Status", "đĸ Connected" if FUNCTION_KEY else "â ī¸ No Key")
+
+st.markdown("---")
+
+# === CHAT INTERFACE ===
+# Display chat history with scrolling
+for idx, message in enumerate(st.session_state.messages):
+ with st.chat_message(message["role"]):
+ st.write(message["content"])
+
+ # Show stats for assistant messages in debug mode
+ if message["role"] == "assistant" and st.session_state.debug_mode:
+ stats_idx = idx // 2
+ if stats_idx < len(st.session_state.stats_history):
+ stats = st.session_state.stats_history[stats_idx]
+ st.caption(f"âąī¸ Response time: {stats['response_time']:.2f}s | {stats['timestamp'].strftime('%H:%M:%S')}")
+
+# Chat input
+prompt = st.chat_input("Ask me anything...", key="chat_input_unique")
+
+if prompt:
+ # Add user message
+ st.session_state.messages.append({"role": "user", "content": prompt})
+ with st.chat_message("user"):
+ st.write(prompt)
- # Today's tasks section
- st.markdown("---")
- st.markdown("**â
Quick Stats:**")
- st.metric("Total Files", len(files))
- st.metric("Category", selected_category)
+ # Get LLM response
+ with st.chat_message("assistant"):
+ with st.spinner("Thinking..."):
+ response, stats = send_to_llm(st.session_state.messages)
+ st.write(response)
+
+ # Show response time
+ if st.session_state.debug_mode:
+ st.caption(f"âąī¸ Response time: {stats['response_time']:.2f}s")
- # User info
- st.markdown("---")
- st.markdown("**đ¤ User Info:**")
- st.caption(f"ID: {user_id}")
- st.caption(f"Last: {datetime.now().strftime('%H:%M:%S')}")
+ # Store assistant response and stats
+ st.session_state.messages.append({"role": "assistant", "content": response})
+ st.session_state.stats_history.append(stats)
+ st.rerun()
From 86736c8faa9c42fb413eace2acec2ab40a07c754 Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Thu, 11 Dec 2025 16:33:02 +0000
Subject: [PATCH 3/3] Complete UI with user management, temperature control,
and all backend features
Co-authored-by: dokuczacz <204607319+dokuczacz@users.noreply.github.com>
---
BACKEND_CREATE_USER_SPEC.md | 205 +++++++++++++++++++++++++++
streamlit_app.py | 271 +++++++++++++++++++++++++++++++++---
2 files changed, 456 insertions(+), 20 deletions(-)
create mode 100644 BACKEND_CREATE_USER_SPEC.md
diff --git a/BACKEND_CREATE_USER_SPEC.md b/BACKEND_CREATE_USER_SPEC.md
new file mode 100644
index 0000000..8092c70
--- /dev/null
+++ b/BACKEND_CREATE_USER_SPEC.md
@@ -0,0 +1,205 @@
+# Backend Implementation Spec: Create User Endpoint
+
+## Overview
+This document specifies the `create_user` endpoint that should be implemented in the **omniflow-agent-backend** repository.
+
+## Endpoint Details
+
+### Function Name
+`create_user`
+
+### HTTP Method
+`POST`
+
+### URL
+`/api/create_user`
+
+### Request Headers
+- `X-User-Id`: (Optional) Admin user ID performing the creation
+- `Content-Type`: `application/json`
+
+### Request Body
+```json
+{
+ "user_id": "string (required)",
+ "create_default_files": "boolean (optional, default: true)"
+}
+```
+
+### Response - Success (200)
+```json
+{
+ "status": "success",
+ "user_id": "alice_123",
+ "message": "User 'alice_123' created successfully",
+ "files_created": [
+ "users/alice_123/tasks.json",
+ "users/alice_123/ideas.json",
+ "users/alice_123/notes.json"
+ ],
+ "timestamp": "2025-12-11T16:30:00Z"
+}
+```
+
+### Response - Error (400)
+```json
+{
+ "status": "error",
+ "error": "Invalid user_id format",
+ "message": "User ID must be 3-64 characters, alphanumeric with _, -, or ."
+}
+```
+
+### Response - Error (409)
+```json
+{
+ "status": "error",
+ "error": "User already exists",
+ "message": "User 'alice_123' already has files in the system"
+}
+```
+
+## Implementation Details
+
+### Default Files to Create
+
+1. **tasks.json** - Task management
+```json
+{
+ "tasks": [
+ {
+ "id": "welcome_task_1",
+ "title": "Welcome to OmniFlow!",
+ "description": "This is your first task. You can add, update, or remove tasks.",
+ "status": "open",
+ "priority": "medium",
+ "created_at": "2025-12-11T16:30:00Z"
+ }
+ ]
+}
+```
+
+2. **ideas.json** - Ideas and brainstorming
+```json
+{
+ "ideas": [
+ {
+ "id": "welcome_idea_1",
+ "content": "Store your ideas here for future reference",
+ "category": "general",
+ "created_at": "2025-12-11T16:30:00Z"
+ }
+ ]
+}
+```
+
+3. **notes.json** - General notes
+```json
+{
+ "notes": [
+ {
+ "id": "welcome_note_1",
+ "content": "Welcome to OmniFlow! Use this space for notes.",
+ "tags": ["welcome"],
+ "created_at": "2025-12-11T16:30:00Z"
+ }
+ ]
+}
+```
+
+### Validation Rules
+
+- **user_id** must match pattern: `^[a-zA-Z0-9._-]{3,64}$`
+- User must not already have files in the system
+- All default files should be created atomically (all or none)
+
+### Integration with Existing System
+
+This function should:
+1. Use `shared/user_manager.py` for user ID validation
+2. Use `shared/azure_client.py` for blob operations
+3. Use `UserNamespace.get_user_blob_name()` for proper file naming
+4. Log operations for audit trail
+
+### Example Implementation Location
+
+Create new function in: `omniflow-agent-backend/create_user/__init__.py`
+
+### Function Configuration
+
+File: `omniflow-agent-backend/create_user/function.json`
+```json
+{
+ "scriptFile": "__init__.py",
+ "bindings": [
+ {
+ "authLevel": "function",
+ "type": "httpTrigger",
+ "direction": "in",
+ "name": "req",
+ "methods": ["post"]
+ },
+ {
+ "type": "http",
+ "direction": "out",
+ "name": "$return"
+ }
+ ]
+}
+```
+
+## Testing
+
+### Test Case 1: Create User with Default Files
+```bash
+curl -X POST https://agentbackendservice.azurewebsites.net/api/create_user?code=xxx \
+ -H "Content-Type: application/json" \
+ -H "X-User-Id: admin" \
+ -d '{
+ "user_id": "test_user_123",
+ "create_default_files": true
+ }'
+```
+
+Expected: 200, files created
+
+### Test Case 2: Invalid User ID
+```bash
+curl -X POST https://agentbackendservice.azurewebsites.net/api/create_user?code=xxx \
+ -H "Content-Type: application/json" \
+ -d '{
+ "user_id": "ab",
+ "create_default_files": true
+ }'
+```
+
+Expected: 400, error message
+
+### Test Case 3: Duplicate User
+```bash
+# Create same user twice
+curl -X POST https://agentbackendservice.azurewebsites.net/api/create_user?code=xxx \
+ -H "Content-Type: application/json" \
+ -d '{
+ "user_id": "existing_user",
+ "create_default_files": true
+ }'
+```
+
+Expected: First call 200, second call 409
+
+## UI Integration
+
+The chatbot UI (`streamlit_app.py`) already includes:
+- `create_new_user()` function to call this endpoint
+- UI checkbox to enable/disable default file creation
+- Error handling for missing endpoint (404)
+- Success/error messages display
+
+## Notes
+
+- This endpoint should be idempotent where possible
+- Consider rate limiting for user creation
+- Add admin authentication in production
+- Log all user creation events for audit
+- Consider adding webhook/notification for user creation
diff --git a/streamlit_app.py b/streamlit_app.py
index 34732df..7461f4f 100644
--- a/streamlit_app.py
+++ b/streamlit_app.py
@@ -9,6 +9,7 @@
BACKEND_URL = "https://agentbackendservice-dfcpcudzeah4b6ae.northeurope-01.azurewebsites.net/api"
FUNCTION_KEY = os.environ.get("AZURE_FUNCTION_KEY", "")
OPENAI_API_KEY = os.environ.get("OPENAI_API_KEY", "")
+REQUEST_TIMEOUT = 30
# Set page config with light theme
st.set_page_config(
@@ -71,18 +72,110 @@
""", unsafe_allow_html=True)
-# === SIMPLE SIDEBAR ===
-st.sidebar.title("âī¸ Settings")
+# === SIDEBAR - USER MANAGEMENT ===
+st.sidebar.title("đ¤ User Management")
-# User ID only
-user_id = st.sidebar.text_input(
- "User ID",
- value="default_user",
- help="Your unique user identifier"
+# Initialize user list in session state
+if "user_list" not in st.session_state:
+ st.session_state.user_list = ["default_user", "alice_123", "bob_456", "test_user"]
+
+# Current user ID input
+current_user = st.sidebar.text_input(
+ "Current User ID",
+ value=st.session_state.get("current_user", "default_user"),
+ help="Your unique user identifier (3-64 chars, alphanumeric, _, -, .)"
+)
+
+# Validate user ID
+import re
+def validate_user_id(uid):
+ if not uid or len(uid) < 3 or len(uid) > 64:
+ return False
+ return bool(re.match(r'^[a-zA-Z0-9._-]+$', uid))
+
+if current_user != st.session_state.get("current_user"):
+ if validate_user_id(current_user):
+ st.session_state.current_user = current_user
+ if current_user not in st.session_state.user_list:
+ st.session_state.user_list.append(current_user)
+ user_id = current_user
+ else:
+ st.sidebar.error("â Invalid user ID format")
+ user_id = st.session_state.get("current_user", "default_user")
+else:
+ user_id = current_user
+
+# Quick user switcher
+st.sidebar.markdown("**Quick Switch:**")
+selected_quick_user = st.sidebar.selectbox(
+ "Select from existing users",
+ st.session_state.user_list,
+ index=st.session_state.user_list.index(user_id) if user_id in st.session_state.user_list else 0,
+ label_visibility="collapsed"
+)
+
+if selected_quick_user != user_id:
+ st.session_state.current_user = selected_quick_user
+ st.rerun()
+
+# Add new user
+with st.sidebar.expander("â Add New User"):
+ new_user_id = st.text_input("New User ID", key="new_user_input")
+ create_default_files = st.checkbox("Create default files", value=True,
+ help="Create tasks.json, ideas.json, notes.json for new user")
+
+ if st.button("Create User"):
+ if validate_user_id(new_user_id):
+ if new_user_id not in st.session_state.user_list:
+ # Try to create user via backend (if endpoint exists)
+ if create_default_files:
+ result = create_new_user(new_user_id)
+ if result.get("status") == "success":
+ st.success(f"â
User '{new_user_id}' created with default files!")
+ st.caption(f"Created: {', '.join(result.get('files_created', []))}")
+ elif "error" in result and "404" in str(result.get("error", "")):
+ st.warning("â ī¸ Backend endpoint not available. User added locally only.")
+ else:
+ st.warning(f"â ī¸ Backend: {result.get('error', 'Unknown error')}")
+ else:
+ st.info("âšī¸ User added (no backend files created)")
+
+ # Add to local list
+ st.session_state.user_list.append(new_user_id)
+ st.session_state.current_user = new_user_id
+ st.rerun()
+ else:
+ st.warning("â ī¸ User already exists")
+ else:
+ st.error("â Invalid format: 3-64 chars, alphanumeric, _, -, .")
+
+# User stats
+st.sidebar.caption(f"đĨ Total users: {len(st.session_state.user_list)}")
+
+st.sidebar.markdown("---")
+
+# Temperature control
+st.sidebar.markdown("**đĄī¸ AI Settings:**")
+temperature = st.sidebar.slider(
+ "Temperature",
+ min_value=0.0,
+ max_value=2.0,
+ value=1.0,
+ step=0.1,
+ help="Controls randomness: 0 = focused/deterministic, 2 = creative/random"
)
+st.sidebar.caption(f"Current: {temperature}")
+
+st.sidebar.markdown("---")
+
+# Feature toggles
+st.sidebar.markdown("**Features:**")
+show_files = st.sidebar.checkbox("đ File Browser", value=False)
+show_history = st.sidebar.checkbox("đ History", value=False)
+show_data_manager = st.sidebar.checkbox("đī¸ Data Manager", value=False)
# === HELPER FUNCTIONS ===
-def call_backend(endpoint: str, payload: dict) -> tuple:
+def call_backend(endpoint: str, payload: dict = None, method: str = "POST") -> tuple:
"""Call Azure backend with user context and return response + timing"""
headers = {
"X-User-Id": user_id,
@@ -90,23 +183,27 @@ def call_backend(endpoint: str, payload: dict) -> tuple:
}
start_time = time.time()
try:
- # Include function key for authentication
url = f"{BACKEND_URL}/{endpoint}?code={FUNCTION_KEY}"
- response = requests.post(url, json=payload, headers=headers, timeout=30)
+
+ if method == "GET":
+ response = requests.get(url, headers=headers, timeout=REQUEST_TIMEOUT)
+ else:
+ response = requests.post(url, json=payload or {}, headers=headers, timeout=REQUEST_TIMEOUT)
+
response.raise_for_status()
elapsed = time.time() - start_time
return response.json(), elapsed
except requests.exceptions.RequestException as e:
elapsed = time.time() - start_time
- st.error(f"Backend error: {e}")
return {"error": str(e)}, elapsed
-def send_to_llm(messages: list) -> tuple:
+def send_to_llm(messages: list, temp: float = 1.0) -> tuple:
"""Send messages to LLM via backend proxy, return response and stats"""
payload = {
"message": messages[-1]["content"] if messages else "",
"user_id": user_id,
- "thread_id": st.session_state.get("thread_id")
+ "thread_id": st.session_state.get("thread_id"),
+ "temperature": temp # Include temperature (backend may use it in future)
}
result, elapsed = call_backend("tool_call_handler", payload)
@@ -115,7 +212,10 @@ def send_to_llm(messages: list) -> tuple:
stats = {
"response_time": elapsed,
"timestamp": datetime.now(),
- "has_error": "error" in result
+ "has_error": "error" in result,
+ "tool_calls_count": result.get("tool_calls_count", 0),
+ "thread_id": result.get("thread_id"),
+ "temperature": temp
}
if "response" in result:
@@ -123,6 +223,54 @@ def send_to_llm(messages: list) -> tuple:
return result["response"], stats
return "Error communicating with assistant", stats
+def get_user_files():
+ """Get list of user's files"""
+ result, _ = call_backend("list_blobs", {"user_id": user_id})
+ return result.get("blobs", [])
+
+def get_interaction_history(limit: int = 10):
+ """Get user's conversation history"""
+ url = f"{BACKEND_URL}/get_interaction_history?limit={limit}&code={FUNCTION_KEY}"
+ headers = {"X-User-Id": user_id}
+ try:
+ response = requests.get(url, headers=headers, timeout=REQUEST_TIMEOUT)
+ response.raise_for_status()
+ return response.json()
+ except Exception as e:
+ return {"error": str(e)}
+
+def read_file_content(filename: str):
+ """Read a specific file"""
+ result, _ = call_backend("read_blob_file", {"file_name": filename})
+ return result.get("data", [])
+
+def add_data_entry(filename: str, entry: dict):
+ """Add new data entry to file"""
+ result, _ = call_backend("add_new_data", {
+ "target_blob_name": filename,
+ "new_entry": entry,
+ "user_id": user_id
+ })
+ return result
+
+def get_filtered_data(filename: str, key: str, value: str):
+ """Get filtered data from file"""
+ result, _ = call_backend("get_filtered_data", {
+ "target_blob_name": filename,
+ "key": key,
+ "value": value,
+ "user_id": user_id
+ })
+ return result
+
+def create_new_user(new_user_id: str):
+ """Create new user with basic file set (requires backend support)"""
+ result, _ = call_backend("create_user", {
+ "user_id": new_user_id,
+ "create_default_files": True
+ })
+ return result
+
# === MAIN LAYOUT ===
st.title("đ OmniFlow Assistant")
@@ -163,11 +311,13 @@ def send_to_llm(messages: list) -> tuple:
if st.session_state.stats_history:
last_stats = st.session_state.stats_history[-1]
avg_time = sum(s["response_time"] for s in st.session_state.stats_history) / len(st.session_state.stats_history)
+ total_tools = sum(s.get("tool_calls_count", 0) for s in st.session_state.stats_history)
st.markdown(
f''
f'Last: {last_stats["response_time"]:.2f}s'
f'Avg: {avg_time:.2f}s'
- f'Total: {len(st.session_state.messages)//2} exchanges'
+ f'Exchanges: {len(st.session_state.messages)//2}'
+ f'Tools: {total_tools}'
f'
',
unsafe_allow_html=True
)
@@ -177,7 +327,7 @@ def send_to_llm(messages: list) -> tuple:
# Debug info panel (collapsible)
if st.session_state.debug_mode:
with st.expander("đ Debug Information", expanded=True):
- col_d1, col_d2, col_d3 = st.columns(3)
+ col_d1, col_d2, col_d3, col_d4 = st.columns(4)
with col_d1:
st.metric("User ID", user_id)
st.metric("Thread ID", st.session_state.thread_id or "Not started")
@@ -189,6 +339,84 @@ def send_to_llm(messages: list) -> tuple:
with col_d3:
st.metric("Backend", "Azure Functions")
st.metric("Status", "đĸ Connected" if FUNCTION_KEY else "â ī¸ No Key")
+ with col_d4:
+ if st.session_state.stats_history:
+ total_tools = sum(s.get("tool_calls_count", 0) for s in st.session_state.stats_history)
+ st.metric("Total Tool Calls", total_tools)
+ st.metric("Temperature", "N/A (not exposed)")
+
+st.markdown("---")
+
+# === OPTIONAL PANELS ===
+if show_files:
+ with st.expander("đ File Browser", expanded=True):
+ if st.button("đ Refresh Files"):
+ st.rerun()
+
+ files = get_user_files()
+ if files:
+ st.write(f"**Found {len(files)} file(s):**")
+ for file in files:
+ col_f1, col_f2 = st.columns([3, 1])
+ with col_f1:
+ st.text(f"đ {file}")
+ with col_f2:
+ if st.button("View", key=f"view_{file}"):
+ content = read_file_content(file)
+ st.json(content)
+ else:
+ st.info("No files found for this user")
+
+if show_history:
+ with st.expander("đ Conversation History", expanded=True):
+ history_limit = st.slider("Number of interactions", 5, 50, 10)
+ if st.button("đ Load History"):
+ history = get_interaction_history(history_limit)
+ if "interactions" in history:
+ st.write(f"**Loaded {len(history['interactions'])} interaction(s):**")
+ for idx, interaction in enumerate(reversed(history["interactions"])):
+ st.markdown(f"**#{idx+1}** - {interaction.get('timestamp', 'N/A')}")
+ st.text(f"User: {interaction.get('user_message', '')[:100]}...")
+ st.text(f"Assistant: {interaction.get('assistant_response', '')[:100]}...")
+ if interaction.get('tool_calls'):
+ st.caption(f"đ§ {len(interaction['tool_calls'])} tool call(s)")
+ st.markdown("---")
+ else:
+ st.error(f"Error: {history.get('error', 'Unknown error')}")
+
+if show_data_manager:
+ with st.expander("đī¸ Data Manager", expanded=True):
+ st.subheader("Add New Entry")
+ dm_file = st.text_input("File name (e.g., tasks.json)")
+ dm_entry_json = st.text_area("Entry (JSON format)", '{"id": "1", "content": "example"}')
+
+ if st.button("â Add Entry"):
+ try:
+ entry = json.loads(dm_entry_json)
+ result = add_data_entry(dm_file, entry)
+ if result.get("status") == "success":
+ st.success(f"â
{result.get('message')}")
+ else:
+ st.error(f"â {result.get('error', 'Unknown error')}")
+ except json.JSONDecodeError:
+ st.error("Invalid JSON format")
+
+ st.markdown("---")
+ st.subheader("Query Data")
+ qd_file = st.text_input("File to query")
+ qd_col1, qd_col2 = st.columns(2)
+ with qd_col1:
+ qd_key = st.text_input("Key")
+ with qd_col2:
+ qd_value = st.text_input("Value")
+
+ if st.button("đ Query"):
+ result = get_filtered_data(qd_file, qd_key, qd_value)
+ if result.get("status") == "success":
+ st.success(f"Found {result.get('count', 0)} of {result.get('total', 0)} entries")
+ st.json(result.get("data", []))
+ else:
+ st.error(f"â {result.get('error', 'Unknown error')}")
st.markdown("---")
@@ -203,7 +431,8 @@ def send_to_llm(messages: list) -> tuple:
stats_idx = idx // 2
if stats_idx < len(st.session_state.stats_history):
stats = st.session_state.stats_history[stats_idx]
- st.caption(f"âąī¸ Response time: {stats['response_time']:.2f}s | {stats['timestamp'].strftime('%H:%M:%S')}")
+ tools_used = stats.get('tool_calls_count', 0)
+ st.caption(f"âąī¸ {stats['response_time']:.2f}s | đ§ {tools_used} tools | {stats['timestamp'].strftime('%H:%M:%S')}")
# Chat input
prompt = st.chat_input("Ask me anything...", key="chat_input_unique")
@@ -217,12 +446,14 @@ def send_to_llm(messages: list) -> tuple:
# Get LLM response
with st.chat_message("assistant"):
with st.spinner("Thinking..."):
- response, stats = send_to_llm(st.session_state.messages)
+ response, stats = send_to_llm(st.session_state.messages, temperature)
st.write(response)
- # Show response time
+ # Show response time and tool usage
if st.session_state.debug_mode:
- st.caption(f"âąī¸ Response time: {stats['response_time']:.2f}s")
+ tools_used = stats.get('tool_calls_count', 0)
+ temp_used = stats.get('temperature', 1.0)
+ st.caption(f"âąī¸ {stats['response_time']:.2f}s | đ§ {tools_used} tools | đĄī¸ {temp_used}")
# Store assistant response and stats
st.session_state.messages.append({"role": "assistant", "content": response})