ashish-soni08 commited on
Commit
8dff1d4
·
verified ·
1 Parent(s): 5260f5f

Upload app.py

Browse files
Files changed (1) hide show
  1. app.py +135 -0
app.py ADDED
@@ -0,0 +1,135 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import sys
2
+ import os
3
+ import logging
4
+ from typing import AsyncIterator
5
+
6
+ from agents import (Runner, set_default_openai_key, trace)
7
+ from openai.types.responses import ResponseTextDeltaEvent
8
+ import streamlit as st
9
+
10
+ from backend import lichtblick_agent
11
+
12
+ # ====================
13
+ # Setup logging
14
+ # ====================
15
+ logging.basicConfig(
16
+ level=logging.INFO,
17
+ format="%(asctime)s | %(levelname)s | %(name)s | %(message)s"
18
+ )
19
+ logger = logging.getLogger("Lichtblick")
20
+
21
+ # ============================
22
+ # Core async function
23
+ # ============================
24
+ async def llm_response(api_key: str, message: str) -> AsyncIterator[str]:
25
+ """
26
+ Streams the response from the Lichtblick assistant as an async text generator.
27
+
28
+ Args:
29
+ api_key (str): The OpenAI API key to authenticate requests.
30
+ message (str): The user's input message to be processed by the agent.
31
+
32
+ Yields:
33
+ str: Incremental chunks of the assistant's response text as they are streamed.
34
+ """
35
+ set_default_openai_key(api_key)
36
+ if not api_key or not api_key.startswith("sk-"):
37
+ logger.error("Missing or invalid OpenAI API key.")
38
+ yield "🤖 API key is missing or invalid. Please check your .env file."
39
+ return
40
+
41
+ # Construct context from message history
42
+ context = ""
43
+ for msg in st.session_state.messages[-5:]: # Use last 5 turns
44
+ role = "User" if msg["role"] == "user" else "Assistant"
45
+ content = msg["content"]
46
+ context += f"{role}: {content}\n\n" # Double newline for clarity
47
+ context += f"User: {message}"
48
+
49
+ try:
50
+ result = Runner.run_streamed(lichtblick_agent, input=message)
51
+ async for event in result.stream_events():
52
+ if event.type == "raw_response_event" and isinstance(event.data, ResponseTextDeltaEvent):
53
+ if event.data.delta:
54
+ yield event.data.delta
55
+ logger.info("Agent streaming complete.")
56
+ except Exception as e:
57
+ logger.exception("Error during agent processing.")
58
+ yield "🤖 Sorry, something went wrong. Please try again."
59
+
60
+ # ============================
61
+ # Lichtblick Streamlit App
62
+ # ============================
63
+ st.set_page_config(
64
+ page_title="Lichtblick",
65
+ page_icon="🇩🇪📚",
66
+ layout="centered",
67
+ initial_sidebar_state="collapsed"
68
+ )
69
+
70
+ # Sidebar
71
+ with st.sidebar:
72
+ st.image("assets/lichtblick_mascot.png")
73
+ openai_api_key = st.sidebar.text_input("OpenAI API Key", type="password")
74
+
75
+ # ✅ Show "ready" toast only once per session
76
+ if openai_api_key and openai_api_key.startswith("sk-"):
77
+ if not st.session_state.get("api_key_validated"):
78
+ st.toast("💡 Lichtblick is ready!", icon="✅")
79
+ st.session_state.api_key_validated = True
80
+ elif openai_api_key:
81
+ st.toast("❌ Invalid API key format.", icon="⚠️")
82
+ st.session_state.api_key_validated = False
83
+
84
+ # Clear chat + reset session
85
+ # 🧹 Centered Clear Chat Button in the sidebar
86
+ st.markdown("<div style='text-align: center;'>", unsafe_allow_html=True)
87
+ if st.button("🧹 Clear Chat", use_container_width=True):
88
+ st.session_state.messages = []
89
+ st.session_state.api_key_validated = False
90
+ st.toast("🧹 Chat history cleared!", icon="✅")
91
+ st.markdown("</div>", unsafe_allow_html=True)
92
+
93
+
94
+ # App title
95
+ st.title("💡:blue[_Lichtblick_] :orange[_Assistant_]💡")
96
+
97
+ # Short Description
98
+ with st.expander("ℹ️ What is Lichtblick?"):
99
+ st.markdown("Lichtblick is your smart and supportive assistant for learning German through sentence analysis, vocabulary help, and clear explanations.")
100
+
101
+
102
+ # Initialize chat history
103
+ if "messages" not in st.session_state:
104
+ st.session_state.messages = []
105
+
106
+ # Display chat messages from history
107
+ for message in st.session_state.messages:
108
+ with st.chat_message(message["role"]):
109
+ st.markdown(message["content"])
110
+
111
+ # Accept user input
112
+ if user_input := st.chat_input("Ready to learn German? Ask me anything!"):
113
+ if not openai_api_key or not openai_api_key.startswith("sk-"):
114
+ st.toast("❌ Please enter a valid OpenAI API key.", icon="⚠️")
115
+ st.stop()
116
+
117
+ elif user_input.strip() == "":
118
+ st.toast("⚠️ Please enter a message.", icon="⚠️")
119
+
120
+ else:
121
+ # Add user message to chat history
122
+ st.session_state.messages.append({"role": "user", "content": user_input})
123
+
124
+ with st.chat_message("user", avatar="🤵🏻"):
125
+ st.markdown(user_input)
126
+
127
+ with st.chat_message("assistant", avatar="🤖"):
128
+ try:
129
+ with st.spinner("💬 Lichtblick is thinking..."):
130
+ with trace("Lichtblick workflow"):
131
+ response = st.write_stream(llm_response(api_key=openai_api_key, message=user_input))
132
+ st.session_state.messages.append({"role": "assistant", "content": response})
133
+ except Exception as e:
134
+ logger.exception("Exception in response streaming.")
135
+ st.toast("🤖 Oops! Something went wrong while processing your request.", icon="❌")