Skip to content
Mens Mercatus

02 · Multimodal assistant

Mercatus Assist & THINK

A C# / ASP.NET Core service and a React workspace that call Gemini from the server, keep the API key off the browser, and say so when the live path is down.

Live, with a labelled fallback

Challenge

A portfolio assistant is easy to fake with a browser key. The engineering problem was a real model call, a swap-able provider, a safe public surface, and an honest screen when the service is down.

Role and scope

Built the API, the proxy, and the workspace

  • Shipped an ASP.NET Core 8 minimal API in C# that calls Gemini through an OpenAI-compatible chat completions client, so the provider can change in config.
  • Ran that API as a systemd service on an Ubuntu VPS, behind nginx, with the static site behind Cloudflare.
  • Put a same-origin proxy in front so the API key stays on the server.
  • Built THINK as a React 19, Vite, and TypeScript workspace: chat, image, live voice over WebSocket, plus sites, code, and a learning path.
  • Labelled the fallback. If the live robot is unhealthy, the UI says it is demo coaching instead of pretending.

Architecture

  1. 01

    Experience

    Mercatus Assist for guided chat. THINK for multimodal work: chat, image, live voice, sites, code, and learning.

  2. 02

    Gateway

    Same-origin proxy to the VPS. The browser never receives the provider key.

  3. 03

    Robot

    ASP.NET Core 8 minimal API. OpenAI-compatible client. Exponential backoff on HTTP 429 and 5xx.

  4. 04

    Fallback

    Health failure switches the surface to a labelled demo path. Live and demo are not the same state.

60–90 second demo

  1. Play the 60-second walkthrough on this page, then open Mercatus Assist.
  2. Ask one question. Note whether the reply is marked live or demo.
  3. Open THINK and try chat only. Live voice and image are capped per IP — that cap is the control, not a bug.
  4. If the robot is down, the honest outcome is the labelled fallback, not a blank error.
Narrated guide, then a question on the live robot, including the moment it is still thinking.

Flows to look at

  • Live path through the VPS proxy. The banner names Gemini and the retry behavior. Feed copy inside THINK is product content, not a résumé claim.

    Mercatus Assist

    Live path through the VPS proxy. The banner names Gemini and the retry behavior. Feed copy inside THINK is product content, not a résumé claim.

  • React workspace: pulse, chat, live voice, sites, and code. The model key stays on the server.

    THINK

    React workspace: pulse, chat, live voice, sites, and code. The model key stays on the server.

Open the live flow

AI behavior

  • AI-assisted: one bounded model response for chat, image, or voice.
  • Agentic: routing across approved surfaces (chat, voice relay, tool-style paths) with session context.
  • Human-controlled: the visitor chooses the path. The service does not take external actions on their behalf.
  • Observability: live versus demo is a visible state. Rate limits are per IP on chat, image, video, and live voice.

Security

  • API key isolation on the VPS.
  • Per-IP rate limits on the public multimodal paths.
  • Provider is config-swappable, so a key rotation does not require a UI rewrite.
  • No private prompts are published here to prove the architecture.

Monitoring

  • The fallback itself is a health signal: visitors see demo mode instead of a silent failure.
  • Process supervision is systemd on Ubuntu, with nginx in front and Cloudflare on the static site.

Limits

  • THINK is a portfolio workspace with caps, not an unlimited production assistant.
  • A public GitHub profile is not on the résumé. The service code is available in a walkthrough.
  • Unit-test counts are not published. The public checks are the fallback label, the proxy boundary, and the retry policy.

Contact meView the résuméAll case studies

065 842 4274Hire me