File size: 9,045 Bytes
ddf0d56
64c5d91
85aa2e5
ddf0d56
 
 
4971d0b
ddf0d56
 
ae8acda
71a7158
c14033c
ae8acda
 
 
 
 
 
 
71a7158
fe52b47
ae8acda
 
 
71a7158
ae8acda
ddf0d56
 
ae8acda
 
 
 
ddf0d56
 
85aa2e5
 
 
 
 
 
 
 
 
 
 
 
fe52b47
 
 
 
 
85aa2e5
 
 
 
 
71a7158
fe52b47
 
 
 
 
 
 
 
 
 
 
 
 
 
85aa2e5
 
 
fe52b47
 
cec3b5e
 
c1e4744
85aa2e5
71a7158
85aa2e5
 
 
fe52b47
 
 
 
 
14b75bf
fe52b47
85aa2e5
 
 
fe52b47
85aa2e5
fe52b47
85aa2e5
 
 
fe52b47
 
71a7158
85aa2e5
cec3b5e
fe52b47
85aa2e5
 
 
 
fe52b47
 
 
 
 
 
 
85aa2e5
fe52b47
 
 
 
 
 
 
 
5d1cbeb
fe52b47
 
85aa2e5
fe52b47
 
 
 
 
 
 
85aa2e5
 
 
 
 
 
71a7158
85aa2e5
fe52b47
 
14b75bf
85aa2e5
 
 
 
 
 
f440f8f
cec3b5e
85aa2e5
 
 
71a7158
85aa2e5
cec3b5e
 
fe52b47
 
71a7158
cec3b5e
 
71a7158
 
fe52b47
 
14b75bf
cec3b5e
fe52b47
f440f8f
55f84e8
 
 
 
 
 
 
 
85aa2e5
 
71a7158
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
---
title: Borderless
emoji: 🌍
colorFrom: yellow
colorTo: purple
sdk: gradio
sdk_version: 6.16.0
app_file: app.py
pinned: false
license: apache-2.0
hardware: cpu
short_description: Agentic immigration research for global movers
tags:
  - agents
  - gradio
  - immigration
  - travel
  - research
  - tool-use
  - qwen
  - langgraph
  - maplibre
  - geospatial
models:
  - Qwen/Qwen3.6-27B
datasets: []
hf_oauth: true
hf_oauth_scopes:
  - inference-api 
hf_oauth_expiration_minutes: 480 # 8 hours
disable_embedding: false
startup_duration_timeout: 10m 
---

# Borderless

**An agentic immigration research tool β€” describe your background in plain English, explore where you could go.**

