Open Robby · Source and setup

Choose the language model; keep the conversation and timing in view

Robby can use Qwen 3.8 27B through the selected OpenRouter endpoint, with Cerebras as the configured default, or the already resident local Nemotron. The administrator's key stays encrypted outside the repository. The last recognized/user/robot turns sit beside the avatar, and measured component timings appear underneath it.

Actual language-model checks

These are individual engineering observations, not a statistical provider speed ranking. Provider load, geography, prompt size and API transit affect them. LLM first sentence is when enough generated words exist for the first TTS request; the model still emits only compact acting cues and speech.

Requested routeActual providerFirst tokenFirst sentenceTaskPassed
qwen / cerebras/fp16Cerebras450.37 ms468.79 msSag mir freundlich guten Morgen.True
qwen / cerebras/fp16Cerebras277.14 ms304.07 msSchreibe ein lustiges Gedicht mit vier Zeilen.True
local / locallocal49.01 ms298.49 msSchlage ein Spiel vor.True
qwen / deepinfra/bf16DeepInfra495.05 ms642.28 msSag nur: Guten Morgen.True

Real browser turns and stage timing

CPU Chromium, actual public HTTPS/WSS, authored synthetic microphone audio, real local ASR and real speech synthesis. No private microphone recording was used. A dash means unmeasured/not applicable; typed input has no ASR operation.

TurnASRSpeaker waitLLM first sentenceTTS first PCMReply → first PCMPlayback lead
fake microphone · Whisper → Qwen/Cerebras → TTS134 ms57 ms514.5 ms802.1 ms1332.2 ms314.5 ms
four-line poem · Qwen/CerebrasTyped—335.3 ms825.6 ms1162.6 ms314.5 ms
local Nemotron switchTyped—577.2 ms834.9 ms1413.3 ms314.5 ms
Qwen alternative endpoint · DeepInfraTyped—1177.9 ms780.7 ms1959.9 ms314.5 ms
real interruption-rule tool · CerebrasTyped—1688.2 ms813.8 ms2506.1 ms314.5 ms

Read the timings correctly

Stages overlap, especially LLM generation and TTS. Do not sum the cards. Each accepted reply has an ID so queued input and late measurements can be distinguished. Existing playback timestamps, interruptions and turn-taking remain in place.

Exact provider routing and tools

The requested web endpoint corresponds to OpenRouter provider tag cerebras/fp16. Requests use provider.only with allow_fallbacks=false; an unavailable provider is reported instead of silently switching. Additional current endpoint tags come from the public model-endpoint API. The model list is allowlisted to Qwen and the local model; arbitrary provider URLs cannot receive the server key.

Reasoning is disabled/excluded for fast spoken replies. Provider-specific unsupported JSON-format parameters are omitted. The same acting catalogue, sentence framer, complete-task guard, avatar commands and tools are shared. Explicit interruption-rule requests require actual tools, preserving the banana-game setup rather than accepting a verbal promise. The independent decision model remains separate. One brief retry handles a rate-limit response before any speech; already emitted words are never replayed. Rate limits and provider outages can still interrupt a request.

Credential handling

The administrator enrolls the key once with python credential_store.py, using a hidden terminal prompt. Authenticated Fernet ciphertext and a separately generated master secret live in a private directory outside the checkout. Directory mode is 0700, file modes 0600. No read-key API exists. The browser sees only credential availability and never receives the administrator's plaintext or ciphertext. Source export and staged release checks exclude credential files.

Optional personal keys use a password field, are cleared on submission and kept encrypted only in that connection's server memory. They neither overwrite the shared administrator key nor enter browser storage. Disconnecting discards the override. Encryption at rest depends on a protected master secret and OS account: this is not an HSM or protection against server-account compromise. Cloud mode sends conversation text to OpenRouter and the chosen provider; audio processing stays on this server and camera inference stays in the browser.

Interface changes

The sticky desktop sidebar shows live ASR, the latest user turn and Robby's current reply. Full history remains available. The six timing cards sit directly under the avatar. Model selectors, microphone/camera controls and the main interruption checkbox stay visible; speech-pause and detailed interruption settings are grouped compactly. Mobile stacks the panels without removing controls. Preferences save model/provider IDs only.

Automatic checks: 398; passed=True. Real route/API checks: True. HTTPS browser checks: True.

Reproduce

pip install -r requirements.txt
python credential_store.py  # hidden prompt; secret lives outside the checkout
# Start/restart the documented Robby server and FastAPI facade.
python run_unit_checks.py
python validate_openrouter.py
uv run --no-project --with playwright --python /path/to/venv/bin/python python browser_openrouter_check.py

