aaif-goose / aaif-goose/goose

Design: per-request nonce header for declarative providers

Đang mở
#11,308 7 bình luận 0 reaction 0 người được giao Xem trên GitHub
Ngôn ngữ chính
Rust
Star
54.2k
Fork
6.2k
Merge trung bình
3 ngày 4 giờ
Pull request đã merge (30 ngày)
240

Mô tả

Records the design behind #11302, per @michaelneale's request. One open question for maintainers at the bottom: where config-field validation should live given several construction paths bypass from_json.

## Problem

A proxy or sidecar sitting in front of an OpenAI-compatible endpoint often needs a **per-request unique value** — idempotency keys, replay-window dedup, request correlation. Declarative (custom) providers have no way to supply one.

`DeclarativeProviderConfig.headers` is resolved once at provider init, so it can carry a constant but never a fresh value. Meanwhile every built-in provider can reach `ApiClient::with_request_builder` and install a per-request decorator; `from_declarative_config` is the only construction path that cannot. So this is a gap rather than a new capability.

## Design

One optional config field, `nonce_header: Option`, on `DeclarativeProviderConfig`.

- **The header name is supplied by config, never hardcoded.** No consumer's vocabulary enters goose.
- **Absent means no-op** — no decorator installed, no code path taken.
- Present installs a decorator inserting a fresh UUIDv4 (122 bits CSPRNG) under that name.
- The name is parsed **once at config load**, so a typo fails immediately with a message naming thw key rather than turning every subsequent request into an opaque header-parse error.

A general dynamic-header or templating mechanism was deliberately not attempted. The nonce is the concrete need; the decorator seam leaves generalization open if one ever turns up.

## Things the review surfaced, and how they resolved

These are the parts worth keeping, since they're where the design actually got decided.

**Decorators compose now.** `ApiClient::request_builder` was a single `Option` that
`with_request_builder` replaced. The registry path installs a second decorator —
`openai_def::from_custom_config` adds the session-id builder right after `from_declarative_config`
returns — which silently discarded the nonce, so CLI and Desktop would never have sent it. The slot is a `Vec` now and the setter appends; decorators run in installation order. Every existing caller is unaffected since each installs one.

Worth recording why the tests didn't catch it: they called `from_declarative_config` directly and asserted on the wire, so they exercised the builder but not the registry path that wraps it. A test through `openai_def::from_custom_config` asserting both headers arrive is the right regression pin, added in #11302.

**Reserved names are rejected.** `agent-session-id` is removed and rewritten by
`session_id_request_builder`, and `ApiClient::send_request` applies authentication *after* every decorator — so `authorization` and `proxy-authorization` would be overwritten too. All three now fail at config load with an error explaining why, rather than silently carrying nothing. A name colliding with a provider's own configured `ApiKey` auth header has the same problem but isn't statically knowable there; that one is documented on the field.

**Scope is the OpenAI declarative builder only.** `from_json` dispatches to the anthropic and ollama builders too, and they don't install the decorator. An earlier revision rejected the field for those engines in `from_json` — but the registry doesn't go through `from_json`: `load_custom_providers`deserializes and the registry calls engine-specific `from_custom_config` directly, and an openai-engine config named `huggingface` routes to `HuggingFaceProvider::from_custom_config`, which builds its own `ApiClient`. So that guard was dead code on the path that matters and has been removed in favour of documenting the limitation on the field itself.

**That last one is the open design question**, and it's the reason this issue is worth having:
**where should validation of provider-config fields live, given several construction paths bypass `from_json`?** Options I can see are a shared validation step in the registry dispatch in `load_custom_providers`, installing the decorator in each specialised constructor, or keeping it builder-scoped and documented. Happy to implement whichever you prefer — I've guessed wrong twice, so I'd rather have the maintainers' answer than a third guess.

## Semver note

Adding a field to `DeclarativeProviderConfig` breaks struct-literal construction for out-of-tree callers — deserialization is fine thanks to `#[serde(default)]`. Seven in-repo sites needed updating, three of them in `goose/src/config/declarative_providers.rs` and two of those non-test. If `#[non_exhaustive]` or a builder is preferred, glad to follow.

Separately, `ApiClient::with_request_builder` changed from replace-on-set to append. Same signature, different behaviour for any out-of-tree caller that installed twice expecting replacement — unlikely to exist, but semver-relevant if `goose-providers` is published.

## Consumer

Mesh-LLM's local sidecar, so the *client* rather than the intermediary contributes the per-request nonce — Mesh-LLM/mesh-llm#1233. The companion Mesh-LLM PR is also mine and sets the key in the `mesh.json` that `mesh-llm goose` generates.

Implementation: #11302.

Hướng dẫn đóng góp

Mở hướng dẫn đóng góp

Hướng nghiên cứu

Issue mô tả việc thêm trường `nonce_header` vào `DeclarativeProviderConfig`. Trước tiên, hãy xem xét tệp `goose/src/config/declarative_providers.rs` để hiểu cấu trúc config. Xem `ApiClient::with_request_builder` và mẫu decorator của request builder. Xem lại phần triển khai trong PR #11302 để có thêm ngữ cảnh. Công việc bao gồm cập nhật config, thêm validation cho các tên header dành riêng và đảm bảo decorator được kết hợp đúng trong registry path. Việc kiểm thử nên bao gồm path `openai_def::from_custom_config` để xác minh rằng các header được gửi.

Do mô hình lập chỉ mục viết ra từ nội dung của issue.

Đánh giá

Công nghệ
rust
Lĩnh vực
backend-api-design
Loại issue
Tính năng
Độ khó
4/5
Thời gian dự kiến
3-5 ngày
Mức độ hoạt động
Sôi nổi
Độ rõ ràng
Đặc tả rõ ràng
Mức phù hợp với người mới
40/100

Nhận issue mới trong hộp thư của bạn

Bản tóm tắt ngắn những issue GitHub phù hợp với người mới.