Profile-based chat titles are implemented
Browse files- app.py +1 -1
- assets/app.js +13 -2
- ui/intake/prompts.py +43 -0
- ui/server_api.py +16 -3
app.py
CHANGED
|
@@ -84,7 +84,7 @@ def api_build_research_prompt(
|
|
| 84 |
family: server_api.DropdownValue,
|
| 85 |
timeline: server_api.DropdownValue,
|
| 86 |
goals: str,
|
| 87 |
-
) -> str:
|
| 88 |
return server_api.build_research_prompt(
|
| 89 |
current_country,
|
| 90 |
residence_status,
|
|
|
|
| 84 |
family: server_api.DropdownValue,
|
| 85 |
timeline: server_api.DropdownValue,
|
| 86 |
goals: str,
|
| 87 |
+
) -> dict[str, str]:
|
| 88 |
return server_api.build_research_prompt(
|
| 89 |
current_country,
|
| 90 |
residence_status,
|
assets/app.js
CHANGED
|
@@ -33,6 +33,7 @@ const state = {
|
|
| 33 |
busy: false,
|
| 34 |
view: "form",
|
| 35 |
activeResearchTaskId: null,
|
|
|
|
| 36 |
};
|
| 37 |
|
| 38 |
const els = {
|
|
@@ -189,7 +190,7 @@ function persistActiveSession() {
|
|
| 189 |
const existing = index >= 0 ? sessions[index] : null;
|
| 190 |
const title = existing?.titleManuallySet
|
| 191 |
? existing.title
|
| 192 |
-
: sessionTitle(state.history);
|
| 193 |
|
| 194 |
const payload = {
|
| 195 |
id: state.sessionId,
|
|
@@ -244,6 +245,7 @@ function deleteSession(sessionId) {
|
|
| 244 |
state.sessionId = crypto.randomUUID();
|
| 245 |
state.history = [];
|
| 246 |
state.globeState = emptyGlobeState();
|
|
|
|
| 247 |
resetForm();
|
| 248 |
els.chatInput.value = "";
|
| 249 |
renderMessages();
|
|
@@ -391,6 +393,7 @@ function loadSession(sessionId) {
|
|
| 391 |
state.sessionId = session.id;
|
| 392 |
state.history = session.history || [];
|
| 393 |
state.globeState = session.globeState || emptyGlobeState();
|
|
|
|
| 394 |
els.chatInput.value = "";
|
| 395 |
renderMessages();
|
| 396 |
applyGlobeState(state.globeState);
|
|
@@ -406,6 +409,7 @@ function startNewChat() {
|
|
| 406 |
state.sessionId = crypto.randomUUID();
|
| 407 |
state.history = [];
|
| 408 |
state.globeState = emptyGlobeState();
|
|
|
|
| 409 |
resetForm();
|
| 410 |
els.chatInput.value = "";
|
| 411 |
renderMessages();
|
|
@@ -888,7 +892,14 @@ async function submitForm(event) {
|
|
| 888 |
setStatus("Building research prompt...");
|
| 889 |
try {
|
| 890 |
const result = await gradioPredict("/build_research_prompt", formPayload());
|
| 891 |
-
const
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 892 |
if (!message) {
|
| 893 |
setStatus("Could not build prompt.");
|
| 894 |
return;
|
|
|
|
| 33 |
busy: false,
|
| 34 |
view: "form",
|
| 35 |
activeResearchTaskId: null,
|
| 36 |
+
sessionAutoTitle: null,
|
| 37 |
};
|
| 38 |
|
| 39 |
const els = {
|
|
|
|
| 190 |
const existing = index >= 0 ? sessions[index] : null;
|
| 191 |
const title = existing?.titleManuallySet
|
| 192 |
? existing.title
|
| 193 |
+
: existing?.title ?? state.sessionAutoTitle ?? sessionTitle(state.history);
|
| 194 |
|
| 195 |
const payload = {
|
| 196 |
id: state.sessionId,
|
|
|
|
| 245 |
state.sessionId = crypto.randomUUID();
|
| 246 |
state.history = [];
|
| 247 |
state.globeState = emptyGlobeState();
|
| 248 |
+
state.sessionAutoTitle = null;
|
| 249 |
resetForm();
|
| 250 |
els.chatInput.value = "";
|
| 251 |
renderMessages();
|
|
|
|
| 393 |
state.sessionId = session.id;
|
| 394 |
state.history = session.history || [];
|
| 395 |
state.globeState = session.globeState || emptyGlobeState();
|
| 396 |
+
state.sessionAutoTitle = session.titleManuallySet ? null : session.title || null;
|
| 397 |
els.chatInput.value = "";
|
| 398 |
renderMessages();
|
| 399 |
applyGlobeState(state.globeState);
|
|
|
|
| 409 |
state.sessionId = crypto.randomUUID();
|
| 410 |
state.history = [];
|
| 411 |
state.globeState = emptyGlobeState();
|
| 412 |
+
state.sessionAutoTitle = null;
|
| 413 |
resetForm();
|
| 414 |
els.chatInput.value = "";
|
| 415 |
renderMessages();
|
|
|
|
| 892 |
setStatus("Building research prompt...");
|
| 893 |
try {
|
| 894 |
const result = await gradioPredict("/build_research_prompt", formPayload());
|
| 895 |
+
const payload = unwrapResult(result) || {};
|
| 896 |
+
const message =
|
| 897 |
+
typeof payload === "string" ? payload : String(payload.text || "");
|
| 898 |
+
const title =
|
| 899 |
+
typeof payload === "object" && payload !== null && !Array.isArray(payload)
|
| 900 |
+
? String(payload.title || "")
|
| 901 |
+
: "";
|
| 902 |
+
state.sessionAutoTitle = title || null;
|
| 903 |
if (!message) {
|
| 904 |
setStatus("Could not build prompt.");
|
| 905 |
return;
|
ui/intake/prompts.py
CHANGED
|
@@ -107,5 +107,48 @@ def build_profile_prompt(
|
|
| 107 |
)
|
| 108 |
|
| 109 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 110 |
def example_prompt(prompt: str) -> dict[str, object]:
|
| 111 |
return _chatbox_value(prompt)
|
|
|
|
| 107 |
)
|
| 108 |
|
| 109 |
|
| 110 |
+
def _truncate_title(title: str, max_length: int = 72) -> str:
|
| 111 |
+
if len(title) <= max_length:
|
| 112 |
+
return title
|
| 113 |
+
return f"{title[: max_length - 3]}..."
|
| 114 |
+
|
| 115 |
+
|
| 116 |
+
def build_session_title(
|
| 117 |
+
current_country: DropdownValue,
|
| 118 |
+
residence_status: DropdownValue,
|
| 119 |
+
education: DropdownValue,
|
| 120 |
+
occupation: DropdownValue,
|
| 121 |
+
experience: DropdownValue,
|
| 122 |
+
budget: DropdownValue,
|
| 123 |
+
family: DropdownValue,
|
| 124 |
+
timeline: DropdownValue,
|
| 125 |
+
goals: str,
|
| 126 |
+
) -> str:
|
| 127 |
+
del residence_status, experience, budget, family # unused for title
|
| 128 |
+
|
| 129 |
+
parts = [
|
| 130 |
+
part
|
| 131 |
+
for part in (
|
| 132 |
+
_format_value(occupation),
|
| 133 |
+
_format_value(current_country),
|
| 134 |
+
_format_value(timeline),
|
| 135 |
+
)
|
| 136 |
+
if part
|
| 137 |
+
]
|
| 138 |
+
if not parts:
|
| 139 |
+
education_text = _format_value(education)
|
| 140 |
+
if education_text:
|
| 141 |
+
parts.append(education_text)
|
| 142 |
+
|
| 143 |
+
if parts:
|
| 144 |
+
return _truncate_title(" · ".join(parts))
|
| 145 |
+
|
| 146 |
+
goals_text = " ".join((goals or "").split())
|
| 147 |
+
if goals_text:
|
| 148 |
+
return _truncate_title(goals_text)
|
| 149 |
+
|
| 150 |
+
return "Untitled chat"
|
| 151 |
+
|
| 152 |
+
|
| 153 |
def example_prompt(prompt: str) -> dict[str, object]:
|
| 154 |
return _chatbox_value(prompt)
|
ui/server_api.py
CHANGED
|
@@ -23,7 +23,7 @@ from ui.intake.choices import (
|
|
| 23 |
country_choices,
|
| 24 |
)
|
| 25 |
from ui.intake.examples import demo_personas, persona_prompt
|
| 26 |
-
from ui.intake.prompts import build_profile_prompt
|
| 27 |
|
| 28 |
DropdownValue = str | int | float | list[str | int | float] | None
|
| 29 |
|
|
@@ -70,7 +70,7 @@ def build_research_prompt(
|
|
| 70 |
family: DropdownValue,
|
| 71 |
timeline: DropdownValue,
|
| 72 |
goals: str,
|
| 73 |
-
) -> str:
|
| 74 |
prompt = build_profile_prompt(
|
| 75 |
None,
|
| 76 |
current_country,
|
|
@@ -84,7 +84,20 @@ def build_research_prompt(
|
|
| 84 |
timeline,
|
| 85 |
goals,
|
| 86 |
)
|
| 87 |
-
return
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 88 |
|
| 89 |
|
| 90 |
def build_persona_prompt(persona_id: str) -> str:
|
|
|
|
| 23 |
country_choices,
|
| 24 |
)
|
| 25 |
from ui.intake.examples import demo_personas, persona_prompt
|
| 26 |
+
from ui.intake.prompts import build_profile_prompt, build_session_title
|
| 27 |
|
| 28 |
DropdownValue = str | int | float | list[str | int | float] | None
|
| 29 |
|
|
|
|
| 70 |
family: DropdownValue,
|
| 71 |
timeline: DropdownValue,
|
| 72 |
goals: str,
|
| 73 |
+
) -> dict[str, str]:
|
| 74 |
prompt = build_profile_prompt(
|
| 75 |
None,
|
| 76 |
current_country,
|
|
|
|
| 84 |
timeline,
|
| 85 |
goals,
|
| 86 |
)
|
| 87 |
+
return {
|
| 88 |
+
"text": str(prompt.get("text") or ""),
|
| 89 |
+
"title": build_session_title(
|
| 90 |
+
current_country,
|
| 91 |
+
residence_status,
|
| 92 |
+
education,
|
| 93 |
+
occupation,
|
| 94 |
+
experience,
|
| 95 |
+
budget,
|
| 96 |
+
family,
|
| 97 |
+
timeline,
|
| 98 |
+
goals,
|
| 99 |
+
),
|
| 100 |
+
}
|
| 101 |
|
| 102 |
|
| 103 |
def build_persona_prompt(persona_id: str) -> str:
|