Spaces:
Sleeping
Sleeping
Commit History
-
List HF username @helmo (solo team)
Verified signaturec668df0 -
Finish Refuge->Fugee rebrand (DESIGN, mockup, scripts, SKILL)
Verified signature237e510 -
Rebrand Refuge->Fugee in docs; Codex model GPT-5.5
Verified signaturea6b364a
-
Add hackathon submission tags
Verified signature559dfe8 -
Add GitHub link
Verified signaturecf69183 -
Name the model (LFM2.5 8B) in the README
Verified signature148840d -
Add demo + launch-post links
Verified signature62c8c7e -
Add UNHCR source guideline PDFs (RAG provenance)
Verified signature2eb2916 -
Add UNHCR guidelines RAG index
Verified signature6947ac5 -
Remove .gitignore on the Space so data assets can be uploaded
Verified signature8df630e -
Add RAG index + source UNHCR PDFs (provenance)
Verified signaturea8b2504 -
Add UNHCR source guideline PDFs (RAG provenance)
Verified signature5543abe -
Add UNHCR guidelines RAG index (Xet)
Verified signature6c3e211 -
Upload folder using huggingface_hub
Verified signaturee1c4ad0 -
Upload specs/data/guidelines_index.json with huggingface_hub
Verified signature5dead9e -
Upload folder using huggingface_hub
Verified signature82364c6 -
Upload specs/data/guidelines_index.json with huggingface_hub
Verified signature0352690 -
[deploy] Shorten Space short_description to <=60 chars (HF metadata limit)
Hélder Monteiro +1: CodexUnverified signatureCo-authored-by: Codex <noreply@openai.com>cb440b2 -
[deploy] Don't commit PDFs to git; upload binaries to the Space via hf upload
Hélder Monteiro +1: CodexUnverified signatureThe HF Hub rejects binary files in plain git (Xet/LFS required), so the guideline PDFs can't ride with the code push. Re-ignore them and upload both the RAG index and the source PDFs to the Space with 'hf upload' (auto-Xet) — PDFs still appear on the Space for provenance, just not in git history. Co-authored-by: Codex <noreply@openai.com>c70e81f -
[data] Commit countries_enriched.json so the app/Space is ready without the pipeline
Hélder Monteiro +1: CodexUnverified signatureThe enriched stats overlay (227 KB) was gitignored as a regenerable artifact, but committing it means prod (and the HF Space) has full country stats immediately, with no need to run enrich_downloader.py. The 23 MB RAG index stays out of git and is uploaded to the Space via 'hf upload' (LFS). country_lookup still falls back to the curated countries.json if either is absent. Co-authored-by: Codex <noreply@openai.com>4bf5ceb -
[deploy] HF Space packaging: README frontmatter, packages.txt, DEPLOY.md
Hélder Monteiro +1: CodexUnverified signatureMake the repo a valid Gradio Space and document the two-piece deploy: - README.md: HF Space YAML frontmatter (sdk: gradio, app_file: app/app.py) + a 'Live demo: HF Space + Modal' section and NUM_CTX/MODAL_* in the config table. - packages.txt: WeasyPrint's system libs (Pango/HarfBuzz) + Noto fonts so PDF generation works on the Space's base image (and non-Latin docs render). - deploy/DEPLOY.md: copy-pasteable steps — HF login, create Space in build-small-hackathon, push code, upload the gitignored RAG index via hf upload (auto-LFS), set Space secrets, verify, submit, and stop the Modal meter after. Co-authored-by: Codex <noreply@openai.com>3010c3f -
[docs] Neutral comments: drop self-promotion framing; describe behaviour, not the model
Hélder Monteiro +1: CodexComment-only cleanup. The intake attribution comments no longer read like the app's purpose is marketing the author (removed 'carries the other follow links…' etc.) — they now just say what the element is. The _is_weak_reasoning docstring describes the unusable-response condition rather than labelling 'the model'. No behaviour change. Co-authored-by: Codex <noreply@openai.com>407af0d -
[ux] Simplify intake credit to 'Made by @helmo for the … Hackathon'
Hélder Monteiro +1: CodexDrop the built-by name + LinkedIn/HF icons; the HF profile (@helmo) already carries the follow links and the Space lists the author. One quiet line on the first screen only, with @helmo linking to the HF profile. Co-authored-by: Codex <noreply@openai.com>644e47e -
[ux] Move credit to the intake screen only; LinkedIn/HF as logo-icons
Hélder Monteiro +1: CodexPer preference: show the author credit only on the first (intake) screen, not across the flow. LinkedIn and Hugging Face are now logo-icons (monochrome, inherit currentColor to stay on-brand) rather than text links, and the 'Made for the Hugging Face Build Small Hackathon' line sits on its own row below: · Built by Hélder Monteiro · [in] · 🤗 · Made for the Hugging Face Build Small Hackathon Lives inside the intake column so it hides with the screen; removed the previous global footer from app.py. Co-authored-by: Codex <noreply@openai.com>ebc2550 -
[ux] Add discreet credit footer (LinkedIn, Hugging Face, hackathon tag)
Hélder Monteiro +1: CodexA small bottom-of-page credit so visitors/judges can find and follow the author, without intruding on the calm, private flow shown to a vulnerable person — kept out of the site bar and the interview/assessment screens. Links open in a new tab; styled from DESIGN.md tokens (muted text, teal links). Rendered as a <div> since GLOBAL_CSS hides Gradio's own <footer>. Co-authored-by: Codex <noreply@openai.com>7005424 -
[ux] Hide stray 'processing' overlay on recommendation card actions
Hélder Monteiro +1: CodexSelecting a country (and revealing the cards from the assessment) only re-renders HTML — pure Python, instant — but Gradio's generic per-component progress overlay still painted a 'processing…' spinner over the card being updated. Set show_progress='hidden' on the card-select clicks and the assessment->recommendations reveal, matching the interview fix. Co-authored-by: Codex <noreply@openai.com>c2877c3 -
[assess] Agentic guard against junk/jailbreak input; tidy economic UX
Hélder Monteiro +1: CodexPer feedback that a smart agent should refuse to assess garbage rather than fabricate a result — and that this must be MODEL REASONING, not hardcoded prose matching: - Prompt: a precedence GATE checked before any category. If the answers are nonsensical / test strings / code / contradictory / devoid of real info, the agent sets case_type=unclear, leaves grounds+countries empty, and asks for a genuine account. 'No protection ground' now requires a genuine account — junk is unclear, never economic (the bucket the model was wrongly falling through to). - assessment.py: an unclear case yields NO recommendations (don't invent destinations from junk); _synth_reasoning returns a clean 'I couldn't determine your situation' message instead of a protection narrative. - recommendations.py: a 'needs more info' panel (no cards, no roadmap, proceed hidden) when there are no recs; the economic intro is trimmed to one line since the full honest explanation already lives in the assessm…9c6656b -
[fix] Give Ollama a real context window (root cause); deterministic weak-check
Hélder Monteiro +1: CodexRoot cause of the 'assessment acts like a chatbot / refuses' behavior: Ollama loaded the model with its default ~4096-token context, which silently TRUNCATED the assessment prompt (system + facts + injected guideline/country tool results) and dropped the instructions — so the model drifted to a greeting. Fix the cause: - agent/loop.py passes options.num_ctx (env NUM_CTX, default 16384) on every chat. - deploy/modal_app.py loads the model with OLLAMA_CONTEXT_LENGTH=NUM_CTX so the server matches the client (no reload on first request). With 16k context lfm2.5:8b now produces a correct, structured assessment on its own (verified over Modal: refugee · sexual-orientation PSG · high · 3 cards). Per feedback, replace the brittle prose phrase-matching weak-detector with a DETERMINISTIC signal: the answer is weak only when the model produced no structured output (case_type/grounds/countries) or almost no text — no guessing at non-deterministic wording. The interview-grounded fallback then r…cd3c555 -
[fix] Treat chatbot-greeting assessments as weak (use grounded fallback)
Hélder Monteiro +1: Codexlfm2.5:8b sometimes answered the assessment with a conversational greeting that asked the person for info it already had ('Could you tell me your preferred language?… please share … when you're ready'). That isn't a refusal, so it slipped past _is_weak_reasoning and leaked to the screen. Add greeting / request-for-info markers so these are treated as weak and replaced by the deterministic interview-grounded analysis + recommendations. Co-authored-by: Codex <noreply@openai.com>e88a54f -
[fix] Robust assessment + 2-3 recs + documents progress
Hélder Monteiro +1: CodexThree issues found in the live test (small-model quality + UX), not deployment: 1. Assessment could show a refusal ('I cannot determine a case type…'). The rich deterministic fallback only triggered when the narration was <120 chars, so a long non-answer leaked. Add _is_weak_reasoning() (refusal markers + length); when weak, take the interview-derived case_type/grounds/risk and the structured _synth_reasoning instead of the model's non-answer. 2. Recommendations always showed ONE country (we only topped up when the list was empty). Now top up protection cases to 2-3 using the person's own stated destination_preferences (looked up, signatory-only), then strong curated systems; cap at 3. Algeria case now -> Sweden/France/Italy. 3. 'Prepare my documents' looked frozen during the LLM draft. show_documents is now an async generator: reveal the docs screen with a 'Preparing your document package…' status immediately, then render the finished Word+PDF package. C…6614702 -
[deploy] Keep Modal endpoint warm + preload model for a fast demo
Hélder Monteiro +1: CodexOLLAMA_KEEP_ALIVE=-1 so the model never unloads while the container is warm; serve() preloads the LLM into GPU at startup so the first user request is ~1s instead of a 40s cold load. Deploy warm with MODAL_MIN_CONTAINERS=1 (L4 ~$0.80/hr, ~$134/week — well within the $250 credits); set it back to 0 (or 'modal app stop fugee-ollama') after the demo to stop the meter. .env.modal gitignored. Co-authored-by: Codex <noreply@openai.com>b7ec6aa -
[deploy] Modal-hosted Ollama backend + remote auth headers
Hélder Monteiro +1: CodexAdds deploy/modal_app.py: runs 'ollama serve' (lfm2.5:8b + nomic-embed-text) on a Modal GPU, cached in a Volume, exposed as an HTTPS endpoint protected by Modal proxy auth. Lets a free CPU Space drive the same models over the network. agent/ollama_auth.py supplies auth headers for a remote Ollama endpoint (Modal-Key/Modal-Secret, or Authorization: Bearer) from env; the LLM loop and the guideline-RAG embed call now pass them. No env set = empty headers = local dev unchanged (verified; 77 tests pass). Modal CLI 1.5.0 API verified against the file. Co-authored-by: Codex <noreply@openai.com>accb404
-
[assess] Remove web search entirely (prompt, code, module, test, env)
Hélder Monteiro +1: CodexThe model was narrating web searches and 'current information' it could not actually do, because the assessment PROMPT still told it to use web_search even though the tool was unwired (not in ASSESSMENT_TOOLS). That both misled the user ('feels like web search is enabled') and risked fabricated 'current' claims. - Prompt: drop all web_search references; state plainly there is no web access and to never claim current/latest info it cannot retrieve. - assessment.py: remove the dead web_search status chip and the now-moot outbound-query privacy guard; update the docstring. - Delete the dormant agent/tools/web_search.py module (imported nowhere active). - Tests: drop test_web_search_real and the Tavily gating (assessment uses only local tools now). - .env.example: remove the Tavily key + stale per-phase model vars; note there is no web search. Co-authored-by: Codex <noreply@openai.com>79d3fac -
[fix] Economic recommendations follow the profile, not a hardcoded Gulf list
Hélder Monteiro +1: CodexThe cards contradicted the assessment: the reasoning sensibly suggested Germany/Norway/Canada for a skilled EU resident, but the cards always showed UAE/Qatar/Saudi because economic cases were hardcoded to work_route_countries(). Economic destinations are WORK routes, not asylum, so the model's profile-aware picks are appropriate (and a skilled person needs Western/EU routes, a low-skilled worker needs Gulf/regional). Now use the model's recommended countries (any country), fall back to its looked-up ones, then the curated Gulf shortlist only as a last resort. Cards/roadmap render gracefully for non-Gulf destinations: a 'Regular / skilled-worker visa' label, region, a generic 'check official immigration site' caveat, and the kafala note only where kafala applies. Co-authored-by: Codex <noreply@openai.com>2349f80 -
[fix] Hide doubled 'processing' spinner; never leak @@END / metadata
Hélder Monteiro +1: Codex1) Two 'processing…' overlays appeared in the interview responder because the choice controls are now always mounted-visible, so Gradio painted its generic per-component progress spinner on each. Set show_progress='hidden' on the interview events (Begin + Continue + reveal); the conversation itself signals progress and the assessment has its own bar. 2) The assessment leaked '@@END' and a raw 'Summary of my assessment' list when the small model wrote the metadata as prose instead of the @@ASSESSMENT block. parse_assessment now recognises the metadata in three forms (the @@ASSESSMENT block, a 'Summary of my assessment' section, or a bare run of field: lines), strips all of it plus any stray @@ASSESSMENT/@@END markers from the visible reasoning, and parses fields tolerant of bullets and parenthetical glosses. Co-authored-by: Codex <noreply@openai.com>3c64ee8 -
[docs] Brand the Word (.docx) output to match the PDF template
Hélder Monteiro +1: CodexThe .docx files were bare (plain text wordmark, default fonts, minimal styling) while the PDFs had the full branded template. Bring the .docx to parity: render the Fugee mark to a committed PNG (Pillow) and embed it in a masthead with the Fraunces wordmark + tagline + teal rule, set Inter body / Fraunces teal headings, amber-bold [placeholders], section dividers, a muted subtitle/note, and a page footer — mirroring templates/_head.html. Verified by rendering both to PNG side by side. Note: fonts are referenced by name (Word substitutes if Fraunces/Inter aren't installed); DOCX font *embedding* is intentionally not done. Co-authored-by: Codex <noreply@openai.com>9835d1a -
docs: add README.md (overview + how to run) and specs/ISSUES.md (gotchas log)
Hélder Monteiro +1: CodexCo-authored-by: Codex <noreply@openai.com>a73ca6d -
[fix] Choice controls never toggle visibility; non-signatories not asylum dests
Hélder Monteiro +1: Codex1) The recurring 'press Continue to show options' bug. Real root cause: toggling a gr.CheckboxGroup from hidden->visible does NOT build its option DOM, so it paints empty until the next interaction — no in-generator/round-trip trick fixed it. Permanent fix: mount the choice controls (radio + both checkbox groups) VISIBLE and drive them by their CHOICES alone (empty = collapsed via a CSS :has rule). Setting choices on an already-mounted-visible group always renders (the one behaviour we'd confirmed works). country/text still toggle visible (those components handle it fine). This also kills the 'all controls visible / stale English labels' leak. 2) Pakistan (a non-signatory with no UNHCR) was shown as an asylum destination with a UNHCR/RSD roadmap. Protection-case recommendations now require the country to be a Convention signatory; non-signatories are dropped. And the roadmap's first step adapts: 'Register with UNHCR' only where UNHCR runs registrati…d22000a -
[ux] Permanent fix: reveal controls in a separate round-trip; localise chrome
Hélder Monteiro +1: CodexRoot cause of the recurring 'options need an extra Continue' (and the Portuguese screen showing ALL controls at once with stale English labels): the control reveal/hide rode in the SAME update as the chat re-render, and the browser drops a CheckboxGroup's paint when it competes with a large chat-HTML change. No in-generator trick (two-pass, sleep, single-frame) fixed it because it's one render cycle. Fix: split each interview turn into two round-trips — the click handler only advances the conversation and hides controls; a chained .then(reveal_controls) then reveals the active control on its own, with nothing else changing, so it always paints. Applied to both the Begin flow and Continue. Also localise the interview chrome (rail steps, 'Your answer', 'Continue') into all 10 languages so the UI around the conversation matches its language — the question text and options were already translated; only this chrome was English. Co-authored-by: Codex <noreply@openai.com>a1f40f1 -
[ux] Reveal choices in one frame; make assessment always substantive
Hélder Monteiro +1: CodexTwo fixes: 1. Choice controls (esp. the documents CheckboxGroup) rendered empty until an extra Continue. Root cause: the reveal frame sent visible:true ALONE, but a CheckboxGroup whose choices were set while hidden builds no option DOM — the choices must ride along on the reveal. Emit a single frame carrying both visibility and choices (exactly what the working Continue path does); drop the two-pass/sleep entirely. 2. Assessment could come out 'dry' — when the small model returned little parseable text, the fallback was a bare one-liner with no recommendations. Now derive case_type/grounds/risk from the interview as a safety net, write a structured multi-paragraph analysis (classification, ground, risk, why protection must be sought abroad, ranked destinations with real data), and guarantee recommendations via a curated strong-asylum shortlist so the next screen is never empty. The model's own richer narration is still preferred when present. Co-a…ec936b9 -
[ux] Reliably reveal every choice control (fix docs options)
Hélder Monteiro +1: CodexThe hidden-then-reveal frames streamed over one response, so Gradio could coalesce the choices-update and the visibility toggle into a single paint — the second checkbox group (documents) then rendered empty until an extra Continue. Emit the two passes from an async generator with a real beat between them so the client paints them as separate cycles. Co-authored-by: Codex <noreply@openai.com>297e933 -
[assess] Honest handling of economic (non-protection) cases
Hélder Monteiro +1: CodexEconomic migration has no right to asylum under the 1951/1969 Conventions, yet the app showed 'Strong match' asylum cards and a UNHCR/RSD roadmap to everyone regardless of case_type — fabricated legal guidance for an economic migrant (CLAUDE.md: wrong output is worse than no output). - Prompt: explicit rule that economic hardship ('no jobs', 'better life') is NOT a protection ground — classify as economic_or_other and point to regular work/study routes, not asylum. - country_lookup: expose non-signatory economic fields (work visa, job market, kafala, honest strategic_guidance caveats) + a curated work_route_countries() shortlist (Gulf labour markets with employer-sponsored work visas — the historically accessible routes, unlike strict Western/EU criteria). - assessment: for economic_or_other, use the deterministic labour shortlist — never the small model's possibly-Western asylum picks. - recommendations: render economic cases as honest 'Work route' cards (work visa, job…180ae9b -
[ux] Show multi-select options instantly (no extra Continue)
Hélder Monteiro +1: CodexThe single shared CheckboxGroup swapped its choices at reveal time, which Gradio renders a beat late — so the persecution-grounds / documents options only appeared after an extra Continue press. Give each multi-select question its own pre-mounted group whose (language-correct) choices are set once and never swapped; only visibility toggles thereafter, so options paint at once. Co-authored-by: Codex <noreply@openai.com>597cc8f -
[ux] Keep the top nav fixed in place across screen transitions
Hélder Monteiro +1: CodexThe site bar spans .gradio-container and centers its content, so it only moves if the container itself changes width. Two causes fixed: - the shell had max-width but no width, so it shrink-wrapped to each phase's content and re-centered between screens -> pin it with width:100%. - a vertical scrollbar appearing on tall screens (and vanishing on short ones) shifted the viewport width -> reserve the gutter always so the page never re-centers. Co-authored-by: Codex <noreply@openai.com>71ed9f5 -
[ux] Make all phase screens fill the shell at the same width
Hélder Monteiro +1: CodexThe card on each phase was shrink-wrapping to its content because the Gradio wrapper column sized to content, so width:100% on the card resolved to the content width — intake ~680px, interview situation ~560px, interview chat ~648px. Tag each outer wrapper with .screen-wrap and force it (and its card) to fill the shell, so all five screens now render at one consistent width. Co-authored-by: Codex <noreply@openai.com>c7aa25a -
[ux] Consistent screen width — all phases use 1180px
Hélder Monteiro +1: CodexEvery screen now shares one width (the largest, 1180px): intake and interview were narrower (1000 / 780); now they match assessment, recommendations and documents. No more per-screen sizing. Co-authored-by: Codex <noreply@openai.com>8d1c642 -
[ux] Widen the intake screen (was too narrow / mobile-looking)
Hélder Monteiro +1: Codexintake-screen max-width 780 -> 1000, inner 560 -> 760, language grid 480 -> 680, so the welcome no longer looks like a narrow mobile card on desktop. Co-authored-by: Codex <noreply@openai.com>52844d8 -
[ux] Trim site-bar nav + redundant intake tagline
Hélder Monteiro +1: Codex- Site bar: removed the non-clickable Intake/Interview/Assessment/… labels; now just "Fugee | Safe guidance for people on the move". - Intake: removed the duplicate "Safe guidance for people on the move" tagline above "Choose your language" (it's already in the site bar). Co-authored-by: Codex <noreply@openai.com>350aa07 -
[ux] Trust note: drop the unenforced consent clause
Hélder Monteiro +1: CodexThere is no consent control, so the note now reads simply "This conversation is private." (was "… Nothing is stored without your consent."). Co-authored-by: Codex <noreply@openai.com>a97b739 -
[docs] Bundle Inter + Fraunces locally — offline PDF generation
Hélder Monteiro +1: CodexDocument PDFs no longer fetch Google Fonts at render time (no internet dependency, and generation dropped ~22s -> ~0.8s): - agent/tools/templates/fonts/: bundled static Latin TTFs (Inter 400/600/700, Fraunces 600/700, ~280KB, committed). - _head.html: local @font-face; non-Latin scripts (Arabic/CJK/Devanagari/ Cyrillic) fall back to the system Noto fonts (also offline). - doc_generator passes base_url=templates dir so the @font-face urls resolve. Verified: no font warnings; English + Arabic documents render offline as valid PDFs. Co-authored-by: Codex <noreply@openai.com>819eaa8