step 3: gut store interfaces and models
- interfaces.go: 1449 → 437 lines (13 kernel interfaces from 33) - models.go: 1137 → 397 lines (17 kernel types from 64) - Deleted 28 store impl files per dialect (PG + SQLite) - Deleted 3 model files (git, memory, workspace) - Deleted 3 store iface files (project, memory, tree_types) - Rewrote stores.go constructors for both PG and SQLite - Stores struct: 20 fields (from 40+, includes separate iface stores) 66 files changed, ~19K lines removed. Build will break — expected, fixed in steps 4-5.
This commit is contained in:
@@ -23,52 +23,6 @@ const (
|
||||
ScopePersonal = "personal"
|
||||
)
|
||||
|
||||
// ── Visibility Constants ────────────────────
|
||||
|
||||
const (
|
||||
VisibilityVisible = "visible"
|
||||
VisibilityHidden = "hidden"
|
||||
)
|
||||
|
||||
// ── Role Constants ──────────────────────────
|
||||
|
||||
const (
|
||||
UserRoleUser = "user"
|
||||
UserRoleAdmin = "admin"
|
||||
|
||||
TeamRoleAdmin = "admin"
|
||||
TeamRoleMember = "member"
|
||||
)
|
||||
|
||||
// ── Grant Type Constants ────────────────────
|
||||
|
||||
const (
|
||||
GrantTypeTool = "tool"
|
||||
GrantTypeKnowledgeBase = "knowledge_base"
|
||||
GrantTypeAPIEndpoint = "api_endpoint"
|
||||
)
|
||||
|
||||
// ── Channel Type Constants ──────────────────
|
||||
|
||||
const (
|
||||
ChannelTypeDirect = "direct"
|
||||
ChannelTypeGroup = "group"
|
||||
ChannelTypeChannel = "channel"
|
||||
)
|
||||
|
||||
// ── Message Role Constants ──────────────────
|
||||
|
||||
const (
|
||||
MessageRoleUser = "user"
|
||||
MessageRoleAssistant = "assistant"
|
||||
MessageRoleSystem = "system"
|
||||
MessageRoleTool = "tool"
|
||||
)
|
||||
|
||||
// =========================================
|
||||
// USERS
|
||||
// =========================================
|
||||
|
||||
type User struct {
|
||||
BaseModel
|
||||
Username string `json:"username" db:"username"`
|
||||
@@ -113,146 +67,21 @@ type TeamMember struct {
|
||||
UserRole string `json:"user_role,omitempty"`
|
||||
}
|
||||
|
||||
// =========================================
|
||||
// PROVIDER CONFIGS (replaces APIConfig)
|
||||
// =========================================
|
||||
|
||||
type ProviderConfig struct {
|
||||
BaseModel
|
||||
Scope string `json:"scope" db:"scope"`
|
||||
OwnerID *string `json:"owner_id,omitempty" db:"owner_id"`
|
||||
Name string `json:"name" db:"name"`
|
||||
Provider string `json:"provider" db:"provider"`
|
||||
Endpoint string `json:"endpoint" db:"endpoint"`
|
||||
APIKeyEnc []byte `json:"-" db:"api_key_enc"`
|
||||
KeyNonce []byte `json:"-" db:"key_nonce"`
|
||||
KeyScope string `json:"-" db:"key_scope"`
|
||||
ModelDefault string `json:"model_default,omitempty" db:"model_default"`
|
||||
Config JSONMap `json:"config,omitempty" db:"config"`
|
||||
Headers JSONMap `json:"headers,omitempty" db:"headers"`
|
||||
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.
|
||||
func (p *ProviderConfig) HasKey() bool {
|
||||
return len(p.APIKeyEnc) > 0
|
||||
}
|
||||
|
||||
type ProviderConfigPatch struct {
|
||||
Name *string `json:"name,omitempty"`
|
||||
Endpoint *string `json:"endpoint,omitempty"`
|
||||
APIKeyEnc []byte `json:"-"`
|
||||
KeyNonce []byte `json:"-"`
|
||||
ModelDefault *string `json:"model_default,omitempty"`
|
||||
Config JSONMap `json:"config,omitempty"`
|
||||
Headers JSONMap `json:"headers,omitempty"`
|
||||
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"`
|
||||
}
|
||||
|
||||
// =========================================
|
||||
// MODEL CATALOG (replaces model_configs)
|
||||
// =========================================
|
||||
|
||||
type CatalogEntry struct {
|
||||
BaseModel
|
||||
ProviderConfigID string `json:"provider_config_id" db:"provider_config_id"`
|
||||
ModelID string `json:"model_id" db:"model_id"`
|
||||
DisplayName string `json:"display_name,omitempty" db:"display_name"`
|
||||
ModelType string `json:"model_type" db:"model_type"` // "chat", "embedding", "image", etc.
|
||||
Capabilities ModelCapabilities `json:"capabilities" db:"capabilities"`
|
||||
Pricing *ModelPricing `json:"pricing,omitempty" db:"pricing"`
|
||||
Visibility string `json:"visibility" db:"visibility"`
|
||||
LastSyncedAt *time.Time `json:"last_synced_at,omitempty" db:"last_synced_at"`
|
||||
}
|
||||
|
||||
type ModelCapabilities struct {
|
||||
Streaming bool `json:"streaming"`
|
||||
ToolCalling bool `json:"tool_calling"`
|
||||
Vision bool `json:"vision"`
|
||||
Thinking bool `json:"thinking"`
|
||||
Reasoning bool `json:"reasoning"`
|
||||
CodeOptimized bool `json:"code_optimized"`
|
||||
WebSearch bool `json:"web_search"`
|
||||
MaxContext int `json:"max_context"`
|
||||
MaxOutputTokens int `json:"max_output_tokens"`
|
||||
}
|
||||
|
||||
func (c ModelCapabilities) HasProviderData() bool {
|
||||
return c.ToolCalling || c.Vision || c.Thinking || c.Reasoning ||
|
||||
c.CodeOptimized || c.WebSearch || c.MaxContext > 0 || c.MaxOutputTokens > 0
|
||||
}
|
||||
|
||||
type ModelPricing struct {
|
||||
InputPerM float64 `json:"input_per_m,omitempty"`
|
||||
OutputPerM float64 `json:"output_per_m,omitempty"`
|
||||
Currency string `json:"currency,omitempty"`
|
||||
}
|
||||
|
||||
// =========================================
|
||||
// PERSONAS (replaces ModelPreset)
|
||||
// =========================================
|
||||
|
||||
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"`
|
||||
|
||||
BaseModelID string `json:"base_model_id" db:"base_model_id"`
|
||||
ProviderConfigID *string `json:"provider_config_id,omitempty" db:"provider_config_id"`
|
||||
|
||||
SystemPrompt string `json:"system_prompt,omitempty" db:"system_prompt"`
|
||||
Temperature *float64 `json:"temperature,omitempty" db:"temperature"`
|
||||
MaxTokens *int `json:"max_tokens,omitempty" db:"max_tokens"`
|
||||
ThinkingBudget *int `json:"thinking_budget,omitempty" db:"thinking_budget"`
|
||||
TopP *float64 `json:"top_p,omitempty" db:"top_p"`
|
||||
|
||||
Scope string `json:"scope" db:"scope"`
|
||||
OwnerID *string `json:"owner_id,omitempty" db:"owner_id"`
|
||||
CreatedBy string `json:"created_by" db:"created_by"`
|
||||
|
||||
IsActive bool `json:"is_active" db:"is_active"`
|
||||
IsShared bool `json:"is_shared" db:"is_shared"`
|
||||
|
||||
// Loaded from persona_grants, not stored in personas table
|
||||
Grants []Grant `json:"grants,omitempty" db:"-"`
|
||||
|
||||
// Loaded from persona_knowledge_bases, not stored in personas table
|
||||
KBIDs []string `json:"kb_ids,omitempty" db:"-"`
|
||||
|
||||
// Memory configuration (v0.18.0)
|
||||
MemoryEnabled bool `json:"memory_enabled" db:"memory_enabled"`
|
||||
MemoryExtractionPrompt *string `json:"memory_extraction_prompt,omitempty" db:"memory_extraction_prompt"`
|
||||
}
|
||||
|
||||
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"`
|
||||
BaseModelID *string `json:"base_model_id,omitempty"`
|
||||
ProviderConfigID *string `json:"provider_config_id,omitempty"`
|
||||
SystemPrompt *string `json:"system_prompt,omitempty"`
|
||||
Temperature *float64 `json:"temperature,omitempty"`
|
||||
MaxTokens *int `json:"max_tokens,omitempty"`
|
||||
ThinkingBudget *int `json:"thinking_budget,omitempty"`
|
||||
TopP *float64 `json:"top_p,omitempty"`
|
||||
IsActive *bool `json:"is_active,omitempty"`
|
||||
IsShared *bool `json:"is_shared,omitempty"`
|
||||
MemoryEnabled *bool `json:"memory_enabled,omitempty"`
|
||||
MemoryExtractionPrompt *string `json:"memory_extraction_prompt,omitempty"`
|
||||
}
|
||||
|
||||
// =========================================
|
||||
// GRANTS
|
||||
@@ -267,9 +96,7 @@ type Grant struct {
|
||||
CreatedAt time.Time `json:"created_at" db:"created_at"`
|
||||
}
|
||||
|
||||
// =========================================
|
||||
// PLATFORM POLICIES
|
||||
// =========================================
|
||||
|
||||
var PolicyDefaults = map[string]string{
|
||||
"allow_user_byok": "false",
|
||||
@@ -281,161 +108,25 @@ var PolicyDefaults = map[string]string{
|
||||
"default_model": "",
|
||||
}
|
||||
|
||||
// =========================================
|
||||
// USER MODEL SETTINGS
|
||||
// =========================================
|
||||
|
||||
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"`
|
||||
SortOrder int `json:"sort_order" db:"sort_order"`
|
||||
}
|
||||
|
||||
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
|
||||
// =========================================
|
||||
|
||||
type Channel struct {
|
||||
BaseModel
|
||||
UserID string `json:"user_id" db:"user_id"`
|
||||
Title string `json:"title" db:"title"`
|
||||
Description string `json:"description,omitempty" db:"description"`
|
||||
Type string `json:"type" db:"type"`
|
||||
Model string `json:"model,omitempty" db:"model"`
|
||||
SystemPrompt string `json:"system_prompt,omitempty" db:"system_prompt"`
|
||||
ProviderConfigID *string `json:"provider_config_id,omitempty" db:"provider_config_id"`
|
||||
IsArchived bool `json:"is_archived" db:"is_archived"`
|
||||
IsPinned bool `json:"is_pinned" db:"is_pinned"`
|
||||
PurgeAfter *time.Time `json:"purge_after,omitempty" db:"purge_after"`
|
||||
FolderID *string `json:"folder_id,omitempty" db:"folder_id"`
|
||||
TeamID *string `json:"team_id,omitempty" db:"team_id"`
|
||||
ProjectID *string `json:"project_id,omitempty" db:"project_id"`
|
||||
WorkspaceID *string `json:"workspace_id,omitempty" db:"workspace_id"`
|
||||
Settings JSONMap `json:"settings,omitempty" db:"settings"`
|
||||
AllowAnonymous bool `json:"allow_anonymous" db:"allow_anonymous"` // v0.24.3: session participants can join
|
||||
}
|
||||
|
||||
// =========================================
|
||||
// SESSION PARTICIPANTS (v0.24.3)
|
||||
// =========================================
|
||||
|
||||
// SessionParticipant is an ephemeral identity for anonymous workflow
|
||||
// channel visitors. Scoped to a single channel, no users row required.
|
||||
type SessionParticipant struct {
|
||||
ID string `json:"id" db:"id"`
|
||||
SessionToken string `json:"session_token" db:"session_token"`
|
||||
ChannelID string `json:"channel_id" db:"channel_id"`
|
||||
DisplayName string `json:"display_name" db:"display_name"`
|
||||
Fingerprint string `json:"fingerprint,omitempty" db:"fingerprint"`
|
||||
CreatedAt time.Time `json:"created_at" db:"created_at"`
|
||||
}
|
||||
|
||||
// =========================================
|
||||
// MESSAGES
|
||||
// =========================================
|
||||
|
||||
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"`
|
||||
ProviderConfigID *string `json:"provider_config_id,omitempty" db:"provider_config_id"`
|
||||
DeletedAt *time.Time `json:"deleted_at,omitempty" db:"deleted_at"`
|
||||
}
|
||||
|
||||
// =========================================
|
||||
// CHANNEL PARTICIPANTS, MODELS, CURSORS
|
||||
// =========================================
|
||||
|
||||
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"`
|
||||
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 {
|
||||
ID string `json:"id" db:"id"`
|
||||
ChannelID string `json:"channel_id" db:"channel_id"`
|
||||
UserID string `json:"user_id" db:"user_id"`
|
||||
ActiveLeafID *string `json:"active_leaf_id,omitempty" db:"active_leaf_id"`
|
||||
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" 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"
|
||||
@@ -461,190 +152,26 @@ func HandleFromName(name string) string {
|
||||
return h
|
||||
}
|
||||
|
||||
// =========================================
|
||||
// ORGANIZATION
|
||||
// =========================================
|
||||
|
||||
type Folder struct {
|
||||
BaseModel
|
||||
UserID string `json:"user_id" db:"user_id"`
|
||||
Name string `json:"name" db:"name"`
|
||||
ParentID *string `json:"parent_id,omitempty" db:"parent_id"`
|
||||
SortOrder int `json:"sort_order" db:"sort_order"`
|
||||
}
|
||||
|
||||
type Project struct {
|
||||
BaseModel
|
||||
Name string `json:"name" db:"name"`
|
||||
Description string `json:"description,omitempty" db:"description"`
|
||||
Color *string `json:"color,omitempty" db:"color"`
|
||||
Icon *string `json:"icon,omitempty" db:"icon"`
|
||||
Scope string `json:"scope" db:"scope"` // personal, team, global
|
||||
OwnerID string `json:"owner_id" db:"owner_id"`
|
||||
TeamID *string `json:"team_id,omitempty" db:"team_id"`
|
||||
IsArchived bool `json:"is_archived" db:"is_archived"`
|
||||
WorkspaceID *string `json:"workspace_id,omitempty" db:"workspace_id"`
|
||||
Settings JSONMap `json:"settings,omitempty" db:"settings"`
|
||||
|
||||
// Computed fields (not DB columns)
|
||||
ChannelCount int `json:"channel_count,omitempty"`
|
||||
KBCount int `json:"kb_count,omitempty"`
|
||||
NoteCount int `json:"note_count,omitempty"`
|
||||
}
|
||||
|
||||
// ProjectPatch holds optional fields for updating a project.
|
||||
type ProjectPatch struct {
|
||||
Name *string `json:"name,omitempty"`
|
||||
Description *string `json:"description,omitempty"`
|
||||
Color *string `json:"color,omitempty"`
|
||||
Icon *string `json:"icon,omitempty"`
|
||||
IsArchived *bool `json:"is_archived,omitempty"`
|
||||
WorkspaceID *string `json:"workspace_id,omitempty"`
|
||||
Settings JSONMap `json:"settings,omitempty"`
|
||||
}
|
||||
|
||||
// ProjectChannel represents a channel's membership in a project.
|
||||
type ProjectChannel struct {
|
||||
ProjectID string `json:"project_id" db:"project_id"`
|
||||
ChannelID string `json:"channel_id" db:"channel_id"`
|
||||
Position int `json:"position" db:"position"`
|
||||
Folder string `json:"folder,omitempty" db:"folder"`
|
||||
AddedAt string `json:"added_at" db:"added_at"`
|
||||
}
|
||||
|
||||
// ProjectKB represents a KB's association with a project.
|
||||
type ProjectKB struct {
|
||||
ProjectID string `json:"project_id" db:"project_id"`
|
||||
KBID string `json:"kb_id" db:"kb_id"`
|
||||
AutoSearch bool `json:"auto_search" db:"auto_search"`
|
||||
AddedAt string `json:"added_at" db:"added_at"`
|
||||
Name string `json:"name,omitempty" db:"name"` // enriched via JOIN
|
||||
}
|
||||
|
||||
// ProjectNote represents a note's association with a project.
|
||||
type ProjectNote struct {
|
||||
ProjectID string `json:"project_id" db:"project_id"`
|
||||
NoteID string `json:"note_id" db:"note_id"`
|
||||
AddedAt string `json:"added_at" db:"added_at"`
|
||||
Title string `json:"title,omitempty" db:"title"` // enriched via JOIN
|
||||
}
|
||||
|
||||
// =========================================
|
||||
// NOTES
|
||||
// =========================================
|
||||
|
||||
type Note struct {
|
||||
BaseModel
|
||||
UserID string `json:"user_id" db:"user_id"`
|
||||
Title string `json:"title" db:"title"`
|
||||
Content string `json:"content" db:"content"`
|
||||
FolderPath string `json:"folder_path" db:"folder_path"`
|
||||
Tags []string `json:"tags,omitempty" db:"tags"`
|
||||
Metadata JSONMap `json:"metadata,omitempty" db:"metadata"`
|
||||
SourceChannelID *string `json:"source_channel_id,omitempty" db:"source_channel_id"`
|
||||
SourceMessageID *string `json:"source_message_id,omitempty" db:"source_message_id"`
|
||||
TeamID *string `json:"team_id,omitempty" db:"team_id"`
|
||||
}
|
||||
|
||||
// NoteLink represents a directed link extracted from [[wikilink]] syntax.
|
||||
type NoteLink struct {
|
||||
TargetNoteID *string `json:"target_note_id,omitempty"`
|
||||
TargetTitle string `json:"target_title"`
|
||||
DisplayText string `json:"display_text,omitempty"`
|
||||
IsTransclusion bool `json:"is_transclusion"`
|
||||
}
|
||||
|
||||
// ExportNoteLink is a note_link with source_note_id included (for export).
|
||||
type ExportNoteLink struct {
|
||||
SourceNoteID string `json:"source_note_id" db:"source_note_id"`
|
||||
TargetNoteID *string `json:"target_note_id,omitempty" db:"target_note_id"`
|
||||
TargetTitle string `json:"target_title" db:"target_title"`
|
||||
DisplayText string `json:"display_text,omitempty" db:"display_text"`
|
||||
IsTransclusion bool `json:"is_transclusion" db:"is_transclusion"`
|
||||
}
|
||||
|
||||
// NoteLinkResult represents a backlink — a note that links to a given note.
|
||||
type NoteLinkResult struct {
|
||||
SourceNoteID string `json:"id"`
|
||||
Title string `json:"title"`
|
||||
FolderPath string `json:"folder_path"`
|
||||
UpdatedAt time.Time `json:"updated_at"`
|
||||
DisplayText string `json:"display_text,omitempty"`
|
||||
}
|
||||
|
||||
// NoteGraphNode is a lightweight note representation for graph display.
|
||||
type NoteGraphNode struct {
|
||||
ID string `json:"id"`
|
||||
Title string `json:"title"`
|
||||
FolderPath string `json:"folder_path"`
|
||||
Tags []string `json:"tags"`
|
||||
UpdatedAt string `json:"updated_at"`
|
||||
LinkCount int `json:"link_count"`
|
||||
}
|
||||
|
||||
// NoteGraphEdge is a resolved link between two notes.
|
||||
type NoteGraphEdge struct {
|
||||
Source string `json:"source"`
|
||||
Target string `json:"target"`
|
||||
Title string `json:"title"`
|
||||
IsTransclusion bool `json:"is_transclusion"`
|
||||
}
|
||||
|
||||
// NoteGraphDangling is an unresolved [[link]] reference.
|
||||
type NoteGraphDangling struct {
|
||||
Source string `json:"source"`
|
||||
Title string `json:"title"`
|
||||
}
|
||||
|
||||
// NoteGraph is the full graph topology for a user's notes.
|
||||
type NoteGraph struct {
|
||||
Nodes []NoteGraphNode `json:"nodes"`
|
||||
Edges []NoteGraphEdge `json:"edges"`
|
||||
Unresolved []NoteGraphDangling `json:"unresolved"`
|
||||
}
|
||||
|
||||
// =========================================
|
||||
// ATTACHMENTS
|
||||
// =========================================
|
||||
|
||||
// =========================================
|
||||
// FILES
|
||||
// =========================================
|
||||
|
||||
// FileOrigin constants
|
||||
const (
|
||||
FileOriginUserUpload = "user_upload"
|
||||
FileOriginToolOutput = "tool_output"
|
||||
FileOriginSystem = "system"
|
||||
)
|
||||
|
||||
// FileDisplayHint constants
|
||||
const (
|
||||
FileHintInline = "inline"
|
||||
FileHintDownload = "download"
|
||||
FileHintThumbnail = "thumbnail"
|
||||
)
|
||||
|
||||
type File struct {
|
||||
ID string `json:"id" db:"id"`
|
||||
ChannelID string `json:"channel_id" db:"channel_id"`
|
||||
MessageID *string `json:"message_id,omitempty" db:"message_id"`
|
||||
UserID string `json:"user_id" db:"user_id"`
|
||||
ProjectID *string `json:"project_id,omitempty" db:"project_id"`
|
||||
Origin string `json:"origin" db:"origin"` // user_upload, tool_output, system
|
||||
Filename string `json:"filename" db:"filename"`
|
||||
ContentType string `json:"content_type" db:"content_type"`
|
||||
SizeBytes int64 `json:"size_bytes" db:"size_bytes"`
|
||||
StorageKey string `json:"-" db:"storage_key"` // never expose filesystem path
|
||||
DisplayHint string `json:"display_hint" db:"display_hint"` // inline, download, thumbnail
|
||||
ExtractedText *string `json:"extracted_text,omitempty" db:"extracted_text"`
|
||||
Metadata JSONMap `json:"metadata,omitempty" db:"metadata"`
|
||||
CreatedAt time.Time `json:"created_at" db:"created_at"`
|
||||
UpdatedAt time.Time `json:"updated_at" db:"updated_at"`
|
||||
}
|
||||
|
||||
|
||||
|
||||
// =========================================
|
||||
// AUDIT LOG
|
||||
// =========================================
|
||||
@@ -662,111 +189,14 @@ type AuditEntry struct {
|
||||
CreatedAt time.Time `json:"created_at" db:"created_at"`
|
||||
}
|
||||
|
||||
// =========================================
|
||||
// USAGE TRACKING
|
||||
// =========================================
|
||||
|
||||
type UsageEntry struct {
|
||||
ID string `json:"id" db:"id"`
|
||||
ChannelID *string `json:"channel_id,omitempty" db:"channel_id"`
|
||||
UserID string `json:"user_id" db:"user_id"`
|
||||
ProviderConfigID *string `json:"provider_config_id,omitempty" db:"provider_config_id"`
|
||||
ProviderScope string `json:"provider_scope" db:"provider_scope"`
|
||||
ModelID string `json:"model_id" db:"model_id"`
|
||||
Role *string `json:"role,omitempty" db:"role"` // null=chat, "utility", "embedding"
|
||||
PromptTokens int `json:"prompt_tokens" db:"prompt_tokens"`
|
||||
CompletionTokens int `json:"completion_tokens" db:"completion_tokens"`
|
||||
CacheCreationTokens int `json:"cache_creation_tokens" db:"cache_creation_tokens"`
|
||||
CacheReadTokens int `json:"cache_read_tokens" db:"cache_read_tokens"`
|
||||
CostInput *float64 `json:"cost_input,omitempty" db:"cost_input"`
|
||||
CostOutput *float64 `json:"cost_output,omitempty" db:"cost_output"`
|
||||
RoutingDecision JSONMap `json:"routing_decision,omitempty" db:"routing_decision"`
|
||||
CreatedAt time.Time `json:"created_at" db:"created_at"`
|
||||
}
|
||||
|
||||
type UsageAggregate struct {
|
||||
GroupKey string `json:"group_key"`
|
||||
Label string `json:"label"`
|
||||
Requests int `json:"requests"`
|
||||
InputTokens int `json:"input_tokens"`
|
||||
OutputTokens int `json:"output_tokens"`
|
||||
TotalCost float64 `json:"total_cost"`
|
||||
}
|
||||
|
||||
type UsageTotals struct {
|
||||
Requests int `json:"requests"`
|
||||
InputTokens int `json:"input_tokens"`
|
||||
OutputTokens int `json:"output_tokens"`
|
||||
TotalCost float64 `json:"total_cost"`
|
||||
Period string `json:"period"`
|
||||
}
|
||||
|
||||
// =========================================
|
||||
// MODEL PRICING
|
||||
// =========================================
|
||||
|
||||
type PricingEntry struct {
|
||||
ID string `json:"id" db:"id"`
|
||||
ProviderConfigID string `json:"provider_config_id" db:"provider_config_id"`
|
||||
ModelID string `json:"model_id" db:"model_id"`
|
||||
InputPerM *float64 `json:"input_per_m" db:"input_per_m"`
|
||||
OutputPerM *float64 `json:"output_per_m" db:"output_per_m"`
|
||||
CacheCreatePerM *float64 `json:"cache_create_per_m" db:"cache_create_per_m"`
|
||||
CacheReadPerM *float64 `json:"cache_read_per_m" db:"cache_read_per_m"`
|
||||
Currency string `json:"currency" db:"currency"`
|
||||
Source string `json:"source" db:"source"` // "catalog" | "manual"
|
||||
UpdatedAt time.Time `json:"updated_at" db:"updated_at"`
|
||||
UpdatedBy *string `json:"updated_by,omitempty" db:"updated_by"`
|
||||
}
|
||||
|
||||
// =========================================
|
||||
// VIEW MODELS (computed, not stored)
|
||||
// =========================================
|
||||
|
||||
// UserModel is the view model returned by the capability resolver.
|
||||
// Combines catalog entries + Personas for the frontend.
|
||||
type UserModel struct {
|
||||
ID string `json:"id"`
|
||||
DisplayName string `json:"display_name"`
|
||||
ModelID string `json:"model_id"`
|
||||
ModelType string `json:"model_type"` // "chat", "embedding", "image", etc.
|
||||
Source string `json:"source"` // "catalog", "persona", "live"
|
||||
|
||||
ProviderConfigID string `json:"provider_config_id"`
|
||||
ConfigID string `json:"config_id"` // Alias of ProviderConfigID for frontend compat
|
||||
ProviderName string `json:"provider_name"`
|
||||
ProviderType string `json:"provider_type"`
|
||||
|
||||
Capabilities ModelCapabilities `json:"capabilities"`
|
||||
|
||||
// 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"`
|
||||
|
||||
Description string `json:"description,omitempty"`
|
||||
Icon string `json:"icon,omitempty"`
|
||||
Avatar string `json:"avatar,omitempty"`
|
||||
SystemPrompt string `json:"system_prompt,omitempty"`
|
||||
Temperature *float64 `json:"temperature,omitempty"`
|
||||
MaxTokens *int `json:"max_tokens,omitempty"`
|
||||
ToolGrants []string `json:"tool_grants,omitempty"`
|
||||
|
||||
Pricing *ModelPricing `json:"pricing,omitempty"`
|
||||
|
||||
Scope string `json:"scope"`
|
||||
OwnerID *string `json:"owner_id,omitempty"`
|
||||
TeamName string `json:"team_name,omitempty"`
|
||||
|
||||
Hidden bool `json:"hidden"`
|
||||
SortOrder int `json:"sort_order"`
|
||||
|
||||
ProviderStatus ProviderStatus `json:"provider_status,omitempty"` // health status from provider health (v0.22.3)
|
||||
}
|
||||
|
||||
// =========================================
|
||||
// JSON HELPERS
|
||||
// =========================================
|
||||
@@ -796,16 +226,7 @@ func (m *JSONMap) Scan(src interface{}) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// ── Extensions ──────────────────────────────
|
||||
|
||||
// Extension tier constants
|
||||
const (
|
||||
ExtTierBrowser = "browser"
|
||||
ExtTierStarlark = "starlark"
|
||||
ExtTierSidecar = "sidecar"
|
||||
)
|
||||
|
||||
// Extension represents an installed extension in the registry.
|
||||
type Extension struct {
|
||||
ID string `json:"id" db:"id"`
|
||||
ExtID string `json:"ext_id" db:"ext_id"` // manifest id
|
||||
@@ -937,114 +358,15 @@ type ResourceGrant struct {
|
||||
CreatedBy string `json:"created_by" db:"created_by"`
|
||||
}
|
||||
|
||||
// ── Knowledge Bases ────────────────────────────
|
||||
|
||||
// KnowledgeBase is a named collection of documents with vector embeddings.
|
||||
type KnowledgeBase struct {
|
||||
ID string `json:"id" db:"id"`
|
||||
Name string `json:"name" db:"name"`
|
||||
Description string `json:"description" db:"description"`
|
||||
Scope string `json:"scope" db:"scope"` // global, team, personal
|
||||
OwnerID *string `json:"owner_id,omitempty" db:"owner_id"`
|
||||
TeamID *string `json:"team_id,omitempty" db:"team_id"`
|
||||
EmbeddingConfig JSONMap `json:"embedding_config" db:"embedding_config"`
|
||||
DocumentCount int `json:"document_count" db:"document_count"`
|
||||
ChunkCount int `json:"chunk_count" db:"chunk_count"`
|
||||
TotalBytes int64 `json:"total_bytes" db:"total_bytes"`
|
||||
Discoverable bool `json:"discoverable" db:"discoverable"`
|
||||
Status string `json:"status" db:"status"` // active, processing, error
|
||||
CreatedAt time.Time `json:"created_at" db:"created_at"`
|
||||
UpdatedAt time.Time `json:"updated_at" db:"updated_at"`
|
||||
}
|
||||
|
||||
// PersonaKB binds a knowledge base to a persona.
|
||||
type PersonaKB struct {
|
||||
PersonaID string `json:"persona_id" db:"persona_id"`
|
||||
KBID string `json:"kb_id" db:"kb_id"`
|
||||
AutoSearch bool `json:"auto_search" db:"auto_search"`
|
||||
AddedAt time.Time `json:"added_at" db:"added_at"`
|
||||
|
||||
// Joined fields (not in persona_knowledge_bases table)
|
||||
KBName string `json:"kb_name,omitempty" db:"kb_name"`
|
||||
DocumentCount int `json:"document_count,omitempty" db:"document_count"`
|
||||
ChunkCount int `json:"chunk_count,omitempty" db:"chunk_count"`
|
||||
}
|
||||
|
||||
// KBDocument is a single uploaded file within a knowledge base.
|
||||
type KBDocument struct {
|
||||
ID string `json:"id" db:"id"`
|
||||
KBID string `json:"kb_id" db:"kb_id"`
|
||||
Filename string `json:"filename" db:"filename"`
|
||||
ContentType string `json:"content_type" db:"content_type"`
|
||||
SizeBytes int64 `json:"size_bytes" db:"size_bytes"`
|
||||
StorageKey string `json:"storage_key" db:"storage_key"`
|
||||
ExtractedText *string `json:"-" db:"extracted_text"`
|
||||
ChunkCount int `json:"chunk_count" db:"chunk_count"`
|
||||
Status string `json:"status" db:"status"` // pending, chunking, embedding, ready, error
|
||||
Error *string `json:"error,omitempty" db:"error"`
|
||||
UploadedBy string `json:"uploaded_by" db:"uploaded_by"`
|
||||
CreatedAt time.Time `json:"created_at" db:"created_at"`
|
||||
UpdatedAt time.Time `json:"updated_at" db:"updated_at"`
|
||||
}
|
||||
|
||||
// KBChunk is a text segment from a document with its embedding vector.
|
||||
type KBChunk struct {
|
||||
ID string `json:"id" db:"id"`
|
||||
KBID string `json:"kb_id" db:"kb_id"`
|
||||
DocumentID string `json:"document_id" db:"document_id"`
|
||||
ChunkIndex int `json:"chunk_index" db:"chunk_index"`
|
||||
Content string `json:"content" db:"content"`
|
||||
TokenCount int `json:"token_count" db:"token_count"`
|
||||
Embedding []float64 `json:"-" db:"embedding"` // never serialized to API
|
||||
Metadata JSONMap `json:"metadata" db:"metadata"`
|
||||
CreatedAt time.Time `json:"created_at" db:"created_at"`
|
||||
}
|
||||
|
||||
// KBSearchResult is a single result from similarity search.
|
||||
type KBSearchResult struct {
|
||||
Content string `json:"content"`
|
||||
Filename string `json:"source"`
|
||||
KBName string `json:"kb"`
|
||||
Similarity float64 `json:"similarity"`
|
||||
Metadata JSONMap `json:"metadata,omitempty"`
|
||||
}
|
||||
|
||||
// ChannelKB represents a knowledge base linked to a channel.
|
||||
type ChannelKB struct {
|
||||
KBID string `json:"kb_id" db:"kb_id"`
|
||||
KBName string `json:"kb_name" db:"name"`
|
||||
Enabled bool `json:"enabled" db:"enabled"`
|
||||
DocumentCount int `json:"document_count" db:"document_count"`
|
||||
}
|
||||
|
||||
// =========================================
|
||||
// PROVIDER HEALTH (v0.22.0)
|
||||
// =========================================
|
||||
|
||||
// ProviderStatus represents the derived health state.
|
||||
type ProviderStatus string
|
||||
|
||||
const (
|
||||
StatusHealthy ProviderStatus = "healthy"
|
||||
StatusDegraded ProviderStatus = "degraded"
|
||||
StatusDown ProviderStatus = "down"
|
||||
StatusUnknown ProviderStatus = "unknown"
|
||||
)
|
||||
|
||||
// ProviderHealthWindow is one hourly bucket of health metrics.
|
||||
type ProviderHealthWindow struct {
|
||||
ID string `json:"id" db:"id"`
|
||||
ProviderConfigID string `json:"provider_config_id" db:"provider_config_id"`
|
||||
WindowStart time.Time `json:"window_start" db:"window_start"`
|
||||
RequestCount int `json:"request_count" db:"request_count"`
|
||||
ErrorCount int `json:"error_count" db:"error_count"`
|
||||
TimeoutCount int `json:"timeout_count" db:"timeout_count"`
|
||||
RateLimitCount int `json:"rate_limit_count" db:"rate_limit_count"` // v0.22.4
|
||||
TotalLatencyMs int64 `json:"total_latency_ms" db:"total_latency_ms"`
|
||||
MaxLatencyMs int `json:"max_latency_ms" db:"max_latency_ms"`
|
||||
LastError *string `json:"last_error,omitempty" db:"last_error"`
|
||||
LastErrorAt *string `json:"last_error_at,omitempty" db:"last_error_at"`
|
||||
}
|
||||
|
||||
// AvgLatencyMs returns the average latency, or 0 if no requests.
|
||||
func (w *ProviderHealthWindow) AvgLatencyMs() int {
|
||||
@@ -1063,75 +385,13 @@ func (w *ProviderHealthWindow) ErrorRate() float64 {
|
||||
}
|
||||
|
||||
// ProviderHealthSummary is the API response for a provider's current health.
|
||||
type ProviderHealthSummary struct {
|
||||
ProviderConfigID string `json:"provider_config_id"`
|
||||
ProviderName string `json:"provider_name,omitempty"`
|
||||
Status ProviderStatus `json:"status"`
|
||||
RequestCount int `json:"request_count"` // last hour
|
||||
ErrorRate float64 `json:"error_rate"` // last hour
|
||||
ErrorCount int `json:"error_count"` // last hour
|
||||
RateLimitCount int `json:"rate_limit_count"` // last hour (v0.22.4)
|
||||
TimeoutCount int `json:"timeout_count"` // last hour
|
||||
AvgLatencyMs int `json:"avg_latency_ms"` // last hour
|
||||
MaxLatencyMs int `json:"max_latency_ms"` // last hour
|
||||
LastError *string `json:"last_error,omitempty"`
|
||||
LastErrorAt *string `json:"last_error_at,omitempty"`
|
||||
}
|
||||
|
||||
// ToolHealthWindow tracks health of built-in tools (web_search, url_fetch, etc.)
|
||||
// in hourly buckets, analogous to ProviderHealthWindow.
|
||||
type ToolHealthWindow struct {
|
||||
ID string `json:"id" db:"id"`
|
||||
ToolName string `json:"tool_name" db:"tool_name"`
|
||||
WindowStart time.Time `json:"window_start" db:"window_start"`
|
||||
RequestCount int `json:"request_count" db:"request_count"`
|
||||
ErrorCount int `json:"error_count" db:"error_count"`
|
||||
TotalLatencyMs int64 `json:"total_latency_ms" db:"total_latency_ms"`
|
||||
MaxLatencyMs int `json:"max_latency_ms" db:"max_latency_ms"`
|
||||
LastError *string `json:"last_error,omitempty" db:"last_error"`
|
||||
LastErrorAt *string `json:"last_error_at,omitempty" db:"last_error_at"`
|
||||
}
|
||||
|
||||
// ToolHealthSummary is the API response for a tool's health.
|
||||
type ToolHealthSummary struct {
|
||||
ToolName string `json:"tool_name"`
|
||||
Status string `json:"status"` // healthy, degraded, down, unknown
|
||||
RequestCount int `json:"request_count"`
|
||||
ErrorRate float64 `json:"error_rate"`
|
||||
AvgLatencyMs int `json:"avg_latency_ms"`
|
||||
}
|
||||
|
||||
// =========================================
|
||||
// CAPABILITY OVERRIDES (v0.22.0)
|
||||
// =========================================
|
||||
|
||||
// CapabilityOverride is an admin correction for a model's capabilities.
|
||||
type CapabilityOverride struct {
|
||||
ID string `json:"id" db:"id"`
|
||||
ProviderConfigID *string `json:"provider_config_id,omitempty" db:"provider_config_id"`
|
||||
ModelID string `json:"model_id" db:"model_id"`
|
||||
Field string `json:"field" db:"field"`
|
||||
Value string `json:"value" db:"value"`
|
||||
SetBy *string `json:"set_by,omitempty" db:"set_by"`
|
||||
CreatedAt string `json:"created_at" db:"created_at"`
|
||||
}
|
||||
|
||||
// =========================================
|
||||
// ROUTING POLICIES (v0.22.2)
|
||||
// =========================================
|
||||
|
||||
// RoutingPolicy is one routing rule that controls how requests are
|
||||
// dispatched to provider configs. Stored in the routing_policies table.
|
||||
type RoutingPolicy struct {
|
||||
ID string `json:"id" db:"id"`
|
||||
Name string `json:"name" db:"name"`
|
||||
Scope string `json:"scope" db:"scope"` // "global" or "team"
|
||||
TeamID *string `json:"team_id,omitempty" db:"team_id"`
|
||||
Priority int `json:"priority" db:"priority"` // lower = first
|
||||
Type string `json:"policy_type" db:"policy_type"` // provider_prefer, team_route, cost_limit, model_alias
|
||||
Config JSONMap `json:"config" db:"config"` // type-specific JSONB
|
||||
IsActive bool `json:"is_active" db:"is_active"`
|
||||
CreatedAt time.Time `json:"created_at" db:"created_at"`
|
||||
UpdatedAt time.Time `json:"updated_at" db:"updated_at"`
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user