docs(tools): tenant tool config refactor - phases 7-8 completion

- 03-tools-system.md: Add § 14 Per-Tenant Tool Configuration (4-tier overlay)
  - Comprehensive overlay explanation (per-agent > tenant > global > hardcoded)
  - Opt-in pattern for tool authors with code example
  - Schema contracts for web_search (provider_order), web_fetch (policy), tts (primary)
  - Secret vs non-secret split guidance
  - Tenant admin workflow (Settings → Builtin Tools UI)
  - Feature flag documentation (TenantScopedSingletons)

- 17-changelog.md: Expand Per-Tenant Tool Configuration entry with phase details
  - Phase 5: Builtin tools settings editor on web UI
  - Phase 7 rest (30a40bbe): Exa + Tavily web search providers
    - Credit @kaitranntt for PR 825 original work
    - 11 new unit tests, provider_order config
  - Phase 8 (def1712f, 43ee918b): Tenant-aware singleton pools
    - web_fetch domain policy resolver (6 new tests)
    - tts primary provider resolver (5 new tests)
    - Feature flag gating (LRU pool, 64 tenant limit, 30 min idle timeout)

All phases 1-8 now shipped on dev branch. Docs integrated with existing
23-multi-tenant-architecture.md § 14 reference. Ready for Phase 9 completion.
This commit is contained in:
viettranx committed 2026-04-12 11:25:12 +07:00
1 parent 43ee918b9e
commit 3a8470b8da
2 files changed
+89 -1

No files matched your search

+79
View File
@@ -757,6 +757,85 @@ Tenant-scoped writes use the symmetric `requireTenantAdmin(w, r, tenantStore)` i
---
## 14. Per-Tenant Tool Configuration (4-Tier Overlay)
Tenant admins can override tool config without affecting other tenants or master. Overlay priority (most specific wins):
```
Tool call Execute(ctx, params)
↓
BuiltinToolSettingsFromCtx(ctx) returns merged map:
1. Per-agent override (agents.builtin_tool_settings; reserved for future)
2. Tenant override (builtin_tool_tenant_configs.settings column)
3. Global default (builtin_tools.settings column, resolver-loaded)
4. Hardcoded fallback (tool internal default when no entry exists)
```
Each tool that needs tenant-aware config calls `BuiltinToolSettingsFromCtx(ctx)` and unmarshals its settings key. Merge happens at tool-name level: tenant entry for `web_search` wins wholesale over global, no deep field-level merge.
**Opt-in pattern** — Any tool can participate by:
```go
func (t *MyTool) Execute(ctx context.Context, params MyParams) (*Result, error) {
settings := tools.BuiltinToolSettingsFromCtx(ctx)
var cfg MyToolConfig = t.defaults
if raw, ok := settings["my_tool"]; ok {
if err := json.Unmarshal(raw, &cfg); err != nil {
slog.Warn("my_tool: invalid tenant settings, using defaults", "error", err)
}
}
return t.doWork(ctx, cfg, params)
}
```
### Schema Contracts
#### web_search (tenant override shape)
```json
{
"provider_order": ["exa", "tavily", "brave", "duckduckgo"],
"exa": { "enabled": true, "max_results": 10 },
"tavily": { "enabled": true, "max_results": 5 },
"brave": { "enabled": true, "max_results": 5 },
"duckduckgo": { "enabled": true, "max_results": 10 }
}
```
#### web_fetch (tenant override shape)
```json
{
"policy": "allow_all",
"allowed_domains": ["github.com", "*.example.com"],
"blocked_domains": ["malicious.com"]
}
```
#### tts (tenant override shape)
```json
{
"primary": "elevenlabs"
}
```
**Secret vs non-secret split:**
- Non-secret (provider priorities, max_results, allowed_domains): `builtin_tool_tenant_configs.settings` (editable via UI)
- Secret (API keys): `config_secrets` table (encrypted, tenant-scoped)
Never put credentials in tool settings JSON — backend does not validate.
### Tenant Admin Workflow
1. Log in as tenant admin → Settings → Builtin Tools
2. Click gear icon next to a tool → JSON editor opens
3. Edit settings per schema contracts above → Save
4. Changes take effect immediately on next agent turn (cache invalidated via pub/sub)
5. Click "Reset to default" to revert to platform defaults
### Feature Flag: Tenant-Scoped Singleton Pools
`config.Tools.TenantScopedSingletons` (default: false) gates Phase 8 refactor — per-tenant connection pool instances for stateful tools (TTS manager, web_fetch policy validator). Disable for legacy singleton behavior. Enable after 1-week staging smoke test.
---
## File Reference
### Core Infrastructure
+10 -1
View File
@@ -48,7 +48,16 @@ Fix adds shared `store.IsMasterScope(ctx)` predicate + `http.requireMasterScope`
#### Per-Tenant Tool Configuration — 4-Tier Overlay (2026-04-12)
Tenant admins can override tool configuration without affecting other tenants. Overlay: `per-agent > tenant > global > hardcoded`, resolved at Execute time via `tools.BuiltinToolSettingsFromCtx(ctx)` — no Tool interface changes. See `docs/03-tools-system.md` § 14. Applies to `web_search`, media tools, `web_fetch`, `knowledge_graph_search`. Web UI dialog is tenant-scope aware. Pending: Exa/Tavily provider port (Phase 7 rest), `web_fetch`/`tts` singleton refactor (Phase 8).
Tenant admins can override tool configuration without affecting other tenants. Overlay: `per-agent > tenant > global > hardcoded`, resolved at Execute time via `tools.BuiltinToolSettingsFromCtx(ctx)` — no Tool interface changes. See `docs/03-tools-system.md` § 14. Applies to `web_search`, `web_fetch`, `tts`. Web UI dialog is tenant-scope aware.
- **Phase 5** (1e5e84d5): Builtin tools settings editor on web UI
- **Phase 7 rest** (30a40bbe): Exa + Tavily web search providers with ranked ordering via `provider_order` config. Credit: @kaitranntt for original PR 825 work, ported to tenant settings storage pattern
- New files: `internal/tools/{web_search_exa,web_search_tavily,web_search_config}.go`
- 11 new unit tests for provider chain + normalization
- Provider-selection helper: `NormalizeWebSearchProviderOrder(order []string) → []string` (DuckDuckGo always last as free fallback)
- **Phase 8** (def1712f, 43ee918b): Tenant-aware singleton pool pattern for stateful tools
- `web_fetch`: Domain policy override via `resolvePolicy(ctx)` reads tenant config; 6 new unit tests
- `tts`: Primary provider override via `resolvePrimary(ctx, mgr)` reads tenant config; 5 new unit tests
- Feature flag: `config.Tools.TenantScopedSingletons` (default: false) gates per-tenant pool instances with LRU eviction (64 tenants) + 30 min idle timeout
### Fixed