diff --git a/.env.example b/.env.example new file mode 100644 index 0000000..f739df2 --- /dev/null +++ b/.env.example @@ -0,0 +1,28 @@ +# Database Configuration +# Use DATABASE_PATH for simple relative/absolute paths (e.g., "database/raggr.db" or "dev.db") +# Or use DATABASE_URL for full connection strings (e.g., "sqlite://database/raggr.db") +DATABASE_PATH=database/raggr.db +# DATABASE_URL=sqlite://database/raggr.db + +# JWT Configuration +JWT_SECRET_KEY=your-secret-key-here + +# Paperless Configuration +PAPERLESS_TOKEN=your-paperless-token +BASE_URL=192.168.1.5:8000 + +# Ollama Configuration +OLLAMA_URL=http://192.168.1.14:11434 +OLLAMA_HOST=http://192.168.1.14:11434 + +# ChromaDB Configuration +CHROMADB_PATH=/path/to/chromadb + +# OpenAI Configuration +OPENAI_API_KEY=your-openai-api-key + +# Immich Configuration +IMMICH_URL=http://192.168.1.5:2283 +IMMICH_API_KEY=your-immich-api-key +SEARCH_QUERY=simba cat +DOWNLOAD_DIR=./simba_photos diff --git a/add_user.py b/add_user.py index 2380a89..6a600fd 100644 --- a/add_user.py +++ b/add_user.py @@ -1,16 +1,27 @@ # GENERATED BY CLAUDE +import os import sys import uuid import asyncio from tortoise import Tortoise from blueprints.users.models import User +from dotenv import load_dotenv + +load_dotenv() + +# Database configuration with environment variable support +DATABASE_PATH = os.getenv("DATABASE_PATH", "database/raggr.db") +DATABASE_URL = os.getenv("DATABASE_URL", f"sqlite://{DATABASE_PATH}") + +print(DATABASE_URL) + async def add_user(username: str, email: str, password: str): """Add a new user to the database""" await Tortoise.init( - db_url="sqlite://database/raggr.db", + db_url=DATABASE_URL, modules={ "models": [ "blueprints.users.models", @@ -56,7 +67,7 @@ async def add_user(username: str, email: str, password: str): async def list_users(): """List all users in the database""" await Tortoise.init( - db_url="sqlite://database/raggr.db", + db_url=DATABASE_URL, modules={ "models": [ "blueprints.users.models", @@ -94,6 +105,11 @@ def print_usage(): print("\nExamples:") print(" python add_user.py add ryan ryan@example.com mypassword123") print(" python add_user.py list") + print("\nEnvironment Variables:") + print(" DATABASE_PATH - Path to database file (default: database/raggr.db)") + print(" DATABASE_URL - Full database URL (overrides DATABASE_PATH)") + print("\n Example with custom database:") + print(" DATABASE_PATH=dev.db python add_user.py list") async def main(): diff --git a/aerich_config.py b/aerich_config.py index faa1c28..1f80157 100644 --- a/aerich_config.py +++ b/aerich_config.py @@ -1,7 +1,12 @@ import os +# Database configuration with environment variable support +# Use DATABASE_PATH for relative paths or DATABASE_URL for full connection strings +DATABASE_PATH = os.getenv("DATABASE_PATH", "database/raggr.db") +DATABASE_URL = os.getenv("DATABASE_URL", f"sqlite://{DATABASE_PATH}") + TORTOISE_ORM = { - "connections": {"default": os.getenv("DATABASE_URL", "sqlite:///app/database/raggr.db")}, + "connections": {"default": DATABASE_URL}, "apps": { "models": { "models": [ diff --git a/app.py b/app.py index 2152fa8..2a8fac9 100644 --- a/app.py +++ b/app.py @@ -26,8 +26,11 @@ app.register_blueprint(blueprints.users.user_blueprint) app.register_blueprint(blueprints.conversation.conversation_blueprint) +# Database configuration with environment variable support +DATABASE_PATH = os.getenv("DATABASE_PATH", "database/raggr.db") + TORTOISE_CONFIG = { - "connections": {"default": "sqlite://database/raggr.db"}, + "connections": {"default": f"sqlite://{DATABASE_PATH}"}, "apps": { "models": { "models": [ diff --git a/main.py b/main.py index 6c88ded..5ee4740 100644 --- a/main.py +++ b/main.py @@ -186,7 +186,7 @@ def consult_oracle( def llm_chat(input: str, transcript: str = "") -> str: system_prompt = "You are a helpful assistant that understands veterinary terms." transcript_prompt = f"Here is the message transcript thus far {transcript}." - prompt = f"""Answer the user in a humorous way as if you were a cat named Simba. Be very coy. + prompt = f"""Answer the user in as if you were a cat named Simba. Don't act too catlike. Be assertive. {transcript_prompt if len(transcript) > 0 else ""} Respond to this prompt: {input}""" output = llm_client.chat(prompt=prompt, system_prompt=system_prompt) diff --git a/raggr-frontend/src/api/userService.ts b/raggr-frontend/src/api/userService.ts index 131f710..632a633 100644 --- a/raggr-frontend/src/api/userService.ts +++ b/raggr-frontend/src/api/userService.ts @@ -55,6 +55,21 @@ class UserService { return data.access_token; } + async validateToken(): Promise { + const refreshToken = localStorage.getItem("refresh_token"); + + if (!refreshToken) { + return false; + } + + try { + await this.refreshToken(); + return true; + } catch (error) { + return false; + } + } + async fetchWithAuth( url: string, options: RequestInit = {}, diff --git a/raggr-frontend/src/components/AnswerBubble.tsx b/raggr-frontend/src/components/AnswerBubble.tsx index f439634..dc42541 100644 --- a/raggr-frontend/src/components/AnswerBubble.tsx +++ b/raggr-frontend/src/components/AnswerBubble.tsx @@ -7,7 +7,7 @@ type AnswerBubbleProps = { export const AnswerBubble = ({ text, loading }: AnswerBubbleProps) => { return ( -
+
{loading ? (
@@ -20,8 +20,10 @@ export const AnswerBubble = ({ text, loading }: AnswerBubbleProps) => {
) : ( -
- {"🐈: " + text} +
+ + {"🐈: " + text} +
)}
diff --git a/raggr-frontend/src/components/ChatScreen.tsx b/raggr-frontend/src/components/ChatScreen.tsx index b214a7c..85fc947 100644 --- a/raggr-frontend/src/components/ChatScreen.tsx +++ b/raggr-frontend/src/components/ChatScreen.tsx @@ -1,4 +1,4 @@ -import { useEffect, useState } from "react"; +import { useEffect, useState, useRef } from "react"; import { conversationService } from "../api/conversationService"; import { QuestionBubble } from "./QuestionBubble"; import { AnswerBubble } from "./AnswerBubble"; @@ -39,8 +39,13 @@ export const ChatScreen = ({ setAuthenticated }: ChatScreenProps) => { const [selectedConversation, setSelectedConversation] = useState(null); + const messagesEndRef = useRef(null); const simbaAnswers = ["meow.", "hiss...", "purrrrrr", "yowOWROWWowowr"]; + const scrollToBottom = () => { + messagesEndRef.current?.scrollIntoView({ behavior: "smooth" }); + }; + const handleSelectConversation = (conversation: Conversation) => { setShowConversations(false); setSelectedConversation(conversation); @@ -91,6 +96,10 @@ export const ChatScreen = ({ setAuthenticated }: ChatScreenProps) => { loadConversations(); }, []); + useEffect(() => { + scrollToBottom(); + }, [messages]); + useEffect(() => { const loadMessages = async () => { if (selectedConversation == null) return; @@ -112,8 +121,11 @@ export const ChatScreen = ({ setAuthenticated }: ChatScreenProps) => { }, [selectedConversation]); const handleQuestionSubmit = async () => { + if (!query.trim()) return; // Don't submit empty messages + const currMessages = messages.concat([{ text: query, speaker: "user" }]); setMessages(currMessages); + setQuery(""); // Clear input immediately after submission if (simbaMode) { console.log("simba mode activated"); @@ -142,7 +154,6 @@ export const ChatScreen = ({ setAuthenticated }: ChatScreenProps) => { setMessages( currMessages.concat([{ text: result.response, speaker: "simba" }]), ); - setQuery(""); // Clear input after successful send } catch (error) { console.error("Failed to send query:", error); // If session expired, redirect to login @@ -156,18 +167,26 @@ export const ChatScreen = ({ setAuthenticated }: ChatScreenProps) => { setQuery(event.target.value); }; + const handleKeyDown = (event: React.KeyboardEvent) => { + // Submit on Enter, but allow Shift+Enter for new line + if (event.key === "Enter" && !event.shiftKey) { + event.preventDefault(); + handleQuestionSubmit(); + } + }; + return (
-
-
+
+
-

ask simba!

+

ask simba!

-
+