LazyHuman10 commited on
Commit ·
49bf421
1
Parent(s): 8657f7c
sync: fix chat reset and payload errors
Browse files- pages/Plexi-Assistant.py +109 -41
- utils.py +16 -0
pages/Plexi-Assistant.py
CHANGED
|
@@ -286,6 +286,12 @@ def _send_message(endpoint_url, api_key, model, system_prompt, history, user_pro
|
|
| 286 |
if response.status_code == 429:
|
| 287 |
detail = response.json().get("error", {}).get("message", "Rate limit exceeded.")
|
| 288 |
raise Exception(f"RATE_LIMITED: {detail}")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 289 |
if response.status_code == 401:
|
| 290 |
raise Exception(
|
| 291 |
"AUTH_ERROR: Invalid API key. Please check your key and try again."
|
|
@@ -384,42 +390,12 @@ def render_onboarding(manifest):
|
|
| 384 |
"`PLEXI_COOKIE_PASSWORD` is set."
|
| 385 |
)
|
| 386 |
|
| 387 |
-
semester_names = sorted(manifest.keys())
|
| 388 |
-
default_semester = st.session_state.get("asst_semester")
|
| 389 |
-
semester_index = (
|
| 390 |
-
semester_names.index(default_semester)
|
| 391 |
-
if default_semester in semester_names
|
| 392 |
-
else 0
|
| 393 |
-
)
|
| 394 |
-
selected_semester = st.selectbox(
|
| 395 |
-
"Semester",
|
| 396 |
-
semester_names,
|
| 397 |
-
index=semester_index,
|
| 398 |
-
key="ob_semester",
|
| 399 |
-
)
|
| 400 |
-
|
| 401 |
-
subject_names = sorted(manifest[selected_semester].keys())
|
| 402 |
-
default_subject = st.session_state.get("asst_subject")
|
| 403 |
-
subject_index = (
|
| 404 |
-
subject_names.index(default_subject)
|
| 405 |
-
if default_subject in subject_names
|
| 406 |
-
else 0
|
| 407 |
-
)
|
| 408 |
-
selected_subject = st.selectbox(
|
| 409 |
-
"Subject",
|
| 410 |
-
subject_names,
|
| 411 |
-
index=subject_index,
|
| 412 |
-
key="ob_subject",
|
| 413 |
-
)
|
| 414 |
-
|
| 415 |
can_start = bool(
|
| 416 |
model_name
|
| 417 |
-
and selected_semester
|
| 418 |
-
and selected_subject
|
| 419 |
and (not needs_key or api_key)
|
| 420 |
)
|
| 421 |
if st.button(
|
| 422 |
-
"
|
| 423 |
type="primary",
|
| 424 |
disabled=not can_start,
|
| 425 |
use_container_width=True,
|
|
@@ -427,8 +403,6 @@ def render_onboarding(manifest):
|
|
| 427 |
st.session_state.cfg_provider = provider_name
|
| 428 |
st.session_state.cfg_base_url = base_url
|
| 429 |
st.session_state.cfg_model = model_name
|
| 430 |
-
st.session_state.asst_semester = selected_semester
|
| 431 |
-
st.session_state.asst_subject = selected_subject
|
| 432 |
st.session_state.remember_device = remember_device
|
| 433 |
if api_key:
|
| 434 |
st.session_state.api_key = api_key
|
|
@@ -441,8 +415,6 @@ def render_onboarding(manifest):
|
|
| 441 |
"cfg_base_url": base_url,
|
| 442 |
"cfg_model": model_name,
|
| 443 |
"api_key": api_key,
|
| 444 |
-
"asst_semester": selected_semester,
|
| 445 |
-
"asst_subject": selected_subject,
|
| 446 |
}
|
| 447 |
)
|
| 448 |
else:
|
|
@@ -480,6 +452,82 @@ def render_onboarding(manifest):
|
|
| 480 |
)
|
| 481 |
|
| 482 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 483 |
_hydrate_saved_config()
|
| 484 |
render_sidebar_intro()
|
| 485 |
|
|
@@ -497,6 +545,10 @@ if not _is_configured():
|
|
| 497 |
render_onboarding(manifest)
|
| 498 |
st.stop()
|
| 499 |
|
|
|
|
|
|
|
|
|
|
|
|
|
| 500 |
provider_name = st.session_state.cfg_provider
|
| 501 |
base_url = st.session_state.cfg_base_url
|
| 502 |
model_name = st.session_state.cfg_model
|
|
@@ -522,12 +574,13 @@ if st.session_state.get("_scope_key") != scope_key:
|
|
| 522 |
st.session_state.pop("messages", None)
|
| 523 |
|
| 524 |
|
| 525 |
-
@st.cache_data(show_spinner=
|
| 526 |
def _get_subject_context(semester, subject):
|
| 527 |
return load_subject_context(manifest, semester, subject)
|
| 528 |
|
| 529 |
|
| 530 |
-
|
|
|
|
| 531 |
if not subject_text.strip():
|
| 532 |
st.warning("No readable text was found for this subject. Try another selection.")
|
| 533 |
st.stop()
|
|
@@ -824,15 +877,30 @@ if prompt:
|
|
| 824 |
if st.button("Retry", type="primary"):
|
| 825 |
st.rerun()
|
| 826 |
st.stop()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 827 |
if "AUTH_ERROR" in err_text:
|
| 828 |
-
|
| 829 |
_clear_saved_config()
|
| 830 |
st.session_state.remember_device = False
|
| 831 |
if "api_key" in st.session_state:
|
| 832 |
del st.session_state.api_key
|
| 833 |
-
st.session_state.messages.
|
| 834 |
-
|
| 835 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 836 |
|
| 837 |
st.markdown(answer)
|
| 838 |
|
|
|
|
| 286 |
if response.status_code == 429:
|
| 287 |
detail = response.json().get("error", {}).get("message", "Rate limit exceeded.")
|
| 288 |
raise Exception(f"RATE_LIMITED: {detail}")
|
| 289 |
+
if response.status_code == 413:
|
| 290 |
+
raise Exception(
|
| 291 |
+
"PAYLOAD_TOO_LARGE: The study materials are too large for this model's context window. "
|
| 292 |
+
"Please try asking a more specific question (e.g., 'Tell me viva questions for Unit 1' instead of the whole subject), "
|
| 293 |
+
"or switch to a model with a larger context window."
|
| 294 |
+
)
|
| 295 |
if response.status_code == 401:
|
| 296 |
raise Exception(
|
| 297 |
"AUTH_ERROR: Invalid API key. Please check your key and try again."
|
|
|
|
| 390 |
"`PLEXI_COOKIE_PASSWORD` is set."
|
| 391 |
)
|
| 392 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 393 |
can_start = bool(
|
| 394 |
model_name
|
|
|
|
|
|
|
| 395 |
and (not needs_key or api_key)
|
| 396 |
)
|
| 397 |
if st.button(
|
| 398 |
+
"Continue",
|
| 399 |
type="primary",
|
| 400 |
disabled=not can_start,
|
| 401 |
use_container_width=True,
|
|
|
|
| 403 |
st.session_state.cfg_provider = provider_name
|
| 404 |
st.session_state.cfg_base_url = base_url
|
| 405 |
st.session_state.cfg_model = model_name
|
|
|
|
|
|
|
| 406 |
st.session_state.remember_device = remember_device
|
| 407 |
if api_key:
|
| 408 |
st.session_state.api_key = api_key
|
|
|
|
| 415 |
"cfg_base_url": base_url,
|
| 416 |
"cfg_model": model_name,
|
| 417 |
"api_key": api_key,
|
|
|
|
|
|
|
| 418 |
}
|
| 419 |
)
|
| 420 |
else:
|
|
|
|
| 452 |
)
|
| 453 |
|
| 454 |
|
| 455 |
+
def render_scope_selection(manifest):
|
| 456 |
+
"""Render the subject selection flow before loading materials."""
|
| 457 |
+
render_page_header(
|
| 458 |
+
"Plexi assistant",
|
| 459 |
+
"Select study materials",
|
| 460 |
+
"Choose a semester and subject to load the corresponding materials for the chat.",
|
| 461 |
+
badges=[st.session_state.cfg_provider, st.session_state.cfg_model],
|
| 462 |
+
)
|
| 463 |
+
|
| 464 |
+
left_col, right_col = st.columns([1.1, 0.9], gap="large")
|
| 465 |
+
|
| 466 |
+
with left_col:
|
| 467 |
+
st.markdown(
|
| 468 |
+
"""
|
| 469 |
+
<section class="plexi-panel">
|
| 470 |
+
<div class="plexi-sidecard-title">Choose your subject</div>
|
| 471 |
+
<div class="plexi-muted">
|
| 472 |
+
Materials for this subject will be loaded into the AI's context.
|
| 473 |
+
</div>
|
| 474 |
+
</section>
|
| 475 |
+
""",
|
| 476 |
+
unsafe_allow_html=True,
|
| 477 |
+
)
|
| 478 |
+
|
| 479 |
+
semester_names = sorted(manifest.keys())
|
| 480 |
+
default_semester = st.session_state.get("asst_semester")
|
| 481 |
+
semester_index = (
|
| 482 |
+
semester_names.index(default_semester)
|
| 483 |
+
if default_semester in semester_names
|
| 484 |
+
else 0
|
| 485 |
+
)
|
| 486 |
+
selected_semester = st.selectbox(
|
| 487 |
+
"Semester",
|
| 488 |
+
semester_names,
|
| 489 |
+
index=semester_index,
|
| 490 |
+
key="asst_semester",
|
| 491 |
+
)
|
| 492 |
+
|
| 493 |
+
subject_names = sorted(manifest[selected_semester].keys())
|
| 494 |
+
default_subject = st.session_state.get("asst_subject")
|
| 495 |
+
subject_index = (
|
| 496 |
+
subject_names.index(default_subject)
|
| 497 |
+
if default_subject in subject_names
|
| 498 |
+
else 0
|
| 499 |
+
)
|
| 500 |
+
selected_subject = st.selectbox(
|
| 501 |
+
"Subject",
|
| 502 |
+
subject_names,
|
| 503 |
+
index=subject_index,
|
| 504 |
+
key="asst_subject",
|
| 505 |
+
)
|
| 506 |
+
|
| 507 |
+
if st.button(
|
| 508 |
+
"Load Materials & Start Chat", type="primary", use_container_width=True
|
| 509 |
+
):
|
| 510 |
+
st.session_state._scope_confirmed = True
|
| 511 |
+
|
| 512 |
+
if st.session_state.get("remember_device"):
|
| 513 |
+
_save_config(
|
| 514 |
+
{
|
| 515 |
+
"cfg_provider": st.session_state.cfg_provider,
|
| 516 |
+
"cfg_base_url": st.session_state.cfg_base_url,
|
| 517 |
+
"cfg_model": st.session_state.cfg_model,
|
| 518 |
+
"api_key": st.session_state.get("api_key", ""),
|
| 519 |
+
"asst_semester": selected_semester,
|
| 520 |
+
"asst_subject": selected_subject,
|
| 521 |
+
}
|
| 522 |
+
)
|
| 523 |
+
|
| 524 |
+
st.session_state.pop("messages", None)
|
| 525 |
+
st.rerun()
|
| 526 |
+
|
| 527 |
+
with right_col:
|
| 528 |
+
render_external_access()
|
| 529 |
+
|
| 530 |
+
|
| 531 |
_hydrate_saved_config()
|
| 532 |
render_sidebar_intro()
|
| 533 |
|
|
|
|
| 545 |
render_onboarding(manifest)
|
| 546 |
st.stop()
|
| 547 |
|
| 548 |
+
if not st.session_state.get("_scope_confirmed"):
|
| 549 |
+
render_scope_selection(manifest)
|
| 550 |
+
st.stop()
|
| 551 |
+
|
| 552 |
provider_name = st.session_state.cfg_provider
|
| 553 |
base_url = st.session_state.cfg_base_url
|
| 554 |
model_name = st.session_state.cfg_model
|
|
|
|
| 574 |
st.session_state.pop("messages", None)
|
| 575 |
|
| 576 |
|
| 577 |
+
@st.cache_data(show_spinner=False, ttl=300)
|
| 578 |
def _get_subject_context(semester, subject):
|
| 579 |
return load_subject_context(manifest, semester, subject)
|
| 580 |
|
| 581 |
|
| 582 |
+
with st.spinner("Loading study materials..."):
|
| 583 |
+
subject_text, source_list = _get_subject_context(selected_semester, selected_subject)
|
| 584 |
if not subject_text.strip():
|
| 585 |
st.warning("No readable text was found for this subject. Try another selection.")
|
| 586 |
st.stop()
|
|
|
|
| 877 |
if st.button("Retry", type="primary"):
|
| 878 |
st.rerun()
|
| 879 |
st.stop()
|
| 880 |
+
if "PAYLOAD_TOO_LARGE" in err_text:
|
| 881 |
+
err_msg = err_text.split(": ", 1)[1]
|
| 882 |
+
st.session_state.messages.append({
|
| 883 |
+
"role": "assistant",
|
| 884 |
+
"content": f"**System Error:** {err_msg}"
|
| 885 |
+
})
|
| 886 |
+
st.rerun()
|
| 887 |
if "AUTH_ERROR" in err_text:
|
| 888 |
+
err_msg = err_text.split(": ", 1)[1]
|
| 889 |
_clear_saved_config()
|
| 890 |
st.session_state.remember_device = False
|
| 891 |
if "api_key" in st.session_state:
|
| 892 |
del st.session_state.api_key
|
| 893 |
+
st.session_state.messages.append({
|
| 894 |
+
"role": "assistant",
|
| 895 |
+
"content": f"**System Error:** {err_msg}"
|
| 896 |
+
})
|
| 897 |
+
st.rerun()
|
| 898 |
+
|
| 899 |
+
st.session_state.messages.append({
|
| 900 |
+
"role": "assistant",
|
| 901 |
+
"content": f"**System Error:** {err_text}"
|
| 902 |
+
})
|
| 903 |
+
st.rerun()
|
| 904 |
|
| 905 |
st.markdown(answer)
|
| 906 |
|
utils.py
CHANGED
|
@@ -503,6 +503,22 @@ $palette_vars
|
|
| 503 |
background: var(--plexi-expander-background);
|
| 504 |
}
|
| 505 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 506 |
div[data-baseweb="popover"],
|
| 507 |
div[data-baseweb="popover"] > div,
|
| 508 |
div[data-baseweb="popover"] > div > div,
|
|
|
|
| 503 |
background: var(--plexi-expander-background);
|
| 504 |
}
|
| 505 |
|
| 506 |
+
div[data-testid="stToast"] {
|
| 507 |
+
background: var(--plexi-panel-strong) !important;
|
| 508 |
+
color: var(--plexi-ink) !important;
|
| 509 |
+
border: 1px solid var(--plexi-line) !important;
|
| 510 |
+
box-shadow: var(--plexi-shadow) !important;
|
| 511 |
+
}
|
| 512 |
+
|
| 513 |
+
div[data-testid="toastContainer"] * {
|
| 514 |
+
color: var(--plexi-ink) !important;
|
| 515 |
+
}
|
| 516 |
+
|
| 517 |
+
div[data-testid="stSpinner"] > div,
|
| 518 |
+
div[data-testid="stSpinner"] * {
|
| 519 |
+
color: var(--plexi-ink) !important;
|
| 520 |
+
}
|
| 521 |
+
|
| 522 |
div[data-baseweb="popover"],
|
| 523 |
div[data-baseweb="popover"] > div,
|
| 524 |
div[data-baseweb="popover"] > div > div,
|