Live demo: **[build-small-hackathon/borderless](https://huggingface.co/spaces/build-small-hackathon/borderless)**

Built for the [Build Small Hackathon](https://huggingface.co/build-small-hackathon) β€” small models (≀32B), big adventure.

## What it does

Immigration research is fragmented across government sites, forums, and spreadsheets. Borderless puts it in one conversational flow:

1. **Describe yourself** β€” citizenship, education, work history, budget, timeline, and goals via a guided intake form or free-form chat.
2. **Get a research plan** β€” the agent parses your profile, picks realistic destination countries, and breaks the work into focused to-dos.
3. **Watch parallel research** β€” subagents investigate each to-do at the same time, searching official sources and scraping government pages.
4. **Explore on a 3D globe** β€” recommended countries appear on an interactive MapLibre globe with pathway labels.
5. **Read a consolidated answer** β€” visa pathways, documents, timelines, costs, risks, and cited official sources in one structured report.

No forms to decode. No keyword guessing. Just a research session that meets you where you are.

## How it works

Borderless uses a **[LangGraph](https://docs.langchain.com/oss/python/langgraph/overview)** workflow powered by **[Qwen/Qwen3.6-27B](https://huggingface.co/Qwen/Qwen3.6-27B)** via the Hugging Face Inference API:

```
User profile β†’ Planner β†’ parallel Researchers (one per to-do) β†’ Consolidator β†’ final answer
```

| Stage | What happens |
|-------|----------------|
| **Planner** | Reads the profile, selects 3–4 destination countries, emits a research plan, and marks countries on the globe. |
| **Researchers** | Run in parallel via LangGraph `Send`. Each subagent searches official immigration sources, scrapes government pages, and writes a findings report for its to-do. |
| **Consolidator** | Merges all findings into a single structured answer with pathways, documents, risks, timelines, and official source links. |

Progress streams live in the chat: thinking steps, the research plan, tool calls (with expandable arguments and results), per-to-do findings, globe updates, and the final answer.

### Research tools

| Tool | What it fetches |
|------|-----------------|
| `get_country_profile` | Country metadata and official immigration domain hints |
| `search_immigration_info` | Web search with source-quality labels for official immigration pages (Exa) |
| `scrape_web_page` | Markdown content from a specific official government or embassy URL (Firecrawl) |
| `crawl_web_site` | Multiple pages from an official immigration website section (Firecrawl) |
| `update_globe` | Marks, highlights, and flies to countries on the MapLibre globe |

Sign in with your Hugging Face account for inference, or set `HF_TOKEN` as a Space secret for server-side API access.

## Features

- **Guided intake** β€” form fields and demo personas turn a profile into a complete research prompt
- **LangGraph parallel research** β€” planner plus parallel subagents, not a single monolithic agent loop
- **Live streaming UI** β€” thinking, tool calls, findings, and the final answer appear as the workflow runs
- **Tool-driven 3D globe** β€” MapLibre GL globe with markers, pathway labels, and fly-to camera moves
- **Source quality** β€” search results flag likely official government and embassy sources
- **Structured recommendations** β€” shortlist, pathways, documents, risks, timelines, next steps, and official sources
- **Chat history** β€” sessions persist in the browser

## Example prompts

- *"I'm a software engineer from India with 8 years of experience and a master's degree. Where could I realistically relocate for skilled work within 12–18 months?"*
- *"I hold a Hong Kong passport and want to study in Europe on a modest budget. What are my visa options?"*
- *"Compare skilled worker pathways to Canada, Germany, and Ireland for a solo applicant with USD 20k savings."*

## Tech stack

- **[Gradio Server](https://gradio.app)** β€” custom HTML/JS UI, OAuth, and streaming API endpoints
- **[LangGraph](https://docs.langchain.com/oss/python/langgraph/overview)** β€” planner / parallel researcher / consolidator workflow
- **[Qwen3.6-27B](https://huggingface.co/Qwen/Qwen3.6-27B)** β€” planning and synthesis via Hugging Face Inference API (`huggingface_hub.InferenceClient`)
- **[MapLibre GL JS](https://maplibre.org/)** β€” interactive 3D globe
- **[Exa](https://exa.ai)** β€” neural web search for discovering immigration sources
- **[Firecrawl](https://firecrawl.dev)** β€” scrape and crawl official web pages

## Project structure

```
app.py                  # Gradio Server entry point and API routes
assets/
  index.html            # Custom UI shell
  app.js                # Intake form, chat, history, streaming client
  gradio_api.js         # Gradio SSE streaming helper
  globe.js / globe.css  # MapLibre globe rendering
  server.css            # App styling
ui/
  server_api.py         # Chat API adapter and intake helpers
  globe_commands.py     # Globe marker/highlight/fly-to state
  country_coords.py     # Country coordinate lookup
  intake/               # Profile form, personas, prompt builders
  agent/
    graph/              # LangGraph workflow (planner, researchers, consolidator)
    respond.py          # Legacy single-agent loop (fallback mode)
    tools.py            # Tool dispatch
    completion.py       # Hugging Face Inference API client
    streaming.py        # Chunked answer streaming
    tool_schemas/       # Function-calling schemas
apis/
  exa.py                # Exa web search
  firecrawl.py          # Firecrawl scrape/crawl
  rest_countries.py     # Country metadata (with static fallback)
  country_profile.py    # Country profile tool
data/
  countries_fallback.yaml   # Static country coordinates when REST Countries is unavailable
  intake/                   # Form choices and demo personas
```

## Hackathon fit

| Constraint | Borderless |
|------------|------------|
| Model ≀ 32B | Qwen3.6-27B (27B) |
| Gradio on HF Spaces | Yes β€” [live Space](https://huggingface.co/spaces/build-small-hackathon/borderless) |
| Agentic | LangGraph multi-agent research with visible tool traces |
| Sharing is Caring | JSONL tool traces can be sanitized and published β€” see `TRACE_SHARING.md` |
| Field Notes | See `FIELD_NOTES.md` |

**Track:** Backyard AI β€” immigration research is a real, specific problem faced by millions of people weighing where they can live, work, and study.

## Run locally

```bash
pip install -r requirements.txt
cp .env.example .env   # then fill in API keys
python app.py
```

Set `HF_TOKEN` in `.env` (or sign in through OAuth on the Space). The default model is `Qwen/Qwen3.6-27B:featherless-ai` on the Hugging Face Inference API.

For web research tools, set API keys from [dashboard.exa.ai](https://dashboard.exa.ai/api-keys) and [firecrawl.dev](https://firecrawl.dev):

| Variable | Purpose |
|----------|---------|
| `HF_TOKEN` | Hugging Face Inference API access (Space secret and/or user OAuth) |
| `EXA_API_KEY` | `search_immigration_info` |
| `FIRECRAWL_API_KEY` | `scrape_web_page`, `crawl_web_site` |
| `BORDERLESS_MODEL_ID` | Model override (default `Qwen/Qwen3.6-27B:featherless-ai`) |
| `BORDERLESS_INFERENCE_MODE` | `hub` (default, InferenceClient) or `router` (OpenAI-compatible router) or `local` (MiniCPM on ZeroGPU) |
| `BORDERLESS_AGENT_MODE` | `graph` (default) or `legacy` for the single-agent loop |
| `BORDERLESS_TRACE_DIR` | JSONL trace output directory |
| `BORDERLESS_DISABLE_TRACE_LOGS` | Set to `1` to disable local trace logs |

On Hugging Face Spaces, add API keys as **Space secrets** (Settings β†’ Secrets). Without keys, web tools return a clear error and the agent continues with partial results.

### Startup logs (normal on Spaces)

| Log | Meaning |
|-----|---------|
| `GRADIO_HOT_RELOAD: ... Using 'demo'` | Harmless β€” Gradio found the app entrypoint. |
| `Invalid file descriptor: -1` in `BaseEventLoop.__del__` | Harmless asyncio cleanup noise during Gradio SSR hot reload. |
| `HF_TOKEN is set ... independently from the token you've just configured` | Informational β€” Space secret and OAuth login both provide tokens. |

## License

Apache-2.0 (model: [Qwen/Qwen3.6-27B](https://huggingface.co/Qwen/Qwen3.6-27B))