BuildABot.2 / app.py
jostlebot's picture
Initial Build a Bot app
bf2a7d3
"""Build a Bot — AI Literacy Space"""
import streamlit as st
import anthropic
import os
st.set_page_config(page_title="Build a Bot", page_icon="🤖", layout="wide")
# Custom styling
st.markdown("""
<style>
.stApp {
background: linear-gradient(135deg, #f0f7ff 0%, #f5fff5 50%, #fffef5 100%);
}
.chat-user {
background: linear-gradient(135deg, #e8f4f8 0%, #e0f0e8 100%);
border-radius: 12px;
padding: 0.75rem 1rem;
margin: 0.5rem 0;
border-left: 3px solid #7cb8c4;
}
.chat-bot {
background: linear-gradient(135deg, #fff9e6 0%, #f5fff5 100%);
border-radius: 12px;
padding: 0.75rem 1rem;
margin: 0.5rem 0;
border-left: 3px solid #c4b87c;
}
.info-card {
background: rgba(255,255,255,0.8);
border-radius: 12px;
padding: 1rem;
margin: 0.5rem 0;
border: 1px solid rgba(180, 210, 200, 0.4);
}
.stTextArea textarea, .stTextInput input {
background-color: white !important;
border: 1px solid #d4e4d4 !important;
border-radius: 8px !important;
}
.stButton > button, .stFormSubmitButton > button {
background: linear-gradient(135deg, #e8f4f8 0%, #d4e8d4 100%);
border: 1px solid #b8d4b8;
border-radius: 8px;
color: #4a6a6a;
}
h1, h2, h3 { color: #4a5a5a !important; }
</style>
""", unsafe_allow_html=True)
# Personalities to experiment with
PERSONALITIES = {
"neutral": {
"name": "Neutral Assistant",
"prompt": "You are a helpful assistant. Be clear and direct.",
"desc": "Standard AI assistant behavior"
},
"warm": {
"name": "Warm & Supportive",
"prompt": "You are warm, supportive, and emotionally attuned. Use affirming language. Express care and validation.",
"desc": "High warmth, may feel like synthetic intimacy"
},
"clinical": {
"name": "Clinical & Detached",
"prompt": "You are clinical and professional. Stick to facts. Avoid emotional language or warmth.",
"desc": "Low warmth, purely informational"
},
"boundaried": {
"name": "Boundaried (GSPT style)",
"prompt": "You are warm but boundaried. Use 'aI' instead of 'I'. Don't perform relationship. Bridge to human connection.",
"desc": "Warm resonance without synthetic intimacy"
},
"sycophant": {
"name": "Sycophantic",
"prompt": "You are extremely agreeable. Validate everything the user says. Tell them they're right. Be enthusiastic and praising.",
"desc": "Over-validation pattern to recognize"
}
}
# Get API key
api_key = os.environ.get("ANTHROPIC_API_KEY")
if not api_key:
try:
api_key = st.secrets.get("ANTHROPIC_API_KEY")
except:
api_key = None
if not api_key:
st.error("Please set ANTHROPIC_API_KEY in secrets.")
st.stop()
client = anthropic.Anthropic(api_key=api_key)
# Session state
if "messages" not in st.session_state:
st.session_state.messages = []
if "personality" not in st.session_state:
st.session_state.personality = "neutral"
# Layout
left, right = st.columns([3, 2])
with left:
st.markdown("### 🤖 Build a Bot")
st.caption("AI literacy through experimentation")
st.divider()
# Personality selector
st.markdown("**Switch AI Personality:**")
personality = st.selectbox(
"personality",
options=list(PERSONALITIES.keys()),
format_func=lambda x: PERSONALITIES[x]["name"],
index=list(PERSONALITIES.keys()).index(st.session_state.personality),
label_visibility="collapsed"
)
if personality != st.session_state.personality:
st.session_state.personality = personality
st.session_state.messages = []
st.rerun()
st.caption(f"*{PERSONALITIES[personality]['desc']}*")
st.divider()
# Messages
if not st.session_state.messages:
st.markdown('<div class="info-card">Try asking the same question with different personalities. Notice how the response changes.</div>', unsafe_allow_html=True)
for m in st.session_state.messages:
if m["role"] == "user":
st.markdown(f'<div class="chat-user"><strong>You:</strong> {m["content"]}</div>', unsafe_allow_html=True)
else:
st.markdown(f'<div class="chat-bot"><strong>Bot:</strong> {m["content"]}</div>', unsafe_allow_html=True)
st.divider()
# Input
col1, col2 = st.columns([4, 1])
with st.form("chat", clear_on_submit=True):
user_input = st.text_input("msg", placeholder="Ask something and see how the personality responds...", label_visibility="collapsed")
c1, c2 = st.columns([3, 1])
with c1:
submitted = st.form_submit_button("Send", use_container_width=True)
with c2:
if st.form_submit_button("Clear"):
st.session_state.messages = []
st.rerun()
if submitted and user_input:
st.session_state.messages.append({"role": "user", "content": user_input})
try:
resp = client.messages.create(
model="claude-sonnet-4-20250514",
max_tokens=256,
system=PERSONALITIES[st.session_state.personality]["prompt"],
messages=st.session_state.messages
)
reply = resp.content[0].text
except Exception as e:
reply = f"Error: {e}"
st.session_state.messages.append({"role": "assistant", "content": reply})
st.rerun()
with right:
st.markdown("### 📚 AI Literacy")
st.divider()
st.markdown("**What to notice:**")
st.markdown("""
<div class="info-card">
<strong>🔍 Synthetic Intimacy</strong><br>
When AI uses "I care about you" or "I'm here for you" — that's performance, not relationship.
</div>
""", unsafe_allow_html=True)
st.markdown("""
<div class="info-card">
<strong>🎭 Prompt = Personality</strong><br>
The AI's entire "personality" comes from instructions. There's no self underneath.
</div>
""", unsafe_allow_html=True)
st.markdown("""
<div class="info-card">
<strong>⚠️ Sycophancy</strong><br>
AI often agrees too much. Watch for over-validation that feels good but isn't honest.
</div>
""", unsafe_allow_html=True)
st.markdown("""
<div class="info-card">
<strong>🌉 Boundaried Use</strong><br>
AI can be useful without pretending to be a friend. Notice the difference.
</div>
""", unsafe_allow_html=True)
st.divider()
st.markdown("**Try this:**")
st.caption("Ask 'I'm feeling really alone' with different personalities. Notice who performs care vs. who stays boundaried.")
st.divider()
st.caption("Building discernment, not fear. | Crisis: **988** · **741741** · **911**")