From f9f362c9001677d31231e08488f24f0094dc1a97 Mon Sep 17 00:00:00 2001 From: xcaliber Date: Sun, 22 Feb 2026 11:23:29 +0000 Subject: [PATCH] Changeset 0.8.4 (#47) --- ROADMAP.md | 49 ++++++ VERSION | 2 +- server/handlers/apiconfigs.go | 3 + server/handlers/presets.go | 8 +- src/index.html | 48 +----- src/js/app.js | 294 +++++++++++++++------------------- src/js/ui.js | 176 +++++++++++++++++++- 7 files changed, 361 insertions(+), 219 deletions(-) diff --git a/ROADMAP.md b/ROADMAP.md index fa1726b..839de7e 100644 --- a/ROADMAP.md +++ b/ROADMAP.md @@ -187,6 +187,55 @@ Required for enterprise and compliance. Cheap to build, expensive to retrofit. - [x] Admin audit viewer (filter by action, resource type, paginated) - [ ] Team admin sees audit entries scoped to their team +**Model Visibility (0.8.3)** +- [x] Three-state visibility: enabled (all users) / team (preset-building only) / disabled +- [x] Migration replaces `is_enabled` boolean with `visibility` varchar +- [x] Admin model list: 3-state cycle button, bulk set all to any state +- [x] `GET /teams/:teamId/models` returns enabled+team models for preset builders +- [x] User-facing `ListEnabledModels` strictly filters `visibility = 'enabled'` + +**Preset Form Unification + UX (0.8.4)** +- [x] Single `renderPresetForm(options)` function shared across admin/team contexts + โ€” parameterized: showAvatar, showProviderConfig, onSubmit, onCancel +- [x] Remove icon field from preset forms (not rendered anywhere meaningful) +- [x] Team admin preset form includes avatar upload (reuse admin component) +- [x] Hide Providers tab in Settings when `user_providers_enabled` is false +- [x] Settings Models tab: only show `visibility='enabled'` base models + (team-only models must not leak to user model list; presets filtered out) +- [x] Model provenance labels: badge indicating global vs personal source + โ€” same model_id from both sources shown separately (different API keys) +- [x] Admin presets list: show created_by name + team name for attribution + (e.g. "Code Reviewer ยท ๐Ÿ‘ฅ Engineering ยท by sarah") + +**User Presets + Model Filtering (0.8.5)** +- [ ] Users can create personal presets from any enabled base model + (backend `CreateUserPreset` exists, needs UI surface in Settings) +- [ ] Settings Models tab reworked as "My Models": toggle to hide/show + base models from selector, "+ Create Preset" action per model row +- [ ] `user_model_preferences` table: user_id, model_config_id, hidden boolean + โ€” lightweight filter, not access control +- [ ] User personal provider models: simple enable/disable toggle + (no team dimension โ€” personal keys are personal) + +**Team Providers (0.8.6+)** +- [ ] `team_id` column on `api_configs` (nullable, symmetric with `user_id`) +- [ ] Team admins manage team-scoped provider configs + (add/remove API keys, toggle active, same UI as personal providers) +- [ ] `allow_team_providers` flag in `teams.settings` JSONB + โ€” admin controls which teams can add their own API keys +- [ ] Team provider models use same 3-state visibility: enabled/disabled/team + โ€” enabled: team members see in model selector (direct use) + โ€” team: team admin can use for presets only (not direct member access) + โ€” disabled: hidden from everyone + Same cycle button, same bulk actions, scoped to team admin panel +- [ ] `ListEnabledModels` adds section: team provider models where + `visibility = 'enabled'` AND user is member of owning team +- [ ] Team preset builder shows team provider models where + `visibility IN ('enabled', 'team')` AND user is admin of owning team +- [ ] Three-tier provider hierarchy with uniform visibility semantics: + global (sys-admin) โ†’ team (team-admin) โ†’ personal (user) +- [ ] Model provenance badges: global / team:TeamName / personal + **Usage / Cost Tracking** - [ ] Capture from provider responses: prompt_tokens, completion_tokens, cache_creation_tokens, cache_read_tokens diff --git a/VERSION b/VERSION index ee94dd8..b60d719 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -0.8.3 +0.8.4 diff --git a/server/handlers/apiconfigs.go b/server/handlers/apiconfigs.go index 0ff8d15..60d351a 100644 --- a/server/handlers/apiconfigs.go +++ b/server/handlers/apiconfigs.go @@ -434,6 +434,7 @@ func (h *APIConfigHandler) ListEnabledModels(c *gin.Context) { ConfigID string `json:"config_id"` Capabilities providers.ModelCapabilities `json:"capabilities"` Pricing *providers.ModelPricing `json:"pricing,omitempty"` + Source string `json:"source,omitempty"` IsPreset bool `json:"is_preset,omitempty"` PresetID string `json:"preset_id,omitempty"` PresetScope string `json:"preset_scope,omitempty"` @@ -477,6 +478,7 @@ func (h *APIConfigHandler) ListEnabledModels(c *gin.Context) { } m.Capabilities.MaxOutputTokens = providers.ResolveMaxOutput(m.ModelID, m.Capabilities) + m.Source = "global" models = append(models, m) } } @@ -534,6 +536,7 @@ func (h *APIConfigHandler) ListEnabledModels(c *gin.Context) { ConfigID: cfgID, Capabilities: caps, Pricing: pm.Pricing, + Source: "personal", }) } } diff --git a/server/handlers/presets.go b/server/handlers/presets.go index 432005b..8a4ef58 100644 --- a/server/handlers/presets.go +++ b/server/handlers/presets.go @@ -286,10 +286,12 @@ func (h *PresetHandler) ListAdminPresets(c *gin.Context) { mp.scope, mp.team_id, mp.created_by, mp.is_shared, mp.is_active, mp.icon, mp.avatar, mp.created_at, mp.updated_at, COALESCE(ac.name, '') as provider_name, - COALESCE(u.username, '') as creator_name + COALESCE(u.username, '') as creator_name, + COALESCE(t.name, '') as team_name FROM model_presets mp LEFT JOIN api_configs ac ON mp.api_config_id = ac.id LEFT JOIN users u ON mp.created_by = u.id + LEFT JOIN teams t ON mp.team_id = t.id ORDER BY mp.scope ASC, mp.name ASC `) if err != nil { @@ -301,6 +303,7 @@ func (h *PresetHandler) ListAdminPresets(c *gin.Context) { type adminPreset struct { presetResponse CreatorName string `json:"creator_name"` + TeamName string `json:"team_name"` } presets := make([]adminPreset, 0) @@ -310,7 +313,8 @@ func (h *PresetHandler) ListAdminPresets(c *gin.Context) { &p.ID, &p.Name, &p.Description, &p.BaseModelID, &p.APIConfigID, &p.SystemPrompt, &p.Temperature, &p.MaxTokens, &p.ToolsEnabled, &p.Scope, &p.TeamID, &p.CreatedBy, &p.IsShared, &p.IsActive, - &p.Icon, &p.Avatar, &p.CreatedAt, &p.UpdatedAt, &p.ProviderName, &p.CreatorName, + &p.Icon, &p.Avatar, &p.CreatedAt, &p.UpdatedAt, &p.ProviderName, + &p.CreatorName, &p.TeamName, ); err != nil { continue } diff --git a/src/index.html b/src/index.html index 71c7b65..79a1908 100644 --- a/src/index.html +++ b/src/index.html @@ -222,7 +222,7 @@ @@ -331,22 +331,7 @@

Team Presets

- +
@@ -421,34 +406,7 @@
- +