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
44 changes: 44 additions & 0 deletions docs/gateway-context.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
# Gateway Context Providers

OpenAB gateway adapters can optionally enrich an admitted user turn with recent chat context that the bot would otherwise miss because of mention gating or platform-specific admission rules.

The gateway-level `ContextProvider` abstraction keeps this behavior shared across platforms:

- `observe()` records a message that was seen by an adapter but not dispatched to the agent.
- `fetch_context()` returns recent context for an admitted turn.
- `inject_context()` prepends that context with a clear boundary before the current message.

## Provider Types

| Provider | Intended platforms | Data source |
|---|---|---|
| `BufferedContextProvider` | LINE, Telegram, WeChat/WeCom, Feishu fallback | webhook observe -> local bounded buffer |
| `ApiFetchContextProvider` | Discord, Slack, Teams, Google Chat where available | platform history API |
| Hybrid provider | Google Chat and other mixed-permission platforms | API fetch when possible, buffer fallback otherwise |

The first implementation wires LINE group/room text to `BufferedContextProvider`. Future adapters can reuse the same trait without changing the prompt injection format.

## Defaults

Context capture is disabled by default.

| Setting | Default |
|---|---|
| `enabled` | `false` |
| `ttl` | `24h` |
| `max_messages` | `50` |
| `max_chars` | `8000` |

Gateway-wide environment variables use the `GATEWAY_CONTEXT_*` prefix. Platform-specific settings can override them; for example LINE uses `LINE_GROUP_CONTEXT_*`.

## Scope And Storage

Buffered context is:

- scoped by platform, channel, optional thread, and bot id
- in-memory only
- drained after injection
- bounded by TTL, message count, and total characters
- not long-term memory, retrieval storage, or GBrain state

This is intentionally short-term conversational continuity. Platforms with reliable history APIs can later implement API-backed or hybrid providers instead of relying only on local buffers.
18 changes: 13 additions & 5 deletions docs/line.md
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,7 @@ In the LINE Developers Console → **Messaging API** tab → scan the QR code wi

- **1:1 chat** — send a message to the bot, get an AI agent response
- **Inbound voice messages in 1:1 chat** — LINE-hosted audio messages are downloaded through the LINE Content API and forwarded to OpenAB as `audio` attachments, so the existing STT flow can transcribe them. This requires `[stt] enabled = true` in OpenAB core. See [STT (Speech-to-Text)](stt.md).
- **Group chat** — add the bot to a group; it responds only when @-mentioned (see @mention gating below)
- **Group chat** — add the bot to a group; it responds when directly @-mentioned. Deployments can opt in to folding recent unmentioned text into the next direct-mention turn through the gateway ContextProvider buffer (see @mention gating below)
- **Inbound images** — user-sent LINE images are downloaded through the LINE Content API and forwarded to OpenAB as image attachments
- **Webhook signature validation** — HMAC-SHA256 via `LINE_CHANNEL_SECRET`

Expand All @@ -96,11 +96,12 @@ In the LINE Developers Console → **Messaging API** tab → scan the QR code wi

- **Threads** — LINE has no thread/topic concept. All messages in a chat share one agent session.
- **Reactions** — LINE Bot API does not support message reactions.
- **@mention gating** — Supported (zero-config). In group/room chats the gateway only forwards messages where the bot is explicitly @-mentioned (LINE's native `mentionees[].isSelf` signal). 1:1 DMs are always forwarded. No env var is needed.
- *Limitation — non-text messages*: LINE only attaches mention data to text messages. Images, videos, stickers, files, and location messages in groups are silently dropped because they cannot carry an @-mention.
- *Limitation — group voice messages*: LINE voice/audio messages in groups and rooms are also dropped today because audio messages do not carry mention metadata. This PR only enables inbound voice STT for 1:1 chats.
- **@mention gating** — Supported (zero-config). In group/room chats the gateway only dispatches a visible bot reply when the bot is explicitly @-mentioned (LINE's native `mentionees[].isSelf` signal). 1:1 DMs are always forwarded. No env var is needed.
- *Optional short-term text buffering*: when `LINE_GROUP_CONTEXT_ENABLED=true`, unmentioned **text** messages in groups/rooms are observed by the gateway ContextProvider and buffered locally for up to 24 hours. The next directly @-mentioned text turn for the same chat drains that buffer and prepends it as short-term context. The buffer is capped per chat by message count and total characters. This improves conversational continuity without making the bot reply to every group message.
- *Limitation — non-text messages*: LINE only attaches mention data to text messages. Images, videos, stickers, files, and location messages in groups are still dropped when not directly @-mentioned because they do not enter the short-term text buffer.
- *Limitation — group voice messages*: LINE voice/audio messages in groups and rooms are also dropped today because audio messages do not carry mention metadata. LINE inbound voice STT is currently for 1:1 chats.
- *Limitation — `@All`*: A group-wide `@All` mention does **not** trigger the bot; only a direct `@BotName` mention does.
- *Breaking change*: This gating is always active. Deployments that previously relied on the bot responding to all group messages will need to @-mention the bot after upgrading.
- *Behavior note*: the short-term context buffer is local, bounded, temporary, scoped by platform/chat/thread/bot, and drained after injection. It is not a long-term chat archive or GBrain memory store.
- **Markdown rendering** — LINE uses its own text formatting. Agent replies are sent as plain text.
- **External-content images** — LINE image messages backed by `contentProvider.type = "external"` are not downloaded yet.
- **External-content audio** — LINE audio messages backed by `contentProvider.type = "external"` are not downloaded yet.
Expand All @@ -111,6 +112,13 @@ In the LINE Developers Console → **Messaging API** tab → scan the QR code wi
|---|---|---|
| `LINE_CHANNEL_SECRET` | Yes | Channel secret for webhook signature validation |
| `LINE_CHANNEL_ACCESS_TOKEN` | Yes | Channel access token for Reply/Push Message API and LINE-hosted image/audio downloads |
| `LINE_GROUP_CONTEXT_ENABLED` | No | Opt in to buffering unmentioned group/room text for the next direct mention. Falls back to `GATEWAY_CONTEXT_ENABLED`. Default: `false` |
| `LINE_GROUP_CONTEXT_TTL_HOURS` | No | Hours to keep unmentioned group/room text eligible for the next direct mention. Falls back to `GATEWAY_CONTEXT_TTL_HOURS`. Default: `24` |
| `LINE_GROUP_CONTEXT_MAX_MESSAGES` | No | Maximum buffered unmentioned text messages per group/room. Falls back to `GATEWAY_CONTEXT_MAX_MESSAGES`. Default: `50` |
| `LINE_GROUP_CONTEXT_MAX_CHARS` | No | Maximum total buffered text characters per group/room. Falls back to `GATEWAY_CONTEXT_MAX_CHARS`. Default: `8000` |
| `LINE_CONTEXT_BOT_ID` | No | Stable bot identity used for ContextProvider isolation when multiple bots share a LINE group. Falls back to `LINE_BOT_ID`, then `line-default-bot` |

See [Gateway Context Providers](./gateway-context.md) for the shared context buffering model and future API-fetch/hybrid provider direction.

## Troubleshooting

Expand Down
13 changes: 13 additions & 0 deletions gateway/Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions gateway/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ reqwest = { version = "0.12", default-features = false, features = ["rustls-tls"
tracing = "0.1"
tracing-subscriber = { version = "0.3", features = ["env-filter"] }
anyhow = "1"
async-trait = "0.1"
uuid = { version = "1", features = ["v4"] }
chrono = { version = "0.4", features = ["serde"] }
hmac = "0.12"
Expand Down
Loading
Loading