Changeset 0.28.0.9 (#181)
This commit is contained in:
@@ -3,6 +3,8 @@
|
||||
What models are available to the current user and how they control
|
||||
visibility.
|
||||
|
||||
---
|
||||
|
||||
### Enabled Models
|
||||
|
||||
The primary endpoint for populating model selectors:
|
||||
@@ -11,44 +13,159 @@ The primary endpoint for populating model selectors:
|
||||
GET /models/enabled
|
||||
```
|
||||
|
||||
Returns `{ "models": [UserModel objects] }`:
|
||||
Returns a composite response:
|
||||
|
||||
```json
|
||||
{
|
||||
"data": [ UserModel, ... ],
|
||||
"default_model": "claude-sonnet-4-20250514"
|
||||
}
|
||||
```
|
||||
|
||||
`default_model` is the admin-configured default model ID (from the
|
||||
`default_model` policy). Empty string if unset.
|
||||
|
||||
**UserModel object:**
|
||||
|
||||
```json
|
||||
{
|
||||
"id": "composite-id",
|
||||
"model_id": "claude-sonnet-4-20250514",
|
||||
"provider_config_id": "uuid",
|
||||
"provider_config_name": "Anthropic",
|
||||
"provider": "anthropic",
|
||||
"display_name": "Claude Sonnet 4",
|
||||
"model_type": "chat|embedding|image",
|
||||
"context_window": 200000,
|
||||
"max_output_tokens": 8192,
|
||||
"supports_vision": true,
|
||||
"supports_tools": true,
|
||||
"supports_thinking": true,
|
||||
"supports_streaming": true,
|
||||
"input_price_per_m": 3.00,
|
||||
"output_price_per_m": 15.00,
|
||||
"provider_status": "healthy|degraded|down|null",
|
||||
"scope": "global|team|personal",
|
||||
"source": "catalog|heuristic",
|
||||
"model_id": "claude-sonnet-4-20250514",
|
||||
"model_type": "chat",
|
||||
"source": "catalog",
|
||||
|
||||
"provider_config_id": "uuid",
|
||||
"config_id": "uuid",
|
||||
"provider_name": "Anthropic",
|
||||
"provider_type": "anthropic",
|
||||
|
||||
"capabilities": {
|
||||
"streaming": true,
|
||||
"tool_calling": true,
|
||||
"vision": true,
|
||||
"thinking": true,
|
||||
"reasoning": false,
|
||||
"code_optimized": false,
|
||||
"web_search": false,
|
||||
"max_context": 200000,
|
||||
"max_output_tokens": 8192
|
||||
},
|
||||
|
||||
"is_persona": false,
|
||||
"persona_id": "uuid|null",
|
||||
"persona_scope": "global|team|personal|null",
|
||||
"persona_avatar": "url|null",
|
||||
"persona_team_name": "string|null"
|
||||
"persona_id": "",
|
||||
"persona_handle": "",
|
||||
"persona_scope": "",
|
||||
"persona_avatar": "",
|
||||
"persona_team_name": "",
|
||||
|
||||
"description": "",
|
||||
"icon": "",
|
||||
"avatar": "",
|
||||
"system_prompt": "",
|
||||
"temperature": null,
|
||||
"max_tokens": null,
|
||||
"tool_grants": [],
|
||||
|
||||
"pricing": {
|
||||
"input_per_m": 3.00,
|
||||
"output_per_m": 15.00,
|
||||
"currency": "USD"
|
||||
},
|
||||
|
||||
"scope": "global",
|
||||
"owner_id": null,
|
||||
"team_name": "",
|
||||
|
||||
"hidden": false,
|
||||
"sort_order": 0,
|
||||
|
||||
"provider_status": "healthy"
|
||||
}
|
||||
```
|
||||
|
||||
The capabilities on each model are resolved through the three-tier
|
||||
chain: catalog DB → heuristic inference → admin overrides (see §10.5).
|
||||
**Field reference:**
|
||||
|
||||
When `is_persona` is `true`, the capability fields (`context_window`,
|
||||
`max_output_tokens`, `supports_*`, pricing, `provider_status`) are
|
||||
inherited from the persona's underlying model. The frontend renders
|
||||
the same capability pills and badges for a persona as for its raw
|
||||
model — the persona adds identity, not capability restrictions.
|
||||
| Field | Type | Description |
|
||||
|-------|------|-------------|
|
||||
| `id` | string | Composite key. Catalog: `{config_id}:{model_id}`. Persona: persona UUID. |
|
||||
| `display_name` | string | Human-readable name (from catalog or persona). |
|
||||
| `model_id` | string | Raw model identifier (e.g. `claude-sonnet-4-20250514`). |
|
||||
| `model_type` | string | `"chat"`, `"embedding"`, `"image"`, etc. |
|
||||
| `source` | string | `"catalog"` (DB catalog entry) or `"persona"`. |
|
||||
| `provider_config_id` | string | UUID of the provider config that serves this model. |
|
||||
| `config_id` | string | Alias of `provider_config_id` (frontend compat). |
|
||||
| `provider_name` | string | Display name of the provider config. |
|
||||
| `provider_type` | string | Provider type slug (e.g. `"anthropic"`, `"openai"`). |
|
||||
| `capabilities` | object | Nested `ModelCapabilities` — see below. |
|
||||
| `is_persona` | bool | `true` if this entry is a Persona, not a raw model. |
|
||||
| `persona_id` | string | Persona UUID (omitted if not a persona). |
|
||||
| `persona_handle` | string | `@handle` for mention routing (omitted if not a persona). |
|
||||
| `persona_scope` | string | Persona scope: `"global"`, `"team"`, `"personal"` (omitted if not). |
|
||||
| `persona_avatar` | string | Avatar URL (omitted if not a persona or unset). |
|
||||
| `persona_team_name` | string | Owning team name for team-scoped personas (omitted if N/A). |
|
||||
| `description` | string | Persona description (omitted if empty). |
|
||||
| `icon` | string | Persona icon (omitted if empty). |
|
||||
| `avatar` | string | Avatar URL (omitted if empty). |
|
||||
| `system_prompt` | string | Persona system prompt (omitted if empty). |
|
||||
| `temperature` | float\|null | Persona temperature override. |
|
||||
| `max_tokens` | int\|null | Persona max-tokens override. |
|
||||
| `tool_grants` | string[] | Tool IDs granted to this persona. |
|
||||
| `pricing` | object\|null | Nested `ModelPricing` — see below. Omitted if unavailable. |
|
||||
| `scope` | string | `"global"`, `"team"`, or `"personal"`. |
|
||||
| `owner_id` | string\|null | Owner user/team ID for team/personal scoped entries. |
|
||||
| `team_name` | string | Team name (persona entries only). |
|
||||
| `hidden` | bool | `true` if the user has hidden this model via preferences. |
|
||||
| `sort_order` | int | User's custom sort position (0 = default). |
|
||||
| `provider_status` | string | Health status: `"healthy"`, `"degraded"`, `"down"`, `"unknown"`, or empty. |
|
||||
|
||||
**ModelCapabilities:**
|
||||
|
||||
| Field | Type | Description |
|
||||
|-------|------|-------------|
|
||||
| `streaming` | bool | Supports streaming responses. |
|
||||
| `tool_calling` | bool | Supports function/tool calling. |
|
||||
| `vision` | bool | Supports image inputs. |
|
||||
| `thinking` | bool | Supports extended thinking. |
|
||||
| `reasoning` | bool | Reasoning-optimized model. |
|
||||
| `code_optimized` | bool | Code-optimized model. |
|
||||
| `web_search` | bool | Has built-in web search. |
|
||||
| `max_context` | int | Context window size in tokens. |
|
||||
| `max_output_tokens` | int | Maximum output tokens. |
|
||||
|
||||
Capabilities are resolved through the three-tier chain: catalog DB →
|
||||
heuristic inference → admin overrides (see `providers.md` §capabilities).
|
||||
|
||||
**ModelPricing:**
|
||||
|
||||
| Field | Type | Description |
|
||||
|-------|------|-------------|
|
||||
| `input_per_m` | float | Input cost per million tokens. |
|
||||
| `output_per_m` | float | Output cost per million tokens. |
|
||||
| `currency` | string | Currency code (e.g. `"USD"`). |
|
||||
|
||||
**Persona inheritance:** When `is_persona` is `true`, the `capabilities`
|
||||
and `pricing` objects are inherited from the persona's underlying base
|
||||
model. The persona adds identity (name, handle, avatar, system prompt,
|
||||
tool grants) — not capability restrictions. Persona entries always have
|
||||
`hidden: false`; persona visibility is controlled by the grant system
|
||||
and the `is_active` flag, not by model preferences.
|
||||
|
||||
**Model allowlist filtering (v0.24.2):** Non-admin users whose groups
|
||||
define model restrictions will only see models in their allowlist.
|
||||
Persona entries whose underlying `model_id` is not in the allowlist are
|
||||
also filtered. Admins bypass this filter entirely.
|
||||
|
||||
**Visibility resolution order:**
|
||||
|
||||
1. Global catalog: enabled models → all authenticated users
|
||||
2. Team catalog: enabled models → team members
|
||||
3. Personal BYOK: enabled models → owner (requires `allow_user_byok` policy)
|
||||
4. Personas: global + team + personal + shared-via-grants
|
||||
5. User hidden preferences applied last (sets `hidden` flag, does not remove)
|
||||
6. Model allowlist filtering (removes entries entirely for non-admins)
|
||||
|
||||
---
|
||||
|
||||
### User Model Preferences
|
||||
|
||||
@@ -58,31 +175,84 @@ defaults. Preferences are keyed on the **composite identity**
|
||||
providers can have independent visibility.
|
||||
|
||||
```
|
||||
GET /models/preferences → { "preferences": [PreferenceEntry objects] }
|
||||
PUT /models/preferences ← { "model_id": "...", "provider_config_id": "uuid", "hidden": true }
|
||||
POST /models/preferences/bulk ← { "entries": [{ "model_id": "...", "provider_config_id": "uuid", "hidden": true }] }
|
||||
GET /models/preferences → { "data": [PreferenceEntry, ...] }
|
||||
PUT /models/preferences ← { "model_id": "...", "provider_config_id": "uuid", ... }
|
||||
POST /models/preferences/bulk ← { "entries": [...], "hidden": bool }
|
||||
```
|
||||
|
||||
**GET /models/preferences**
|
||||
|
||||
Returns all preference entries for the authenticated user.
|
||||
|
||||
PreferenceEntry:
|
||||
|
||||
```json
|
||||
{
|
||||
"id": "uuid",
|
||||
"user_id": "uuid",
|
||||
"model_id": "grok-4.1-fast",
|
||||
"provider_config_id": "uuid",
|
||||
"hidden": true,
|
||||
"preferred_temperature": 0.7,
|
||||
"preferred_max_tokens": 4096,
|
||||
"sort_order": 0
|
||||
"sort_order": 0,
|
||||
"created_at": "2025-06-15T14:30:00Z",
|
||||
"updated_at": "2025-06-15T14:30:00Z"
|
||||
}
|
||||
```
|
||||
|
||||
**PUT /models/preferences**
|
||||
|
||||
Upserts a single preference entry. All fields except `model_id` are
|
||||
optional — only provided fields are updated. If no entry exists, one
|
||||
is created.
|
||||
|
||||
Request body:
|
||||
|
||||
```json
|
||||
{
|
||||
"model_id": "claude-sonnet-4-20250514",
|
||||
"provider_config_id": "uuid",
|
||||
"hidden": true,
|
||||
"preferred_temperature": 0.7,
|
||||
"preferred_max_tokens": 4096,
|
||||
"sort_order": 1
|
||||
}
|
||||
```
|
||||
|
||||
`model_id` and `provider_config_id` are required. All other fields are
|
||||
optional — only provided fields are updated. If no entry exists, one
|
||||
is created.
|
||||
|
||||
Returns `{ "message": "preference updated" }`.
|
||||
|
||||
**POST /models/preferences/bulk**
|
||||
|
||||
Sets the hidden state for multiple model+provider pairs at once. The
|
||||
`hidden` value is applied to **all** entries in the batch — this is
|
||||
not per-entry hidden control.
|
||||
|
||||
Request body:
|
||||
|
||||
```json
|
||||
{
|
||||
"entries": [
|
||||
{ "model_id": "grok-4.1-fast", "provider_config_id": "uuid" },
|
||||
{ "model_id": "llama-3.1-70b", "provider_config_id": "uuid" }
|
||||
],
|
||||
"hidden": true
|
||||
}
|
||||
```
|
||||
|
||||
Returns `{ "message": "preferences updated", "count": 2 }`.
|
||||
|
||||
**Identity rule:** `provider_config_id` is required on write. The same
|
||||
bare `model_id` from two different provider configs (e.g. global Venice
|
||||
vs personal BYOK Venice) are independent preference entries. The
|
||||
frontend composite ID format is `{provider_config_id}:{model_id}`.
|
||||
|
||||
**Persona preferences:** Personas are not in this table. A persona's
|
||||
visibility is controlled by the grant system (§15.3) and the
|
||||
`is_active` flag, not by model preferences.
|
||||
visibility is controlled by the grant system and the `is_active` flag,
|
||||
not by model preferences.
|
||||
|
||||
---
|
||||
|
||||
Reference in New Issue
Block a user