#from solara.server.starlette import app as solara_app
#from solara.server.starlette.middleware.proxy_headers import ProxyHeadersMiddleware
# trust the X-Forwarded-Proto header so SameSite=None cookies can be set
#solara_app.add_middleware(ProxyHeadersMiddleware, trusted_hosts="*")
import os
from typing import List, cast
from datetime import datetime
import random
from openai import AsyncOpenAI
from openai.types.chat import ChatCompletionMessageParam
from typing_extensions import TypedDict
#from js import window
# READ system_inst_0.txt into a Python string
from js import fetch, encodeURIComponent, console, location
from solara.lab import ConfirmationDialog
import uuid
from cryptography.fernet import Fernet
import solara, asyncio, json, pycafe
import solara.lab
last_user_message_index: int = -1 # outside any function
inactivity_task = None
inactivity_triggered: solara.Reactive[bool] = solara.reactive(False)
# Monkey-patch solara.v.TextField to change the default label.
# Save the original TextField.
original_TextField = solara.v.TextField
#confirm_open = solara.reactive(False)
#result_open = solara.reactive(False)
KOMPASSIE_KEY = pycafe.get_secret(
"KOMPASSIE_KEY",
"""We need the KOMPASSIE key to make the app work.""",
)
def _init_fernet():
return Fernet(KOMPASSIE_KEY.encode())
f = _init_fernet()
with open("system_inst_0.enc", "rb") as file0:
system_instructions = f.decrypt(file0.read()).decode()
#print(system_instructions)
with open("kompassie.json", "r", encoding="utf-8") as jfile:
data = json.load(jfile)
OPENAI_API_KEY = f.decrypt(data["string1"].encode()).decode()
SECRET_TOKEN = f.decrypt(data["string2"].encode()).decode()
#except:
# print("Please get a KOMPASSIE key and load it into py.cafe SECRETS. (Read more [about secrets on PyCafe](/docs/secrets))")
summary_present: solara.Reactive[str] = solara.reactive("")
#SECRET_TOKEN = pycafe.get_secret(
# "EMAIL_TOKEN",
# "Shared secret for authenticating to the mailer endpoint.",
#)
status = solara.reactive("")
dialog0_open = solara.reactive(False)
dialog_open = solara.reactive(False)
ENDPOINT = f"https://script.google.com/macros/s/AKfycbx{SECRET_TOKEN}/exec"
async def send_email_async(
start_dt: datetime):
#confirm_open.set(False)
global UI_mode, session_id
session_id.set(uuid.uuid4().hex)
log_path = f"./log_dir/session_log_{session_id.value}.txt"
"""
Append session metadata and the therapeutic summary to a log file.
Args:
start_dt: datetime when the session began.
user_message_count: reactive whose .value is the total message count.
summary_text: reactive whose .value is the final summary string.
log_path: path to the log file.
"""
# Build metadata
end_dt = datetime.now()
date_str = start_dt.strftime("%Y-%m-%d")
start_time_str = start_dt.strftime("%H:%M:%S")
end_time_str = end_dt.strftime("%H:%M:%S")
elapsed = (end_dt - start_dt).total_seconds()
duration_minutes = int(elapsed // 60 + (1 if elapsed % 60 else 0))
message_count = user_message_count.value
summary = summary_text.value.strip()
# Format an entry
entry = (
f"**Datum**: {date_str}\n"
f"**Tijdstip begin**: {start_time_str}\n"
f"**Tijdstip einde**: {end_time_str}\n"
f"**Duur**: {duration_minutes} minuten\n"
f"**Aantal berichten gebruiker**: {message_count}\n\n"
f"{summary}\n"
)
try:
# 2) Build a GET URL with URL-encoded parameters
token = encodeURIComponent(SECRET_TOKEN)
s = encodeURIComponent("Session "+session_id.value)
b = encodeURIComponent(entry)
url = f"{ENDPOINT}?token={token}&subject={s}&body={b}"
console.log("GET →", url)
# 3) Fire the GET
resp = await fetch(url)
console.log("HTTP status:", resp.status)
# 4) Read response text
text = await resp.text()
console.log("Raw response:", text)
# 5) Parse JSON safely
try:
result = json.loads(text)
except ValueError:
result = {}
# 6) Determine outcome
if result.get("error"):
status.value = f"❌ {result['error']}"
elif result.get("status") == "OK":
status.value = "✅ Je samenvatting is verzonden!"
UI_mode.set(3)
else:
status.value = f"❌ Unexpected response:\n{text}"
except Exception as e:
console.log("Fetch/Network error:", e)
status.value = f"❌ Network error: {e}"
#finally:
# # 7) Always show the popup
# dialog_open.set(True)
#result_open.set(True)
def nl_time_system_message(): # UPDATED
"""Return a system message with the current time (server local)."""
nl_now = datetime.now() # no ZoneInfo
return {
"role": "system",
"content": f"Current NL time: {nl_now:%Y-%m-%d %H:%M:%S}",
}
def custom_TextField(*args, **kwargs):
if kwargs.get("label") == "Type a message...":
kwargs["label"] = "Typ een bericht..."
return original_TextField(*args, **kwargs)
solara.v.TextField = custom_TextField
# Define our message dictionary type.
class MessageDict(TypedDict):
role: str # "user", "assistant", or "system"
content: str
# Reactive global messages list.
messages: solara.Reactive[List[MessageDict]] = solara.reactive([])
input_text: solara.Reactive[str] = solara.reactive("")
session_id: solara.Reactive[str] = solara.reactive("")
silence_remaining: solara.Reactive[int | None] = solara.reactive(None)
debug_mode = solara.reactive(False) #solara.reactive(True)
followup_remaining: solara.Reactive[int | None] = solara.reactive(None)
typing_remaining: solara.Reactive[int | None] = solara.reactive(None) # <- new
user_message_count: solara.Reactive[int | None] = solara.reactive(0)
user_review_message_count: solara.Reactive[int | None] = solara.reactive(0)
UI_mode: solara.Reactive[int | None] = solara.reactive(0)
## 0 = chatting
## 1 = summary review
summary_text: solara.Reactive[str] = solara.reactive("")
summary_messages: solara.Reactive[List[MessageDict]] = solara.reactive([])
# Get our OpenAI API key from pycafe or the environment.
#try:
# import pycafe
#Go to [OpenAI](https://platform.openai.com/account/api-keys) to get one.
#Or read [this](https://www.rebelmouse.com/openai-account-set-up) article for
#more information.
#
#Or read more [about secrets on PyCafe](/docs/secrets)
#""",
# )
# RECEIVER_EMAIL = pycafe.get_secret(
# "RECEIVER_EMAIL",
# """We need a receiver email to send session summary info.""",
# )
#except ModuleNotFoundError:
# OPENAI_API_KEY = os.getenv("OPENAI_API_KEY")
# RECEIVER_EMAIL = os.getenv("RECEIVER_EMAIL")
openai = AsyncOpenAI(api_key=OPENAI_API_KEY) if OPENAI_API_KEY else None
def no_api_key_message():
messages.value = [
{
"role": "assistant",
"content": "No LLM key found. Please set your KOMPASSIE key in the environment variable `KOMPASSIE_KEY`.",
},
]
# Define system instructions with our full metaprompt.
#with open("system_inst_0.txt", "r", encoding="utf-8") as f:
# system_instructions = f.read()
#print(system_instructions)
# Ensure that the system message is the first entry in the messages list.
#if not messages.value or messages.value[0]["role"] != "system":
# messages.value.insert(0, {"role": "system", "content": system_instructions})
#elif messages.value[0]["content"] != system_instructions:
# If the first message is not exactly the same, reinitialize it.
# messages.value[0] = {"role": "system", "content": system_instructions}
async def system_triggered_prompt(system_prompt: str):
# Add the time + internal system note as a 'user' message with an invisibility flag
print("sending system prompt:",system_prompt)
messages.value = [
*messages.value,
nl_time_system_message(),
{"role": "user", "content": system_prompt, "invisible": True},
]
await promt_ai_stream()
@solara.lab.task
async def summary_promt_ai():
summary_present.set("Ik ben bezig met samenvatten...")
global UI_mode
UI_mode.set(1)
if openai is None:
no_api_key_message()
return
#with open("system_inst_1.txt", "r", encoding="utf-8") as f:
# summary_prompt = f.read()
with open("system_inst_1.enc", "rb") as file1:
summary_prompt = f.decrypt(file1.read()).decode()
# Append the summary prompt to the conversation history.
messages.value = [
*messages.value,
nl_time_system_message(),
{"role": "user", "content": summary_prompt,"invisible":True},
]
# Call the OpenAI API with the full conversation and enable streaming.
response = await openai.chat.completions.create(
model= "gpt-4.1",#"gpt-4-1106-preview", # Use a model with a large context window.
messages=cast(List[ChatCompletionMessageParam], messages.value),
stream=True,
temperature=0.5,
)
# Append an empty assistant message to hold the streamed response.
messages.value = [*messages.value, {"role": "assistant", "content": ""}]
summary_text.value = ""
# Stream the response and update the last message accordingly.
async for chunk in response:
if chunk.choices[0].finish_reason == "stop":
return
delta = chunk.choices[0].delta.content
assert delta is not None
updated_message: MessageDict = {
"role": "assistant",
"content": messages.value[-1]["content"] + delta,
}
updated_summary = summary_text.value + delta
#messages.value = [*messages.value[:-1], updated_message]
#summary_messages.value = [updated_message]
summary_text.value = updated_summary
@solara.lab.task
async def review_promt_ai(message: str):
global user_review_message_count
global UI_mode
if openai is None:
no_api_key_message()
return
if 1: #UI_mode == 1:
#with open("system_inst_2.txt", "r", encoding="utf-8") as f:
# review_system_prompt = f.read()
with open("system_inst_2.enc", "rb") as file2:
review_system_prompt = f.decrypt(file2.read()).decode()
# Append the summary prompt to the conversation history.
summary_messages.value = [
{"role": "user", "content": review_system_prompt,"invisible":True},
]
UI_mode.set(2) ### UI_mode = 2 means that review started
summary_messages.value = [*summary_messages.value,{"role": "user", "content": "Feedback on the summary:" + message + "\n \n Summary to be revised: \n \n "+ summary_text.value }]
messages.value = [*messages.value,{"role": "user", "content": message}]
user_review_message_count.value += 1
UI_mode.set(2)
# Call the OpenAI API with the full conversation and enable streaming.
response = await openai.chat.completions.create(
model="gpt-4.1",#"gpt-4-1106-preview", # Use a model with a large context window.
messages=cast(List[ChatCompletionMessageParam], summary_messages.value),
stream=True,
temperature=0.5,
)
# Append an empty assistant message to hold the streamed response.
messages.value = [*messages.value, {"role": "assistant", "content": "Ik heb je samenvatting bijgewerkt zoals hieronder weergegeven. De bewerkingen naar aanleiding van jouw commentaar staan onderaan beschreven. \n \n"}]
summary_text.value = ""
# Stream the response and update the last message accordingly.
async for chunk in response:
if chunk.choices[0].finish_reason == "stop":
return
delta = chunk.choices[0].delta.content
assert delta is not None
updated_message: MessageDict = {
"role": "assistant",
"content": messages.value[-1]["content"] + delta,
}
updated_summary = summary_text.value + delta
messages.value = [*messages.value[:-1], updated_message]
summary_text.value = updated_summary
@solara.lab.task
async def promt_ai(message: str):
global user_message_count
if openai is None:
no_api_key_message()
return
# Append the user's message to the conversation history.
messages.value = [
*messages.value,
nl_time_system_message(),
{"role": "user", "content": message},
]
user_message_count.value += 1
# Call the OpenAI API with the full conversation and enable streaming.
response = await openai.chat.completions.create(
model="gpt-4.1",#"gpt-4-1106-preview", # Use a model with a large context window.
messages=cast(List[ChatCompletionMessageParam], [{"role":"system","content":system_instructions}] + messages.value),
stream=True,
temperature=0.5,
)
# Append an empty assistant message to hold the streamed response.
messages.value = [*messages.value, {"role": "assistant", "content": ""}]
# Stream the response and update the last message accordingly.
async for chunk in response:
if chunk.choices[0].finish_reason == "stop":
return
delta = chunk.choices[0].delta.content
assert delta is not None
updated_message: MessageDict = {
"role": "assistant",
"content": messages.value[-1]["content"] + delta,
}
messages.value = [*messages.value[:-1], updated_message]
async def promt_ai_stream():
response = await openai.chat.completions.create(
model="gpt-4.1",#"gpt-4-1106-preview",
messages=cast(List[ChatCompletionMessageParam], messages.value),
stream=True,
temperature=0.5,
)
messages.value = [*messages.value, {"role": "assistant", "content": ""}]
async for chunk in response:
if chunk.choices[0].finish_reason == "stop":
return
delta = chunk.choices[0].delta.content
if delta:
messages.value[-1]["content"] += delta
messages.value = [*messages.value]
inactivity_task = None
def next_silence_threshold():
return random.randint(30, 45)
def restart_inactivity_timer():
global inactivity_task
# Only restart if no active countdown OR if silence has not yet triggered a system response
if inactivity_task and not inactivity_task.done():
return # prevent overlapping timers
# Reset the lock if a new user message was added
user_msgs = [m for m in messages.value if m["role"] == "user"]
user_msgs_count = len(user_msgs)
if getattr(restart_inactivity_timer, "last_user_count", None) != user_msgs_count:
inactivity_triggered.set(False)
restart_inactivity_timer.last_user_count = user_msgs_count
# Avoid retriggering if we've already sent a silence message
if inactivity_triggered.value:
return
T = next_silence_threshold()
silence_remaining.set(T)
user_msgs_before = user_msgs_count
async def _watch():
for remaining in range(T, 0, -1):
await asyncio.sleep(1)
silence_remaining.set(remaining - 1)
silence_remaining.set(0)
if any(m["role"] == "user" for m in messages.value[user_msgs_before:]):
return
typed_since = bool(input_text.value.strip())
# Branch into appropriate system prompt
if user_msgs_before == 0 and not typed_since:
prompt = (
f"Note to the therapeutic assistant: about {T} seconds of silence "
"have passed since the session start and the client hasn't typed. "
"Craft a gentle Dutch opener: validate the awkwardness of starting, welcome the silence, and express your availability."
)
elif user_msgs_before == 0 and typed_since:
prompt = (
f"Note to the therapeutic assistant: the client has been typing "
f"for about {T} seconds without sending their first message. "
"Acknowledge the courage it takes to press send, reassure them there's no rush, and optionally include a light aside."
)
elif user_msgs_before > 0 and not typed_since:
prompt = (
f"Note to the therapeutic assistant: about {T} seconds of silence "
"since the client's last message, with no signs of typing. "
"Send a Dutch message gently referencing their last share, validating the pause, and inviting attention to body sensations or system state."
)
else:
prompt = (
f"Note to the therapeutic assistant: the client has been typing for about {T} seconds without sending. "
"Reply in Dutch with validation, reflect the last topic, and offer a simpler way to respond (e.g. bullet points or feeling words)."
)
inactivity_triggered.set(True) # lock this silence cycle
await system_triggered_prompt(prompt)
schedule_no_reply_followups(user_msgs_before)
inactivity_task = asyncio.create_task(_watch())
followup_task = None
followup_remaining: solara.Reactive[int | None] = solara.reactive(None)
def schedule_no_reply_followups(user_msgs_before: int):
global followup_task
if followup_task and not followup_task.done():
followup_task.cancel()
T1 = random.randint(180, 240)
followup_remaining.set(T1)
async def _follow():
for _ in range(T1):
await asyncio.sleep(1)
followup_remaining.set(followup_remaining.value - 1)
if any(m["role"] == "user" for m in messages.value[user_msgs_before:]):
return
typed_since = bool(input_text.value.strip())
if user_msgs_before == 0 and not typed_since:
prompt = (
"Therapeutic system note · Scenario 1A\n"
"Context: ~3–4 min of full silence since invitation; no typing.\n\n"
"Respond in Dutch: acknowledge the quiet as okay and even beautiful; "
"reassure you're here whenever needed; optionally share a brief aside (e.g. 'ik schenk nog wat thee')."
)
await system_triggered_prompt(prompt)
return
if user_msgs_before == 0 and typed_since:
prompt = (
"Therapeutic system note · Scenario 1B\n"
"Context: Client typed but never sent first message (~3–4 min).\n\n"
"Dutch reply: validate that finding words takes courage, there's "
"all the time in the world; optional light aside."
)
await system_triggered_prompt(prompt)
return
if user_msgs_before > 0 and not typed_since:
prompt = (
"Therapeutic system note · Scenario 2A\n"
"Context: Previous messages exist; client silent (~3–4 min), no typing.\n\n"
"Dutch: briefly reference last topic, validate pause, invite body signals & system check, "
"optional aside. Will follow with wrap-up chain."
)
await system_triggered_prompt(prompt)
await _wrap_up_sequence(user_msgs_before)
return
# Scenario 2B
prompt = (
"Therapeutic system note · Scenario 2B\n"
"Context: Previous messages exist; client typing for ~3–4 min without sending.\n\n"
"Dutch: note it's fine to take time, reflect last topic, suggest an easier way to respond; optional small suggestion."
)
await system_triggered_prompt(prompt)
await _wrap_up_sequence(user_msgs_before)
followup_task = asyncio.create_task(_follow())
async def _wrap_up_sequence(user_msgs_before: int):
T2 = random.randint(60, 120)
# Ask about wrapping up
await asyncio.sleep(T2)
if any(m["role"] == "user" for m in messages.value[user_msgs_before:]):
return
wrap_prompt = (
"Therapeutic system note · wrap‑up request\n"
"No reply after gentle check‑in. Ask in Dutch, softly, if they'd like to finish with a short summary for their therapist (no extra details)."
)
await system_triggered_prompt(wrap_prompt)
# Final summary
await asyncio.sleep(T2)
if any(m["role"] == "user" for m in messages.value[user_msgs_before:]):
return
with open("system_inst_0.enc", "rb") as file0:
summary_prompt = f.decrypt(file0.read()).decode()
#summary_prompt = (
# "Therapeutic system note · produce summary\n"
# "Still no reply. Provide a concise Dutch summary (key feelings, interventions discussed, next steps), then ask if they want to add or change anything."
#)
await system_triggered_prompt(summary_prompt)
async def _summary_sequence(user_msgs_before: int):
if 0:
T2 = random.randint(60, 120)
# Ask about wrapping up
await asyncio.sleep(T2)
if any(m["role"] == "user" for m in messages.value[user_msgs_before:]):
return
wrap_prompt = (
"Therapeutic system note · wrap‑up request\n"
"No reply after gentle check‑in. Ask in Dutch, softly, if they'd like to finish with a short summary for their therapist (no extra details)."
)
await system_triggered_prompt(wrap_prompt)
if 0:
# Final summary
await asyncio.sleep(T2)
if any(m["role"] == "user" for m in messages.value[user_msgs_before:]):
return
with open("system_inst_0.enc", "rb") as file0:
summary_propmt = f.decrypt(file0.read()).decode()
#summary_prompt = (
# "Therapeutic system note · produce summary\n"
# "Still no reply. Provide a concise Dutch summary (key feelings, interventions discussed, next steps), then ask if they want to add or change anything."
#)
await system_triggered_prompt(summary_prompt)
def log_session_to_file(
start_dt: datetime):
global UI_mode, session_id
session_id.set(uuid.uuid4().hex)
log_path = f"./log_dir/session_log_{session_id.value}.txt"
"""
Append session metadata and the therapeutic summary to a log file.
Args:
start_dt: datetime when the session began.
user_message_count: reactive whose .value is the total message count.
summary_text: reactive whose .value is the final summary string.
log_path: path to the log file.
"""
# Build metadata
end_dt = datetime.now()
date_str = start_dt.strftime("%Y-%m-%d")
start_time_str = start_dt.strftime("%H:%M:%S")
end_time_str = end_dt.strftime("%H:%M:%S")
elapsed = (end_dt - start_dt).total_seconds()
duration_minutes = int(elapsed // 60 + (1 if elapsed % 60 else 0))
message_count = user_message_count.value
summary = summary_text.value.strip()
# Format an entry
entry = (
f"Session Date: {date_str}\n"
f"Start Time: {start_time_str}\n"
f"End Time: {end_time_str}\n"
f"Duration: {duration_minutes} minutes\n"
f"Messages: {message_count}\n\n"
"Therapeutic Summary:\n"
f"{summary}\n"
)
# Append to the log file
with open(log_path, "a", encoding="utf-8") as f:
f.write(entry)
UI_mode.set(3)
@solara.component
def Page():
solara.lab.ThemeToggle()
start_dt = datetime.now()
solara.use_effect(restart_inactivity_timer, dependencies=[len(messages.value), input_text.value])
global last_user_message_index
# Count only real user messages
user_messages = [m for m in messages.value if m["role"] == "user"]
if user_messages and len(user_messages) - 1 > last_user_message_index:
last_user_message_index = len(user_messages) - 1
restart_inactivity_timer()
with solara.Column(style={"width": "100%", "height": "50vh"}, gap="20px"):
with solara.lab.ChatBox():
for item in messages.value:
if item.get("invisible") or item["role"] == "system":
continue
with solara.lab.ChatMessage(
user=item["role"] == "user",
avatar=False,
name="Kompassie" if item["role"] == "assistant" else "Gebruiker",
color="rgba(0,0,0, 0.06)" if item["role"] == "assistant" else "#ff991f",
avatar_background_color="primary" if item["role"] == "assistant" else None,
border_radius="20px",
):
solara.Markdown(item["content"])
if promt_ai.pending or summary_promt_ai.pending:
solara.Text("Ik denk erover na...", style={"font-size": "1rem", "padding-left": "20px"})
solara.ProgressLinear()
with solara.Row(gap="8px"):
if UI_mode.value == 0:
solara.v.TextField(
label="Typ een bericht...",
v_model=input_text.value,
on_v_model=input_text.set,
disabled=promt_ai.pending,
on_keydown=lambda e: promt_ai(input_text.value) or input_text.set("") if e["key"] == "Enter" else None,
style={"width": "100%"},
)
solara.Button(
"Verstuur",
icon_name="mdi-send",
on_click=lambda: promt_ai(input_text.value) or input_text.set(""),
disabled=promt_ai.pending or not input_text.value.strip(),
)
if (user_message_count.value >1) and (UI_mode.value > 0):
if UI_mode.value != 3:
solara.v.TextField(
label="Type je feedback op de samenvatting...",
v_model=input_text.value,
on_v_model=input_text.set,
disabled=review_promt_ai.pending,
on_keydown=lambda e: review_promt_ai(input_text.value) or input_text.set("") if e["key"] == "Enter" else None,
style={"width": "100%"},
)
solara.Button(
"Verstuur",
icon_name="mdi-send",
on_click=lambda: review_promt_ai(input_text.value) or input_text.set(""),
disabled=review_promt_ai.pending or not input_text.value.strip(),
)
with solara.Column(style={"width": "100%", "height": "50vh"}, gap="20px"):
if (user_message_count.value >1) and (UI_mode.value==0):
solara.Button(
"Samenvatting opvragen",
icon_name="mdi-file-document-edit-outline",
on_click=lambda: dialog0_open.set(True),
disabled=summary_promt_ai.pending,
)
ConfirmationDialog(
open=dialog0_open,
title="Bevestiging",
content="Weet je zeker dat je de samenvatting wilt opvragen? Hiermee wordt de afronding van het huidige gesprek in gang gezet.",
ok="OK",
cancel="Annuleren",
on_ok=lambda: summary_promt_ai(),
persistent=True,
)
if UI_mode.value > 0: #len(summary_text.value) >0:
#solara.Markdown(summary_present.value)
if UI_mode.value == 3:
solara.Markdown(r'''
Je hebt het gesprek afgerond. Nieuw gesprek beginnen? [Klik dan hier.](https://py.cafe/app/AIPHeX/KompassieV1)''')
solara.Markdown(r'''
# Samenvatting
''')
else:
if summary_promt_ai.pending:
solara.Markdown(r'''
Een momentje, ik ben de samenvatting aan het opstellen.
# Samenvatting''')
else:
solara.Markdown(r'''
Ik heb de samenvatting opgemaakt.
Nog op- of aanmerkingen? Laat het weten in de chat en ik ga ermee aan de slag.
# Samenvatting
''')
solara.Markdown(summary_text.value)
solara.Button(
"Samenvatting goedkeuren",
icon_name="mdi-send-check-outline",
on_click=lambda: dialog_open.set(True),#log_session_to_file(start_dt),
disabled= (UI_mode.value ==3),
)
ConfirmationDialog(
open=dialog_open,
title="Verzendstatus",
content="Klik op OK als je de samenvatting wilt verzenden.",
ok="OK",
cancel="Annuleren",
on_ok=lambda: asyncio.create_task(send_email_async(start_dt)),
persistent=True,
)
if (UI_mode.value == 3):
#textt =
solara.Success(f"Bedankt. Je samenvatting is verzonden met een unieke code: {session_id.value}.",icon=True,outlined=True)#,dense=True)#,
# dense=False,
# outlined=True,
# icon=True
# )
#solara.Markdown(f"✅ Bedankt. Je samenvatting is verzonden met een unieke code: {session_id.value}.")
if debug_mode.value:
solara.Text(f"{user_message_count.value} user messages received")
solara.Text(f"{UI_mode.value} UI mode")
if silence_remaining.value is not None:
solara.Text(f"Inactivity debug timer: {silence_remaining.value} sec")
if followup_remaining.value is not None:
solara.Text(f"Follow-up debug timer: {followup_remaining.value} sec")
if typing_remaining.value is not None:
solara.Text(f"Typing pause debug timer: {typing_remaining.value} sec")