The validation calls actual paid OpenRouter routes using the configured server credential, with authored requests. Historical local-model browser checks explicitly select local mode. No key is included in this report, its JSON receipts, source archive or repository. The source contains the exact shared system prompt, transport adapter, key-store code, per-session settings and measurement definitions.

Primary references: OpenRouter exact-provider routing, reasoning controls, Qwen model/endpoints, and Fernet authenticated encryption.

Machine-readable receipts
{
  "api": {
    "passed": true,
    "started": 1793009385.3248456,
    "rows": [
      {
        "model": "qwen",
        "provider": "cerebras/fp16",
        "actual_provider": "Cerebras",
        "input": "Sag mir freundlich guten Morgen.",
        "reply": "Guten Morgen!\nIch hoffe, dein Kaffee ist schon bereit und die Sonne schickt dir ein kleines Lächeln.",
        "first_token_ms": 450.37,
        "first_sentence_ms": 468.79,
        "total_http_ms": 489.37,
        "passed": true
      },
      {
        "model": "qwen",
        "provider": "cerebras/fp16",
        "actual_provider": "Cerebras",
        "input": "Schreibe ein lustiges Gedicht mit vier Zeilen.",
        "reply": "Ein Roboter stolpert in den Türrahmen rein,\nsein Blinklicht flackert, doch er bleibt nicht allein,\ndenn der Staubsauger lacht und saugt den Keks am Boden auf,\nso wird aus einem Patzer ein ganz lustiger Strauß.",
        "first_token_ms": 277.14,
        "first_sentence_ms": 304.07,
        "total_http_ms": 372.4,
        "passed": true
      },
      {
        "model": "local",
        "provider": "local",
        "actual_provider": "local",
        "input": "Schlage ein Spiel vor.",
        "reply": "Wir könnten ein Wortassoziationsspiel spielen, wo wir uns gegenseitig Wörter nennen, die mit dem vorherigen verbunden sind!",
        "first_token_ms": 49.01,
        "first_sentence_ms": 298.49,
        "total_http_ms": 300.98,
        "passed": true
      },
      {
        "model": "qwen",
        "provider": "deepinfra/bf16",
        "actual_provider": "DeepInfra",
        "input": "Sag nur: Guten Morgen.",
        "reply": "Guten Morgen.",
        "first_token_ms": 495.05,
        "first_sentence_ms": 642.28,
        "total_http_ms": 644.78,
        "passed": true
      }
    ],
    "physical_microphone": false,
    "owner_encrypted_outside_source": true,
    "real_rule_tool": {
      "passed": true,
      "reply": "Klar, das habe ich jetzt eingestellt! Sobald du von Bananen sprichst, rufe ich dir sofort zu: Stopp, stopp!",
      "actions": [
        {
          "name": "set_interruption_rule",
          "arguments": {
            "condition": "User talks about bananas",
            "trigger_terms": [
              "Banane",
              "Bananen",
              "banana",
              "bananas"
            ],
            "match_mode": "any_term",
            "reply": "Stopp, stopp!"
          }
        }
      ],
      "provider": "Cerebras"
    },
    "finished": 1793009388.2036214,
    "key_never_in_responses": true
  },
  "https_browser": {
    "passed": true,
    "started": 1793010979.7064188,
    "physical_microphone": false,
    "turns": [
      {
        "case": "fake microphone · Whisper → Qwen/Cerebras → TTS",
        "reply": "Huhu! Ja, gerne!\nAch, das ist ja eine richtig schöne Grimasse!\nIch hoffe, dir hat das Gesicht gefallen!",
        "user": "Hallo Robby, kannst du bitte eine lustige Grimasse machen?",
        "timings": {
          "asr": 134,
          "pause": 620,
          "asr_model": "whisper_turbo",
          "speaker": 56.979,
          "speaker_background": null,
          "speaker_cached": false,
          "queue": 57.18,
          "llm": 514.52,
          "ttft": 483.91,
          "model": "qwen/qwen3.8-27b",
          "provider": "Cerebras",
          "tts": 802.08,
          "reply": 1332.2,
          "llm_wall": 563.35,
          "buffer": 314.5124716553287,
          "tts_gpu": 4300,
          "rtf": 0.6127
        },
        "asr_card": "134 ms",
        "speaker_card": "57 ms",
        "pcm_total": 14
      },
      {
        "case": "four-line poem · Qwen/Cerebras",
        "reply": "Die Sonne lacht vom blauen Himmel,\ndie Vögel zwitschern fröhlich schnell,\nmein Herz ist leicht, mein Lachen hell,\ndenn heute ist ein Tag für mich und dich!",
        "user": "Schreibe ein fröhliches Gedicht mit vier Zeilen.",
        "timings": {
          "asr": null,
          "pause": null,
          "asr_model": null,
          "speaker": null,
          "speaker_background": null,
          "speaker_cached": null,
          "queue": null,
          "llm": 335.28,
          "ttft": 304.13,
          "model": "qwen/qwen3.8-27b",
          "provider": "Cerebras",
          "tts": 825.65,
          "reply": 1162.64,
          "llm_wall": 385.3,
          "buffer": 314.5124716553305,
          "tts_gpu": 5044,
          "rtf": 0.6008
        },
        "asr_card": "Typed",
        "speaker_card": "—",
        "pcm_total": 31
      },
      {
        "case": "local Nemotron switch",
        "reply": "Stell dir vor, wir bauen eine riesige, alberne Roboter-Stadt aus Kissen und Socken!\nWir können dann die Verkehrsschilder aus Bananenschalen machen und die Bewohner sind nur wackelnde Socken-Monster!",
        "user": "Schlage ein lustiges Spiel vor.",
        "timings": {
          "asr": null,
          "pause": null,
          "asr_model": null,
          "speaker": null,
          "speaker_background": null,
          "speaker_cached": null,
          "queue": null,
          "llm": 577.25,
          "ttft": 362.01,
          "model": "nemotron-bolt",
          "provider": "local",
          "tts": 834.87,
          "reply": 1413.33,
          "llm_wall": 825.01,
          "buffer": 314.5124716553269,
          "tts_gpu": 4058,
          "rtf": 0.4543
        },
        "asr_card": "Typed",
        "speaker_card": "—",
        "pcm_total": 43
      },
      {
        "case": "Qwen alternative endpoint · DeepInfra",
        "reply": "Guten Morgen.",
        "user": "Sag nur: Guten Morgen.",
        "timings": {
          "asr": null,
          "pause": null,
          "asr_model": null,
          "speaker": null,
          "speaker_background": null,
          "speaker_cached": null,
          "queue": null,
          "llm": 1177.9,
          "ttft": 583.28,
          "llm_wall": 1177.92,
          "model": "qwen/qwen3.8-27b",
          "provider": "DeepInfra",
          "tts": 780.74,
          "reply": 1959.88,
          "buffer": 314.5124716553269,
          "rtf": 1.1618,
          "tts_gpu": 1051
        },
        "asr_card": "Typed",
        "speaker_card": "—",
        "pcm_total": 47
      },
      {
        "case": "real interruption-rule tool · Cerebras",
        "reply": "Alles klar, ich habe die Regel eingestellt!",
        "user": "Wenn ich über Bananen spreche, unterbrich mich und sage Stopp, stopp!",
        "timings": {
          "asr": null,
          "pause": null,
          "asr_model": null,
          "speaker": null,
          "speaker_background": null,
          "speaker_cached": null,
          "queue": null,
          "llm": 1688.22,
          "model": "qwen/qwen3.8-27b",
          "provider": "Cerebras",
          "tts": 813.83,
          "reply": 2506.1,
          "buffer": 314.5124716553269,
          "rtf": 1.0979,
          "tts_gpu": 1076
        },
        "asr_card": "Typed",
        "speaker_card": "—",
        "pcm_total": 51
      }
    ],
    "desktop_context_beside_avatar": true,
    "timings_below_avatar": true,
    "real_rule_installed": true,
    "personal_key_not_persisted": true,
    "selection_persists_after_reload": true,
    "mobile_no_overflow": true,
    "owner_key_not_in_browser_responses": true,
    "errors": [],
    "finished": 1793011042.5712416
  },
  "unit": {
    "finished": 1793011755.8150213,
    "modules": [
      "test_audio_input",
      "test_asr_priority",
      "test_gpu_guard",
      "test_enrollment_cues",
      "test_recovery_rules",
      "test_stop_commands",
      "test_live_decisions",
      "test_laya_policy",
      "test_conversation_prompt",
      "test_conversation_director",
      "test_shared_turn",
      "test_speaker_names",
      "test_speaker_session",
      "test_speaker_turns",
      "test_identity",
      "test_speaker_teacher",
      "test_bridge",
      "test_performance",
      "speaker_v2.test_predictor",
      "speaker_v4.test_v4",
      "speaker_v5.test_v5",
      "speaker_live.test_playback",
      "test_open_speakers",
      "test_knowledge_tools",
      "test_audio_transport",
      "test_stop_feedback",
      "test_stop_voice",
      "test_release_scan",
      "test_avatar_actions",
      "test_avatar_commands",
      "test_avatar_personality",
      "test_bolt",
      "test_pause_endpoint",
      "test_asr_choice",
      "test_llm_backends",
      "test_visual_identity",
      "test_conversation_focus"
    ],
    "tests": 398,
    "failures": 0,
    "errors": 0,
    "skipped": 0,
    "passed": true,
    "network_or_physical_microphone_used": false
  }
}

Generated 2026-10-26 10:50 UTC.