Changeset 0.23.0 (#153)

This commit is contained in:
2026-03-05 22:40:26 +00:00
parent 40d9834f64
commit 2fc620e1ac
62 changed files with 6214 additions and 362 deletions

View File

@@ -3,6 +3,7 @@ package models
import (
"database/sql"
"encoding/json"
"strings"
"time"
)
@@ -128,6 +129,8 @@ type ProviderConfig struct {
Settings JSONMap `json:"settings,omitempty" db:"settings"`
IsActive bool `json:"is_active" db:"is_active"`
IsPrivate bool `json:"is_private" db:"is_private"`
ProxyMode string `json:"proxy_mode" db:"proxy_mode"`
ProxyURL *string `json:"proxy_url,omitempty" db:"proxy_url"`
}
// HasKey returns true if an encrypted API key is stored.
@@ -146,6 +149,8 @@ type ProviderConfigPatch struct {
Settings JSONMap `json:"settings,omitempty"`
IsActive *bool `json:"is_active,omitempty"`
IsPrivate *bool `json:"is_private,omitempty"`
ProxyMode *string `json:"proxy_mode,omitempty"`
ProxyURL *string `json:"proxy_url,omitempty"`
}
// =========================================
@@ -194,6 +199,7 @@ type ModelPricing struct {
type Persona struct {
BaseModel
Name string `json:"name" db:"name"`
Handle string `json:"handle,omitempty" db:"handle"`
Description string `json:"description,omitempty" db:"description"`
Icon string `json:"icon,omitempty" db:"icon"`
Avatar string `json:"avatar,omitempty" db:"avatar"`
@@ -227,6 +233,7 @@ type Persona struct {
type PersonaPatch struct {
Name *string `json:"name,omitempty"`
Handle *string `json:"handle,omitempty"`
Description *string `json:"description,omitempty"`
Icon *string `json:"icon,omitempty"`
Avatar *string `json:"avatar,omitempty"`
@@ -278,6 +285,7 @@ type UserModelSetting struct {
BaseModel
UserID string `json:"user_id" db:"user_id"`
ModelID string `json:"model_id" db:"model_id"`
ProviderConfigID *string `json:"provider_config_id,omitempty" db:"provider_config_id"`
Hidden bool `json:"hidden" db:"hidden"`
PreferredTemperature *float64 `json:"preferred_temperature,omitempty" db:"preferred_temperature"`
PreferredMaxTokens *int `json:"preferred_max_tokens,omitempty" db:"preferred_max_tokens"`
@@ -285,12 +293,24 @@ type UserModelSetting struct {
}
type UserModelSettingPatch struct {
ProviderConfigID *string `json:"provider_config_id,omitempty"`
Hidden *bool `json:"hidden,omitempty"`
PreferredTemperature *float64 `json:"preferred_temperature,omitempty"`
PreferredMaxTokens *int `json:"preferred_max_tokens,omitempty"`
SortOrder *int `json:"sort_order,omitempty"`
}
// HiddenEntry identifies a model+provider pair for bulk visibility operations.
type HiddenEntry struct {
ModelID string `json:"model_id"`
ProviderConfigID string `json:"provider_config_id"`
}
// CompositeModelKey builds the composite key used for per-provider model preferences.
func CompositeModelKey(providerConfigID, modelID string) string {
return providerConfigID + ":" + modelID
}
// =========================================
// CHANNELS
// =========================================
@@ -319,41 +339,47 @@ type Channel struct {
type Message struct {
BaseModel
ChannelID string `json:"channel_id" db:"channel_id"`
Role string `json:"role" db:"role"`
Content string `json:"content" db:"content"`
Model string `json:"model,omitempty" db:"model"`
TokensUsed int `json:"tokens_used,omitempty" db:"tokens_used"`
ToolCalls JSONMap `json:"tool_calls,omitempty" db:"tool_calls"`
Metadata JSONMap `json:"metadata,omitempty" db:"metadata"`
ParentID *string `json:"parent_id,omitempty" db:"parent_id"`
SiblingIndex int `json:"sibling_index" db:"sibling_index"`
ParticipantType string `json:"participant_type,omitempty" db:"participant_type"`
ParticipantID string `json:"participant_id,omitempty" db:"participant_id"`
DeletedAt *time.Time `json:"deleted_at,omitempty" db:"deleted_at"`
ChannelID string `json:"channel_id" db:"channel_id"`
Role string `json:"role" db:"role"`
Content string `json:"content" db:"content"`
Model string `json:"model,omitempty" db:"model"`
TokensUsed int `json:"tokens_used,omitempty" db:"tokens_used"`
ToolCalls JSONMap `json:"tool_calls,omitempty" db:"tool_calls"`
Metadata JSONMap `json:"metadata,omitempty" db:"metadata"`
ParentID *string `json:"parent_id,omitempty" db:"parent_id"`
SiblingIndex int `json:"sibling_index" db:"sibling_index"`
ParticipantType string `json:"participant_type,omitempty" db:"participant_type"`
ParticipantID string `json:"participant_id,omitempty" db:"participant_id"`
ProviderConfigID *string `json:"provider_config_id,omitempty" db:"provider_config_id"`
DeletedAt *time.Time `json:"deleted_at,omitempty" db:"deleted_at"`
}
// =========================================
// CHANNEL MEMBERS, MODELS, CURSORS
// CHANNEL PARTICIPANTS, MODELS, CURSORS
// =========================================
type ChannelMember struct {
ID string `json:"id" db:"id"`
ChannelID string `json:"channel_id" db:"channel_id"`
UserID string `json:"user_id" db:"user_id"`
Role string `json:"role" db:"role"`
JoinedAt time.Time `json:"joined_at" db:"joined_at"`
LastReadAt time.Time `json:"last_read_at" db:"last_read_at"`
type ChannelParticipant struct {
ID string `json:"id" db:"id"`
ChannelID string `json:"channel_id" db:"channel_id"`
ParticipantType string `json:"participant_type" db:"participant_type"`
ParticipantID string `json:"participant_id" db:"participant_id"`
Role string `json:"role" db:"role"`
DisplayName *string `json:"display_name,omitempty" db:"display_name"`
AvatarURL *string `json:"avatar_url,omitempty" db:"avatar_url"`
JoinedAt time.Time `json:"joined_at" db:"joined_at"`
LastReadAt time.Time `json:"last_read_at" db:"last_read_at"`
}
type ChannelModel struct {
ID string `json:"id" db:"id"`
ChannelID string `json:"channel_id" db:"channel_id"`
ModelID string `json:"model_id" db:"model_id"`
ProviderConfigID string `json:"provider_config_id,omitempty" db:"provider_config_id"`
DisplayName string `json:"display_name,omitempty" db:"display_name"`
SystemPrompt string `json:"system_prompt,omitempty" db:"system_prompt"`
IsDefault bool `json:"is_default" db:"is_default"`
ID string `json:"id" db:"id"`
ChannelID string `json:"channel_id" db:"channel_id"`
ModelID string `json:"model_id" db:"model_id"`
ProviderConfigID string `json:"provider_config_id,omitempty" db:"provider_config_id"`
PersonaID *string `json:"persona_id,omitempty" db:"persona_id"`
Handle string `json:"handle,omitempty" db:"handle"`
DisplayName string `json:"display_name,omitempty" db:"display_name"`
SystemPrompt string `json:"system_prompt,omitempty" db:"system_prompt"`
IsDefault bool `json:"is_default" db:"is_default"`
}
type ChannelCursor struct {
@@ -364,6 +390,56 @@ type ChannelCursor struct {
UpdatedAt time.Time `json:"updated_at" db:"updated_at"`
}
// =========================================
// PERSONA GROUPS (v0.23.0)
// =========================================
type PersonaGroup struct {
BaseModel
Name string `json:"name" db:"name"`
Description string `json:"description,omitempty" db:"description"`
OwnerID string `json:"owner_id" db:"owner_id"`
Scope string `json:"scope" db:"scope"`
TeamID *string `json:"team_id,omitempty" db:"team_id"`
Members []PersonaGroupMember `json:"members,omitempty" db:"-"`
}
type PersonaGroupMember struct {
ID string `json:"id" db:"id"`
GroupID string `json:"group_id" db:"group_id"`
PersonaID string `json:"persona_id" db:"persona_id"`
IsLeader bool `json:"is_leader" db:"is_leader"`
SortOrder int `json:"sort_order" db:"sort_order"`
// Joined from personas table for API responses
PersonaName string `json:"persona_name,omitempty" db:"-"`
PersonaHandle string `json:"persona_handle,omitempty" db:"-"`
PersonaAvatar string `json:"persona_avatar,omitempty" db:"-"`
}
// HandleFromName generates a URL-safe @mention handle from a display name.
// "Veronica Sharpe" → "veronica-sharpe"
func HandleFromName(name string) string {
h := strings.ToLower(strings.TrimSpace(name))
h = strings.Map(func(r rune) rune {
if (r >= 'a' && r <= 'z') || (r >= '0' && r <= '9') || r == '-' {
return r
}
if r == ' ' || r == '_' {
return '-'
}
return -1
}, h)
// Collapse multiple hyphens
for strings.Contains(h, "--") {
h = strings.ReplaceAll(h, "--", "-")
}
h = strings.Trim(h, "-")
if len(h) > 50 {
h = h[:50]
}
return h
}
// =========================================
// ORGANIZATION
// =========================================
@@ -635,6 +711,7 @@ type UserModel struct {
// Persona fields — always emitted so frontend can branch on is_persona.
IsPersona bool `json:"is_persona"`
PersonaID string `json:"persona_id,omitempty"`
PersonaHandle string `json:"persona_handle,omitempty"`
PersonaScope string `json:"persona_scope,omitempty"`
PersonaAvatar string `json:"persona_avatar,omitempty"`
PersonaTeamName string `json:"persona_team_name,omitempty"`