Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 28 additions & 0 deletions plugins/google_meet/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,34 @@ On macOS, hermes will **not** switch your system audio input automatically — t
user has to do it. This is deliberate: switching default input on a whim would
be a surprising side effect.

### Configuring the OpenAI Realtime model

Realtime mode uses the OpenAI Realtime API. By default the bot connects with
`gpt-realtime`. Override the model via env var:

| Env var | Default | Purpose |
|---|---|---|
| `HERMES_MEET_REALTIME_MODEL` | `gpt-realtime` | OpenAI Realtime model identifier passed in the WebSocket URL |
| `HERMES_MEET_REALTIME_VOICE` | `alloy` | Voice id (e.g. `alloy`, `verse`, `marin`, `cedar`) |
| `HERMES_MEET_REALTIME_KEY` | falls back to `OPENAI_API_KEY` | API key for the Realtime WS |
| `HERMES_MEET_REALTIME_INSTRUCTIONS` | none | System instructions string |

Supported model identifiers (as of the 2026-05-07 OpenAI Realtime API GA):

| Identifier | When to use |
|---|---|
| `gpt-realtime` | Default. Stable baseline. |
| `gpt-realtime-2` | Recommended for new agents. GPT-5-class reasoning, 128K context. |
| `gpt-realtime-2025-08-28` | Pinned snapshot for `gpt-realtime-2`. Use when you want pinned behavior. |

Hermes does not validate the value, so any current or future identifier OpenAI
ships will pass straight through. Example:

```bash
echo 'HERMES_MEET_REALTIME_MODEL=gpt-realtime-2' >> ~/.hermes/.env
hermes meet join https://meet.google.com/abc-defg-hij --mode realtime
```

## Remote node host

On the node machine (e.g. user's Mac with a signed-in Chrome):
Expand Down
2 changes: 2 additions & 0 deletions plugins/google_meet/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,8 @@ The user says any of:

Pick `realtime` only when the user actually wants the agent to speak. It costs real money (OpenAI Realtime is pay-per-audio-minute) and requires a virtual audio device set up on the machine running the bot.

Realtime mode connects to the OpenAI Realtime API. The model is configurable via `HERMES_MEET_REALTIME_MODEL` (default `gpt-realtime`). Set it to `gpt-realtime-2` for the GPT-5-class voice model, or pin to the snapshot `gpt-realtime-2025-08-28` for stable behavior. See `plugins/google_meet/README.md` for the full env-var table.

## Two locations

| Location | When |
Expand Down
7 changes: 4 additions & 3 deletions tests/plugins/test_google_meet_realtime.py
Original file line number Diff line number Diff line change
Expand Up @@ -86,23 +86,24 @@ def _connect(url, **kwargs):
# ---------------------------------------------------------------------------

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please make this a model-agnostic pass-through contract rather than a fixed provider-model catalog. A single arbitrary sentinel identifier, asserted as the URL model query value, protects the behavior without creating a change-detector test when OpenAI changes its identifiers.



def test_connect_sends_session_update_with_voice_and_instructions(monkeypatch):
@pytest.mark.parametrize("model_id", ["gpt-realtime", "gpt-realtime-2"])
def test_connect_sends_session_update_with_voice_and_instructions(monkeypatch, model_id):
from plugins.google_meet.realtime.openai_client import RealtimeSession

ws = _FakeWS(recv_frames=[])
captured = _install_fake_websockets(monkeypatch, ws)

sess = RealtimeSession(
api_key="sk-test",
model="gpt-realtime",
model=model_id,
voice="verse",
instructions="Be brief.",
)
sess.connect()

# Auth + beta headers set.
assert captured["url"].startswith("wss://api.openai.com/v1/realtime")
assert "model=gpt-realtime" in captured["url"]
assert f"model={model_id}" in captured["url"]
headers = captured["headers"] or []
hdict = dict(headers)
assert hdict.get("Authorization") == "Bearer sk-test"
Expand Down
10 changes: 10 additions & 0 deletions website/docs/user-guide/features/built-in-plugins.md
Original file line number Diff line number Diff line change
Expand Up @@ -207,6 +207,16 @@ The agent kicks off the meeting join, streams the transcription back into its co

**When to use it:** recurring standups where you want a bot to transcribe + summarize for async attendees; deposition-style interviews where you want structured notes; any case where you'd otherwise need Fireflies / Otter / Grain. When you'd rather not have an AI listening in — don't enable it.

**Realtime mode (optional):** the bot can speak back into the call via the OpenAI Realtime API when joined with `mode='realtime'` (see `plugins/google_meet/README.md` for the audio bridge setup). The model identifier is configurable:

| Variable | Default | Purpose |
|---|---|---|
| `HERMES_MEET_REALTIME_MODEL` | `gpt-realtime` | OpenAI Realtime model identifier passed in the WebSocket URL |
| `HERMES_MEET_REALTIME_KEY` | falls back to `OPENAI_API_KEY` | API key for the Realtime WebSocket |
| `HERMES_MEET_REALTIME_VOICE` | `alloy` | Voice id (`alloy`, `verse`, `marin`, `cedar`) |

Supported model identifiers (as of the 2026-05-07 OpenAI Realtime API GA): `gpt-realtime` (stable baseline), `gpt-realtime-2` (recommended for new agents; GPT-5-class reasoning, 128K context window), and the pinned snapshot `gpt-realtime-2025-08-28`. Hermes does not validate the value, so future identifiers OpenAI ships work without a Hermes upgrade.

**Disabling:** `hermes plugins disable google_meet`. Any cached transcripts and recordings stay in `~/.hermes/cache/google_meet/` until you remove them.

### hermes-achievements
Expand Down
Loading