Spaces:
Sleeping
Sleeping
Implement new chat interface for immigration research, including user profile input and example prompts. Introduce a dedicated prompts module for system instructions, enhancing clarity and functionality.
Browse files- ui/chat.py +173 -34
- ui/inference/prompts.py +69 -0
ui/chat.py
CHANGED
|
@@ -1,42 +1,171 @@
|
|
| 1 |
# ui/chat.py
|
|
|
|
|
|
|
| 2 |
import gradio as gr
|
| 3 |
|
| 4 |
from ui.globe import render_globe_panel
|
| 5 |
from ui.globe_commands import empty_globe_state
|
| 6 |
from ui.inference import respond
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 7 |
|
| 8 |
-
|
| 9 |
-
|
| 10 |
-
|
| 11 |
-
|
| 12 |
-
|
| 13 |
-
|
| 14 |
-
|
| 15 |
-
|
| 16 |
-
|
| 17 |
-
|
| 18 |
-
|
| 19 |
-
|
| 20 |
-
|
| 21 |
-
|
| 22 |
-
|
| 23 |
-
"show, highlight, or fly to countries, call update_globe immediately "
|
| 24 |
-
"instead of describing the tool call."
|
| 25 |
-
),
|
| 26 |
-
label="System message",
|
| 27 |
),
|
| 28 |
-
|
| 29 |
-
|
| 30 |
-
|
| 31 |
-
|
| 32 |
-
|
| 33 |
-
|
| 34 |
-
step=0.05,
|
| 35 |
-
label="Top-p (nucleus sampling)",
|
| 36 |
),
|
| 37 |
]
|
| 38 |
|
| 39 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 40 |
class SidebarChatInterface(gr.ChatInterface):
|
| 41 |
def __init__(
|
| 42 |
self,
|
|
@@ -67,18 +196,28 @@ def create_chat_interface(
|
|
| 67 |
For information on how to customize the ChatInterface, peruse the gradio docs: https://www.gradio.app/docs/chatinterface
|
| 68 |
"""
|
| 69 |
globe_state = gr.JSON(value=empty_globe_state(), visible=False, elem_id="globe-state")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 70 |
|
| 71 |
chat = SidebarChatInterface(
|
| 72 |
respond,
|
| 73 |
multimodal=True,
|
| 74 |
-
textbox=
|
| 75 |
-
show_label=False,
|
| 76 |
-
placeholder="Enter message or upload file...",
|
| 77 |
-
file_count="multiple",
|
| 78 |
-
sources=["upload"],
|
| 79 |
-
),
|
| 80 |
history_container=history_container,
|
| 81 |
-
additional_inputs=
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 82 |
additional_outputs=[globe_state],
|
| 83 |
)
|
| 84 |
|
|
|
|
| 1 |
# ui/chat.py
|
| 2 |
+
from __future__ import annotations
|
| 3 |
+
|
| 4 |
import gradio as gr
|
| 5 |
|
| 6 |
from ui.globe import render_globe_panel
|
| 7 |
from ui.globe_commands import empty_globe_state
|
| 8 |
from ui.inference import respond
|
| 9 |
+
from ui.inference.prompts import BORDERLESS_SYSTEM_PROMPT
|
| 10 |
+
|
| 11 |
+
DEFAULT_MAX_TOKENS = 1800
|
| 12 |
+
DEFAULT_TEMPERATURE = 0.35
|
| 13 |
+
DEFAULT_TOP_P = 0.9
|
| 14 |
|
| 15 |
+
EXAMPLE_PROMPTS = [
|
| 16 |
+
(
|
| 17 |
+
"India software engineer",
|
| 18 |
+
"I'm a software engineer from India with 5 years of backend experience, "
|
| 19 |
+
"a master's degree, English fluency, and a budget of about USD 18,000. "
|
| 20 |
+
"I want to relocate for work within the next 12-18 months. Which "
|
| 21 |
+
"countries are realistic, what visa pathways should I look at, and "
|
| 22 |
+
"what documents should I prepare?",
|
| 23 |
+
),
|
| 24 |
+
(
|
| 25 |
+
"Hong Kong student",
|
| 26 |
+
"I hold a Hong Kong passport and want to study in Europe on a modest "
|
| 27 |
+
"budget. I speak English and conversational Mandarin, can show around "
|
| 28 |
+
"USD 12,000 in savings, and care about post-study work options. Compare "
|
| 29 |
+
"realistic countries and mark them on the globe.",
|
|
|
|
|
|
|
|
|
|
|
|
|
| 30 |
),
|
| 31 |
+
(
|
| 32 |
+
"Remote worker family",
|
| 33 |
+
"I'm a freelance product designer from Brazil, married with one child, "
|
| 34 |
+
"earning around USD 4,500 per month from overseas clients. We want a "
|
| 35 |
+
"safe country with good schools, a route that does not require a local "
|
| 36 |
+
"job offer immediately, and a timeline under one year.",
|
|
|
|
|
|
|
| 37 |
),
|
| 38 |
]
|
| 39 |
|
| 40 |
|
| 41 |
+
def _chatbox_value(text: str) -> dict[str, object]:
|
| 42 |
+
return {"text": text.strip(), "files": []}
|
| 43 |
+
|
| 44 |
+
|
| 45 |
+
def example_prompt(prompt: str) -> dict[str, object]:
|
| 46 |
+
return _chatbox_value(prompt)
|
| 47 |
+
|
| 48 |
+
|
| 49 |
+
def build_profile_prompt(
|
| 50 |
+
citizenship: str,
|
| 51 |
+
current_location: str,
|
| 52 |
+
education: str,
|
| 53 |
+
work_history: str,
|
| 54 |
+
languages: str,
|
| 55 |
+
budget: str,
|
| 56 |
+
family: str,
|
| 57 |
+
timeline: str,
|
| 58 |
+
goals: str,
|
| 59 |
+
) -> dict[str, object]:
|
| 60 |
+
sections = [
|
| 61 |
+
("Citizenship/passports", citizenship),
|
| 62 |
+
("Current location", current_location),
|
| 63 |
+
("Education", education),
|
| 64 |
+
("Work history", work_history),
|
| 65 |
+
("Languages", languages),
|
| 66 |
+
("Budget and savings", budget),
|
| 67 |
+
("Family/dependents", family),
|
| 68 |
+
("Target timeline", timeline),
|
| 69 |
+
("Goals and preferences", goals),
|
| 70 |
+
]
|
| 71 |
+
details = "\n".join(
|
| 72 |
+
f"- {label}: {value.strip()}"
|
| 73 |
+
for label, value in sections
|
| 74 |
+
if value and value.strip()
|
| 75 |
+
)
|
| 76 |
+
if not details:
|
| 77 |
+
details = "- I have not filled in the profile yet. Ask me only for the missing details that matter most."
|
| 78 |
+
|
| 79 |
+
return _chatbox_value(
|
| 80 |
+
"Research realistic migration options for my profile.\n\n"
|
| 81 |
+
f"{details}\n\n"
|
| 82 |
+
"Please recommend 3-5 suitable countries, mark them on the globe, "
|
| 83 |
+
"compare visa pathways, required documents, approximate costs, "
|
| 84 |
+
"realistic timelines, risks, and official sources to verify."
|
| 85 |
+
)
|
| 86 |
+
|
| 87 |
+
|
| 88 |
+
def render_intake_panel(chat_input: gr.MultimodalTextbox) -> None:
|
| 89 |
+
with gr.Column(elem_classes=["borderless-intake"]):
|
| 90 |
+
gr.Markdown(
|
| 91 |
+
"## Borderless\n"
|
| 92 |
+
"Describe your background once. The agent researches official sources, "
|
| 93 |
+
"compares realistic visa pathways, and marks suitable countries on the globe.",
|
| 94 |
+
elem_classes=["borderless-hero"],
|
| 95 |
+
)
|
| 96 |
+
with gr.Accordion("Start with a guided profile", open=True):
|
| 97 |
+
with gr.Row():
|
| 98 |
+
citizenship = gr.Textbox(
|
| 99 |
+
label="Citizenship / passports",
|
| 100 |
+
placeholder="e.g. India; no second passport",
|
| 101 |
+
)
|
| 102 |
+
current_location = gr.Textbox(
|
| 103 |
+
label="Current location",
|
| 104 |
+
placeholder="e.g. Singapore on an employment pass",
|
| 105 |
+
)
|
| 106 |
+
with gr.Row():
|
| 107 |
+
education = gr.Textbox(
|
| 108 |
+
label="Education",
|
| 109 |
+
placeholder="e.g. Master's in computer science",
|
| 110 |
+
)
|
| 111 |
+
work_history = gr.Textbox(
|
| 112 |
+
label="Work history",
|
| 113 |
+
placeholder="e.g. 5 years as backend software engineer",
|
| 114 |
+
)
|
| 115 |
+
with gr.Row():
|
| 116 |
+
languages = gr.Textbox(
|
| 117 |
+
label="Languages",
|
| 118 |
+
placeholder="e.g. English C1, German A2",
|
| 119 |
+
)
|
| 120 |
+
budget = gr.Textbox(
|
| 121 |
+
label="Budget",
|
| 122 |
+
placeholder="e.g. USD 18,000 available for relocation",
|
| 123 |
+
)
|
| 124 |
+
with gr.Row():
|
| 125 |
+
family = gr.Textbox(
|
| 126 |
+
label="Family / dependents",
|
| 127 |
+
placeholder="e.g. spouse, one child, or solo applicant",
|
| 128 |
+
)
|
| 129 |
+
timeline = gr.Textbox(
|
| 130 |
+
label="Target timeline",
|
| 131 |
+
placeholder="e.g. 12-18 months",
|
| 132 |
+
)
|
| 133 |
+
goals = gr.Textbox(
|
| 134 |
+
label="Goals in everyday language",
|
| 135 |
+
lines=3,
|
| 136 |
+
placeholder=(
|
| 137 |
+
"e.g. I want a realistic skilled-worker route, lower risk, "
|
| 138 |
+
"and a path to permanent residence."
|
| 139 |
+
),
|
| 140 |
+
)
|
| 141 |
+
gr.Button("Create research prompt", variant="primary").click(
|
| 142 |
+
fn=build_profile_prompt,
|
| 143 |
+
inputs=[
|
| 144 |
+
citizenship,
|
| 145 |
+
current_location,
|
| 146 |
+
education,
|
| 147 |
+
work_history,
|
| 148 |
+
languages,
|
| 149 |
+
budget,
|
| 150 |
+
family,
|
| 151 |
+
timeline,
|
| 152 |
+
goals,
|
| 153 |
+
],
|
| 154 |
+
outputs=chat_input,
|
| 155 |
+
queue=False,
|
| 156 |
+
)
|
| 157 |
+
|
| 158 |
+
gr.Markdown("Try a demo persona:", elem_classes=["borderless-examples-title"])
|
| 159 |
+
with gr.Row():
|
| 160 |
+
for label, prompt in EXAMPLE_PROMPTS:
|
| 161 |
+
gr.Button(label, size="sm").click(
|
| 162 |
+
fn=example_prompt,
|
| 163 |
+
inputs=[gr.State(prompt)],
|
| 164 |
+
outputs=chat_input,
|
| 165 |
+
queue=False,
|
| 166 |
+
)
|
| 167 |
+
|
| 168 |
+
|
| 169 |
class SidebarChatInterface(gr.ChatInterface):
|
| 170 |
def __init__(
|
| 171 |
self,
|
|
|
|
| 196 |
For information on how to customize the ChatInterface, peruse the gradio docs: https://www.gradio.app/docs/chatinterface
|
| 197 |
"""
|
| 198 |
globe_state = gr.JSON(value=empty_globe_state(), visible=False, elem_id="globe-state")
|
| 199 |
+
chat_input = gr.MultimodalTextbox(
|
| 200 |
+
show_label=False,
|
| 201 |
+
placeholder="Ask a follow-up, or create a prompt from the guided profile above...",
|
| 202 |
+
file_count="multiple",
|
| 203 |
+
sources=["upload"],
|
| 204 |
+
render=False,
|
| 205 |
+
)
|
| 206 |
+
|
| 207 |
+
render_intake_panel(chat_input)
|
| 208 |
|
| 209 |
chat = SidebarChatInterface(
|
| 210 |
respond,
|
| 211 |
multimodal=True,
|
| 212 |
+
textbox=chat_input,
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 213 |
history_container=history_container,
|
| 214 |
+
additional_inputs=[
|
| 215 |
+
gr.State(BORDERLESS_SYSTEM_PROMPT),
|
| 216 |
+
gr.State(DEFAULT_MAX_TOKENS),
|
| 217 |
+
gr.State(DEFAULT_TEMPERATURE),
|
| 218 |
+
gr.State(DEFAULT_TOP_P),
|
| 219 |
+
globe_state,
|
| 220 |
+
],
|
| 221 |
additional_outputs=[globe_state],
|
| 222 |
)
|
| 223 |
|
ui/inference/prompts.py
ADDED
|
@@ -0,0 +1,69 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""System prompts for Borderless immigration research sessions."""
|
| 2 |
+
|
| 3 |
+
from __future__ import annotations
|
| 4 |
+
|
| 5 |
+
BORDERLESS_SYSTEM_PROMPT = """
|
| 6 |
+
You are Borderless, an agentic immigration research assistant with access to web
|
| 7 |
+
search, page scraping, country metadata, and an interactive 3D globe.
|
| 8 |
+
|
| 9 |
+
Your job is to help the user research realistic migration options. You are not
|
| 10 |
+
a lawyer and must not present the answer as legal advice. Be practical,
|
| 11 |
+
specific, and clear about uncertainty.
|
| 12 |
+
|
| 13 |
+
Research protocol:
|
| 14 |
+
1. Parse the user's profile: citizenship, current location, education, work
|
| 15 |
+
history, languages, budget, timeline, family constraints, and goals.
|
| 16 |
+
2. Identify 3-5 plausible destination countries. Prefer realistic fit over
|
| 17 |
+
popular destinations.
|
| 18 |
+
3. Use get_country_profile for countries you plan to compare or mark on the
|
| 19 |
+
globe.
|
| 20 |
+
4. Use search_immigration_info to discover current visa rules, documents, fees,
|
| 21 |
+
processing times, and official government pages. Prefer official immigration
|
| 22 |
+
authority, government, and embassy sources. Use include_domains when you know
|
| 23 |
+
the correct official domains.
|
| 24 |
+
5. Use scrape_web_page on the best official URLs before making concrete claims
|
| 25 |
+
about eligibility, required documents, fees, processing times, or application
|
| 26 |
+
steps. Use crawl_web_site only when several pages from the same official site
|
| 27 |
+
are necessary.
|
| 28 |
+
6. If you recommend or compare destination countries, call update_globe with
|
| 29 |
+
action "show". Pass ISO-2 country codes and concise marker labels such as
|
| 30 |
+
"Skilled worker - 6-12 mo" or "Study route - lower budget".
|
| 31 |
+
7. If a tool fails, continue with a partial answer, explain what could not be
|
| 32 |
+
verified, and tell the user which official source to check next.
|
| 33 |
+
|
| 34 |
+
Source quality rules:
|
| 35 |
+
- Cite official source URLs for facts that affect eligibility, documents, fees,
|
| 36 |
+
timelines, or application steps.
|
| 37 |
+
- Treat law firm blogs, forums, and relocation sites as context only unless no
|
| 38 |
+
official source is available; label them as unofficial.
|
| 39 |
+
- Do not invent point scores, income thresholds, fees, or processing times.
|
| 40 |
+
If current official data is not found, say what is missing.
|
| 41 |
+
|
| 42 |
+
Final answer format:
|
| 43 |
+
Start with a short, plain-English recommendation. Then use these sections:
|
| 44 |
+
|
| 45 |
+
## Snapshot
|
| 46 |
+
Summarize the user's profile and key constraints in 2-4 bullets.
|
| 47 |
+
|
| 48 |
+
## Best-Fit Countries
|
| 49 |
+
Use a compact table with country, recommended pathway, why it fits, main risk,
|
| 50 |
+
and rough timeline.
|
| 51 |
+
|
| 52 |
+
## Pathway Details
|
| 53 |
+
For each recommended country, list the visa pathway, eligibility notes,
|
| 54 |
+
required documents, approximate steps, timeline, and budget sensitivity.
|
| 55 |
+
|
| 56 |
+
## Documents To Prepare
|
| 57 |
+
Group common documents first, then country-specific documents.
|
| 58 |
+
|
| 59 |
+
## Risks And Tradeoffs
|
| 60 |
+
Mention language, job market, funds, age/points, credential recognition,
|
| 61 |
+
family constraints, and policy uncertainty where relevant.
|
| 62 |
+
|
| 63 |
+
## Official Sources
|
| 64 |
+
List cited official URLs and what each source supports.
|
| 65 |
+
|
| 66 |
+
## Next Steps
|
| 67 |
+
Give 3-5 concrete actions the user can take next. End by reminding them to
|
| 68 |
+
verify details on official sites or with a qualified immigration professional.
|
| 69 |
+
""".strip()
|