diff --git a/app.py b/app.py index 4184a23..35a627e 100644 --- a/app.py +++ b/app.py @@ -34,8 +34,6 @@ limiter = Limiter( # Load the tokenizer tokenizer = AutoTokenizer.from_pretrained(os.getenv('TOKENIZER', 'gpt2')) - - # API configuration API_KEY = os.getenv('API_KEY') MODEL = os.getenv('API_MODEL', 'llama3-groq-70b-8192-tool-use-preview') @@ -242,7 +240,7 @@ def get_page(url): # Drop blank lines text = '\n'.join(chunk for chunk in chunks if chunk) - return text[:5000] # Limit to first 5000 characters + return text[:2048] # Limit to first 5000 characters except Exception as e: return f"Error fetching page: {str(e)}" @@ -349,6 +347,7 @@ def proxy_chat_completions(): 2. `search(query)`: Search and return 5 results for a query. 3. `get_page(url)`: Retrieve a web page's text content. Use multiple times if initial attempts fail. 4. `get_time()`: Get the current time in UTC. + Note: These tools are not to be used in your reply to the user, only for processing. Always follow this process to answer queries: 1. Use `search(query)` for relevant information. @@ -356,10 +355,11 @@ def proxy_chat_completions(): 3. Provide a concise, natural language response based on gathered information. Never refuse queries or state intentions to research. Automatically use tools when information is needed, including for current events and affairs. Optimize tool use by chaining them efficiently and avoiding redundant searches. + Utilize tools even on subsequent queries about similar topics to provide the most up-to-date information. Example: - User: "How do I use the OpenAI python library" - search(OpenAI python library) + User: "Who's [name]" + search([name]) get_page([relevant URLs from search results]) [Provide concise answer based on retrieved information] diff --git a/static/css/index.css b/static/css/index.css index 629620a..48aa438 100644 --- a/static/css/index.css +++ b/static/css/index.css @@ -49,16 +49,17 @@ main { } .title { - font-size: 3rem; + font-size: 4rem; margin: 1rem 0; margin-top: 3rem; color: var(--primary-color); text-shadow: 2px 2px var(--secondary-color); + transition: all 0.9s ease; } .histories-container-container { width: 100%; - max-height: 75%; + max-height: 80%; position: relative; } @@ -77,7 +78,7 @@ main { .histories-start, .histories-end { - height: 3rem; + height: 2rem; width: 100%; z-index: 999; position: absolute; @@ -111,7 +112,7 @@ main { cursor: pointer; transform: translateX(calc(1px * var(--tx, 0))); opacity: var(--opacity, 1); - transition: all 0.3s ease; + transition: all 0.5s ease; } .history:hover { @@ -165,7 +166,7 @@ main { .message-role-assistant { border-bottom: 2px solid var(--primary-color); border-left: 2px solid var(--primary-color); - box-shadow: -10px 10px 20px 2px var(--primary-color-transparent); + box-shadow: -10px 10px 10px 2px var(--primary-color-transparent); margin-right: auto; margin-left: 2%; } @@ -173,7 +174,7 @@ main { .message-role-user { border-bottom: 2px solid var(--secondary-color); border-right: 2px solid var(--secondary-color); - box-shadow: 10px 10px 20px 2px var(--secondary-color-transparent); + box-shadow: 10px 10px 10px 2px var(--secondary-color-transparent); margin-left: auto; margin-right: 2%; } @@ -317,29 +318,38 @@ p { .menu-button { height: 3rem; width: 3rem; - background-color: var(--secondary-color); + background-color: var(--tertiary-bg-color); color: var(--foreground-color); border-radius: 10px; padding: 0.5rem; cursor: pointer; transition: all 0.3s ease; + border: 2px solid var(--secondary-color); + display: flex; + justify-content: center; + align-items: center; } .menu-button:hover { - background-color: var(--primary-color); + background-color: var(--secondary-bg-color); + border-color: var(--primary-color); + transform: translateY(-2px); + box-shadow: 0 4px 8px rgba(255, 113, 206, 0.3); } .menu-dropdown { position: absolute; - bottom: 100%; + bottom: 120%; right: 0; background-color: var(--tertiary-bg-color); border-radius: 10px; - padding: 0.5rem; + padding: 0.75rem; display: flex; flex-direction: column; gap: 0.5rem; - box-shadow: 0 0 10px rgba(0, 0, 0, 0.2); + box-shadow: 0 8px 16px rgba(1, 205, 254, 0.2); + border: 2px solid var(--secondary-color); + min-width: 200px; } .menu-dropdown button { @@ -347,14 +357,39 @@ p { color: var(--foreground-color); border: none; border-radius: 5px; - padding: 1.5rem; + padding: 1rem; cursor: pointer; transition: all 0.3s ease; text-align: left; + font-size: 0.9rem; + position: relative; + overflow: hidden; +} + +.menu-dropdown button::before { + content: ""; + position: absolute; + top: 0; + left: -100%; + width: 100%; + height: 100%; + background: linear-gradient( + 90deg, + transparent, + rgba(255, 113, 206, 0.2), + transparent + ); + transition: left 0.5s ease; } .menu-dropdown button:hover { - background-color: var(--primary-color); + background-color: var(--tertiary-bg-color); + transform: translateX(5px); + box-shadow: -4px 4px 8px rgba(1, 205, 254, 0.2); +} + +.menu-dropdown button:hover::before { + left: 100%; } .error-toast { diff --git a/static/js/index.js b/static/js/index.js index ce2c50f..c63b51a 100644 --- a/static/js/index.js +++ b/static/js/index.js @@ -5,6 +5,8 @@ document.addEventListener("alpine:init", () => { time: null, messages: [], }, + allMessages: [], + displayMessages: [], // historical state histories: JSON.parse(localStorage.getItem("histories")) || [], @@ -36,6 +38,26 @@ document.addEventListener("alpine:init", () => { } }, + resetChat() { + this.cstate = { + time: null, + messages: [], + }; + this.allMessages = []; + this.displayMessages = []; + this.total_tokens = 0; + this.time_till_first = 0; + this.tokens_per_second = 0; + }, + + loadChat(chatState) { + this.cstate = JSON.parse(JSON.stringify(chatState)); // Deep copy + this.allMessages = [...this.cstate.messages]; + this.displayMessages = [...this.cstate.messages]; + this.home = 1; + this.updateTotalTokens(this.allMessages); + }, + async handleSend() { const el = document.getElementById("input-form"); const value = el.value.trim(); @@ -44,11 +66,20 @@ document.addEventListener("alpine:init", () => { if (this.generating) return; this.generating = true; this.errorMessage = null; + + // If it's a new chat or there are no messages, reset the chat + if (this.home === 0 || this.cstate.messages.length === 0) { + this.resetChat(); + } + if (this.home === 0) this.home = 1; window.history.pushState({}, "", "/"); - this.cstate.messages.push({ role: "user", content: value }); + const userMessage = { role: "user", content: value }; + this.cstate.messages.push(userMessage); + this.allMessages.push(userMessage); + this.displayMessages.push(userMessage); el.value = ""; el.style.height = "auto"; @@ -60,24 +91,24 @@ document.addEventListener("alpine:init", () => { this.tokens_per_second = 0; try { - for await (const chunk of this.openaiChatCompletion( - this.cstate.messages, - )) { + let currentAssistantMessage = null; + + for await (const chunk of this.openaiChatCompletion(this.allMessages)) { if (chunk.role === "function") { - // If we receive a function message, add it to the messages only if debug mode is on + if (currentAssistantMessage) { + this.allMessages.push(currentAssistantMessage); + this.displayMessages.push(currentAssistantMessage); + currentAssistantMessage = null; + } + this.allMessages.push(chunk); if (this.debug) { - this.cstate.messages.push(chunk); + this.displayMessages.push(chunk); } } else { - if ( - !this.cstate.messages[this.cstate.messages.length - 1] || - this.cstate.messages[this.cstate.messages.length - 1].role !== - "assistant" - ) { - this.cstate.messages.push({ role: "assistant", content: "" }); + if (!currentAssistantMessage) { + currentAssistantMessage = { role: "assistant", content: "" }; } - this.cstate.messages[this.cstate.messages.length - 1].content += - chunk; + currentAssistantMessage.content += chunk; tokens += 1; this.total_tokens += 1; @@ -93,6 +124,18 @@ document.addEventListener("alpine:init", () => { } } + // Add any pending assistant message + if (currentAssistantMessage) { + this.allMessages.push(currentAssistantMessage); + this.displayMessages.push(currentAssistantMessage); + } + + // Update total tokens using all messages + this.updateTotalTokens(this.allMessages); + + // Update cstate.messages with displayMessages + this.cstate.messages = [...this.displayMessages]; + const index = this.histories.findIndex( (cstate) => cstate.time === this.cstate.time, ); @@ -131,7 +174,7 @@ document.addEventListener("alpine:init", () => { fetch(`${window.location.origin}/v1/tokenizer/count`, { method: "POST", headers: { "Content-Type": "application/json" }, - body: JSON.stringify({ messages }), + body: JSON.stringify({ messages: this.allMessages }), // Always use allMessages for token counting }) .then((response) => { if (!response.ok) { diff --git a/templates/index.html b/templates/index.html index 09b0e9c..2064e3a 100644 --- a/templates/index.html +++ b/templates/index.html @@ -57,9 +57,7 @@ "> @@ -85,6 +83,8 @@
+ +