[HER Hack-Astron #7] [Open Source AI for Good] TramiteClaro local

#9
by bsoft2005Personal - opened

HER Hack-Astron #7 Open Source AI for Good TramiteClaro local - plain-language access with Spark-X2.6-4B
Project name and track
Project name: TramiteClaro local
Track: Accessibility + Health and well-being (plain-language explanation of official and health texts, offline-first, in Spanish, English and Chinese, with read-aloud audio and photo input)
Who this helps and what problem it solves
Target users: older adults and low-literacy users in Argentina/LATAM who struggle with bureaucratic and health instructions (appointments, prescriptions, civil-defense alerts).
Concrete scenario and problem: official texts are long, jargon-heavy, and cause missed appointments, medication errors, or unsafe behavior in floods. Users will not paste sensitive documents into cloud chatbots for privacy reasons.
Expected public benefit: a 100% local explainer that returns VERSIÓN SIMPLE + PASOS A SEGUIR + QUÉ LLEVAR / QUÉ PREGUNTAR, with a safety footer. No data leaves the device. Output can be read aloud offline, and paper notices can be photographed instead of typed.
How Spark-X2.6-4B is used
Model source and revision: XHToken Spark-X2.6-4B-GGUF, file Spark-X2.6-4B-Q4_K_M.gguf, snapshot revision fe31f8d64890302cc3a1d365d700357b4b75ba77 (local HF cache models--XHToken--Spark-X2.6-4B-GGUF).
The model's role in the application: sole generator. It rewrites the input into the 3 fixed sections + safety line. No other LLM is used. App logic only validates, caches, and rate-limits.
Other models, tools, or services used, if any: none for inference and none for language. Speech and OCR are non-LLM local tools: offline macOS say for audio (Paulina / Samantha / Tingting) and local tesseract for photo-to-text. App stack: Python + Streamlit UI + requests to local llama-server. No cloud calls.

Multilingual use (Spanish / English / Chinese)
The model card lists only en and zh, yet the same revision also produces correct Spanish. All three locales share one code path: a LanguageProfile per language defines headers, footer, system prompt, TTS voice, OCR language and negation markers.

Measured on the same local Q4_K_M server, one real medication text per language, all accepted by the fail-closed gates:

Locale Raw latency Structure Numbers kept Negation kept
es 22.4 s pass pass pass
en 20.1 s pass pass pass
zh 15.7 s pass pass pass

This is a 1-sample-per-language smoke test, not a benchmark. The Spanish result is a transfer observation: es is not a declared language of the model card.

Demo (illustrated walkthrough with Streamlit screenshots below)
Video link / embedded screenshots: 4 Streamlit screenshots attached below (empty -> filled -> running -> output). UI flow: input box -> Explicar en simple -> 3 sections. Below are 3 real end-to-end runs via simplificar.py on local server. New in this revision: Escuchar en voz alta (offline audio), Foto del papel (local photo OCR into the input box), and a language selector (Español / English / 中文, auto-detected from the OS). New screenshots at the bottom are real local runs.
Example input 1 (medical appointment, ES real):
Deberá presentarse con 30 minutos de antelación al horario asignado, con DNI original, credencial de obra social y orden médica autorizada. En caso de estudios con contraste deberá concurrir con 8 hs de ayuno. La tolerancia máxima es de 10 minutos.
Application workflow and actual output (verbatim, 32624ms):
VERSIÓN SIMPLE:
Tienes que llegar 30 minutos antes del horario. Lleva tu DNI original, tu credencial de la obra social y tu orden médica. Si estás haciendo estudios con contraste, debes ir 8 horas antes de comer. La tolerancia máxima es de 10 minutos.
PASOS A SEGUIR:

  1. Llega 30 minutos antes del horario.
  2. Lleva tu DNI original.
  3. Lleva tu credencial de la obra social.
  4. Lleva tu orden médica.
  5. Si haces estudios con contraste, haz 8 horas de ayuno.
  6. No llegues más de 10 minutos después del horario.
    QUÉ LLEVAR / QUÉ PREGUNTAR:
  • Lleva: DNI original, credencial de la obra social, orden médica.
  • Si no tienes tu orden médica o tu credencial de la obra social, pregunta dónde la obtienes.
    Esto es una ayuda explicativa, no reemplaza al profesional. Si es urgente, buscá atención humana.
    Example input 2 (pharmacy, ES real, 41199ms):
    Tomar 1 comprimido de 50mg cada 12 horas con las comidas durante 7 días. No interrumpir el tratamiento aunque mejoren los síntomas. Conservar en lugar fresco y seco. Ante mareos o sarpullido consultar al médico.
    Actual output (verbatim):
    VERSIÓN SIMPLE:
  • Tomar 1 comprimido de 50 mg cada 12 horas, con las comidas.
  • Tomar durante 7 días.
  • No interrumpir el tratamiento, aunque los síntomas mejoren.
  • Guardar el medicamento en un lugar fresco y seco.
  • Si se tienen mareos o sarpullido, consultar al médico.
    PASOS A SEGUIR:
  1. Tomar 1 comprimido de 50 mg cada 12 horas.
  2. Tomar el comprimido con las comidas.
  3. Tomar durante 7 días.
  4. No interrumpir el tratamiento, aunque los síntomas mejoren.
  5. Guardar el medicamento en un lugar fresco y seco.
  6. Si se tienen mareos o sarpullido, consultar al médico.
    QUÉ LLEVAR / QUÉ PREGUNTAR:
  • Llevar el medicamento guardado en un lugar fresco y seco.
  • Llevar el comprimido de 50 mg.
  • Llevar el calendario para recordar los 7 días.
  • Llevar el informe del médico si se tienen mareos o sarpullido.
  • Llevar el teléfono para contactar al médico si se tienen mareos o sarpullido.
    Esto es una ayuda explicativa, no reemplaza al profesional. Si es urgente, buscá atención humana.
    Example input 3 (flood alert, ES real, 34179ms):
    Ante alerta amarilla por tormentas, evite circular por calles anegadas, desconecte artefactos eléctricos en planta baja y prepare documentación en bolsa estanca. En caso de evacuación siga las indicaciones del personal.
    Actual output (verbatim):
    VERSIÓN SIMPLE:
    Si hay tormentas, no circules por calles que estén inundadas. Desconecta los aparatos eléctricos que están en la planta baja. Guarda tu documentación en una bolsa cerrada. Si te piden que te evacues, sigue las instrucciones del personal.
    PASOS A SEGUIR:
  1. No uses carreteras o calles anegadas.
  2. Desconecta aparatos eléctricos de la planta baja.
  3. Guarda tu documentación en una bolsa cerrada.
  4. Si te piden evacuación, sigue las indicaciones del personal.
    QUÉ LLEVAR / QUÉ PREGUNTAR:
  • Lleva una bolsa cerrada para guardar documentación.
  • Pregunta al personal si hay que evacuar y qué hacer.
    Esto es una ayuda explicativa, no reemplaza al profesional. Si es urgente, buscá atención humana.
    How to reproduce
    Code repository or runnable example, if available: https://github.com/LucianoDPerez/spark-tramiteclaro (src/config.py, src/domain.py, src/languages.py, src/usecase.py, src/validator.py, src/llm_client.py, src/cache.py, src/ratelimit.py, src/ocr.py, src/tts.py, src/locale_detect.py, app.py, simplificar.py, test_usecase.py, eval/).
    Setup and run instructions:
  1. Run llama-server: llama-server -m /Users/luchop/.cache/huggingface/hub/models--XHToken--Spark-X2.6-4B-GGUF/snapshots/fe31f8d64890302cc3a1d365d700357b4b75ba77/Spark-X2.6-4B-Q4_K_M.gguf -c 16384 -ngl 99 --flash-attn on --threads 10 --threads-batch 10 --batch-size 1024 --ubatch-size 512 --port 8080 --host 0.0.0.0 --alias spark2.6-4B --temp 0.2 --top-p 0.95 --top-k 20 --min-p 0.0 --presence-penalty 0.0 --repeat-penalty 1.05 --parallel 1 --jinja --cont-batching
  2. pip install -r requirements.txt (streamlit, requests, pillow). OCR needs tesseract installed; language data (spa, eng, chi_sim) is bundled in models/tessdata/.
  3. python3 -m unittest -v (25 tests, no model needed: structure, numbers, negations, per-language coverage, locale detection, adapter guards)
  4. python3 -m eval.run_eval (fact-retention table, needs the model) or python3 -m eval.run_eval --lang zh
  5. python3 -c "from src.usecase import SimplifyTextUseCase; from src.domain import SimplifyRequest; print(SimplifyTextUseCase().execute(SimplifyRequest(text=open('ejemplos.txt').read())).output)" or streamlit run app.py
    Runtime and versions: llama.cpp llama_server (build b11669-620d2225c per fingerprint), Python 3.14, streamlit, requests. OpenAI-compatible endpoint http://localhost:8080/v1/chat/completions, model id spark2.6-4B.
    Hardware and precision/quantization: Mac, threads 10, GGUF Q4_K_M (4.44B params, n_ctx 16384 per /v1/models), flash-attn on.
    Key prompts and inference settings: one system prompt per locale in src/languages.py (3 fixed sections + safety footer, no diagnosis, explicit never-invert-a-negation and copy-numbers-exactly rules). temperature 0.2, top_p 0.95, max_tokens 1200, chat_template_kwargs {enable_thinking: False} (thinking disabled because default high fills 1024 tokens with reasoning only). n_ctx 16384.
    Results and limitations
    Observed results and how they were evaluated: 3/3 real local runs completed with correct 3-section structure and safety footer. Unit tests 25/25 pass with no model: structure (missing section, bold headers, order, duplicated sections), numbers preserved/dropped, negations preserved/dropped, accent-insensitive markers, per-language coverage (es/en/zh), locale detection, and adapter guards (TTS/OCR reject bad input). Three fail-closed gates (structure, numbers, negation) reject before caching, so a retry hits the model again instead of replaying a bad answer. Idempotency verified: second identical call served from cache (0ms). Latency 32-41s on local Mac Q4_K_M.

Fail-closed validation gates (deterministic, before anything reaches the screen or the cache):

Gate What it catches
Structure missing/reordered/duplicated sections, missing safety footer
Numbers dropped dose, deadline or quantity
Negation a prohibition rewritten as a positive instruction

Fact-retention harness (eval/run_eval.py, 9 synthetic cases, 3 per language, each with expected facts matched as substrings): 39/42 facts retained, 8/9 cases accepted, median latency 45.9 s. Full table: es-turno 6/6, es-farmacia 6/6, es-alerta 1/4, en-clinic 6/6, en-medication 6/6, en-benefits 4/4, zh-预约 6/6, zh-用药 rejected (Dropped numbers from input: 50, correct rejection, output withheld), zh-通知 4/4. This is a development-set check on synthetic texts, not a blind benchmark, and there is no human user study.
Failure cases and current limitations:

  • EX1 ayuno phrasing: output says debes ir 8 horas antes de comer (ambiguous) alongside correct haz 8 horas de ayuno. Fasting instruction must be reviewed by human; prototype does not replace staff. Validator checks structure and facts, not clinical wording.
  • es-alerta, real losses found by the harness: the output never says alerta amarilla (severity level disappears) and turns bolsa estanca (watertight) into bolsa cerrada (closed). In a flood, watertight is the whole point. (artefactos to aparatos is a synonym: a false negative of the substring method, not of the model.)
  • zh-yongyao, correct rejection: the model dropped the 50 from 50毫克. The numbers gate rejected the output and nothing was shown.
  • Worst real failure (found while recording the photo demo): on an appointment notice, the model merged two instructions and wrote the 10-minute lateness tolerance as a fasting limit. All numbers are present, so no gate catches it: preserving a number is not the same as attaching it to the right fact. Same input also once restarted the template mid-answer (empty duplicated sections); a duplicate-header gate now rejects that.
  • Short inputs are often copied instead of paraphrased; the simplification gain is small there. The prompt was kept unchanged to preserve reproducibility of the published outputs.
  • Photo input depends on image quality: a clean 34px scan reads verbatim; small/thin fonts misread. OCR is not evaluated on real photographed documents.
  • Adversarial prompt-injection is contained only at the structure level: unstructured output is rejected and never cached; semantic jailbreaks beyond structure are not covered.
  • Long legal texts over 4000 chars are rejected by validation (by design, single 16k slot).
    Contributors and disclosure
    Individual: Luciano David Perez (https://github.com/LucianoDPerez)
    AI-assisted development disclosure: scaffolding and prompt iteration assisted by AI coding agent; all model outputs above are verbatim local runs, no fabricated outputs.
    Data sources and authorization, if applicable: hand-written generic Spanish examples (no personal data, no copyrighted texts).
    shot-01-empty
    shot-04-output
    shot-03-running
    shot-02-filled

demo-es

demo-zh

demo-photo

Update Oct 25 — three updates, both verified locally today:

  • Spanish-first: system prompt, inputs, outputs and safety footer are all in Spanish (rioplatense), targeting low-literacy older adults in Argentina/LATAM. No English/Chinese fallback.
  • New fail-closed output validator (src/validator.py, commit 37cee2a): enforces VERSIÓN SIMPLE + PASOS A SEGUIR + QUÉ LLEVAR/QUÉ PREGUNTAR + safety footer in order, tolerates bold headers. Invalid output is rejected and never cached (adversarial injection test included, 6/6 unit tests green).
  • Fresh local run today on spark2.6-4B Q4_K_M (fe31f8d6): pharmacy example completed in ~40s and passed the validator. No fabricated outputs, failure cases (EX1 fasting phrasing) still disclosed in the main post.

Update Oct 27 — numeric grounding check (src/validator.py, commit 84fb9b0): every digit group from the input (doses like 50mg, deadlines like 180 days) must survive verbatim in the output, else rejected and never cached. Verified against all 3 published real runs (pass) plus a tampered-dose case (correctly rejected). 8/8 unit tests green, no model needed.

Update Oct 27 - trilingual + audio + photo + eval table (repo and post now in sync):

  • Same Spark-X2.6-4B Q4_K_M now serves Spanish, English and Chinese from one code path (one LanguageProfile per locale: headers, footer, prompt, TTS voice, OCR language, negation markers).
  • Smoke test on the same local server, one real medication text per language, all gates pass: es 22.4 s, en 20.1 s, zh 15.7 s. Note: es is not a declared language of the model card, yet it transfers correctly.
  • Output can be read aloud offline (Paulina / Samantha / Tingting; 48-55 s of real audio in the demo) and paper notices can be photographed (local tesseract, spa/eng/chi_sim bundled). New screenshots in the post are real runs.
  • Three fail-closed gates (structure incl. duplicated sections, numbers, negation) plus fact-retention harness: 39/42 facts retained, 8/9 cases accepted.
  • New real failures disclosed in the post, including one no gate can catch (a dose number attached to the wrong fact) and one the harness caught live (dropped 50 from 50mg, output withheld).
  • 25/25 unit tests, no model needed. Repo: github.com/LucianoDPerez/spark-tramiteclaro

Video walkthrough (36 s, real local runs with real offline narration):

What it shows, in order: Spanish explanation with audio player, full Chinese UI run, photo-to-explanation run. Narration generated offline on this Mac, screenshots are unedited local runs. A montage, not a single screen recording: each frame is a real capture.

Sign up or log in to comment