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

@@ -183,20 +183,40 @@ func (s *ChannelStore) SetCursor(ctx context.Context, channelID, userID, leafID
}
func (s *ChannelStore) SetModel(ctx context.Context, cm *models.ChannelModel) error {
// For persona entries, use the persona-aware path
if cm.PersonaID != nil && *cm.PersonaID != "" {
return s.SetPersonaModel(ctx, cm)
}
// Raw model upsert (no persona) — matches idx_channel_models_raw partial index
_, err := DB.ExecContext(ctx, `
INSERT INTO channel_models (channel_id, model_id, provider_config_id, display_name, system_prompt, settings, is_default)
VALUES ($1, $2, $3, $4, $5, $6, $7)
ON CONFLICT (channel_id, model_id) DO UPDATE SET
provider_config_id = $3, display_name = $4, system_prompt = $5, settings = $6, is_default = $7`,
ON CONFLICT (channel_id, model_id, provider_config_id) WHERE persona_id IS NULL DO UPDATE SET
display_name = $4, system_prompt = $5, settings = $6, is_default = $7`,
cm.ChannelID, cm.ModelID, cm.ProviderConfigID, cm.DisplayName, cm.SystemPrompt, "{}", cm.IsDefault)
return err
}
// SetPersonaModel inserts or updates a persona's channel model roster entry.
// Uses the idx_channel_models_persona partial index (one per persona per channel).
func (s *ChannelStore) SetPersonaModel(ctx context.Context, cm *models.ChannelModel) error {
_, err := DB.ExecContext(ctx, `
INSERT INTO channel_models (channel_id, model_id, provider_config_id, persona_id, display_name, system_prompt, settings, is_default)
VALUES ($1, $2, $3, $4, $5, $6, $7, $8)
ON CONFLICT (channel_id, persona_id) WHERE persona_id IS NOT NULL DO UPDATE SET
model_id = $2, provider_config_id = $3, display_name = $5, system_prompt = $6, settings = $7, is_default = $8`,
cm.ChannelID, cm.ModelID, cm.ProviderConfigID, cm.PersonaID, cm.DisplayName, cm.SystemPrompt, "{}", cm.IsDefault)
return err
}
func (s *ChannelStore) GetModels(ctx context.Context, channelID string) ([]models.ChannelModel, error) {
rows, err := DB.QueryContext(ctx, `
SELECT id, channel_id, model_id, COALESCE(provider_config_id::text, ''),
COALESCE(display_name, ''), COALESCE(system_prompt, ''), is_default
FROM channel_models WHERE channel_id = $1`, channelID)
SELECT cm.id, cm.channel_id, cm.model_id, COALESCE(cm.provider_config_id::text, ''),
COALESCE(cm.persona_id::text, ''), COALESCE(p.handle, ''), COALESCE(cm.display_name, ''),
COALESCE(cm.system_prompt, ''), cm.is_default
FROM channel_models cm
LEFT JOIN personas p ON p.id = cm.persona_id
WHERE cm.channel_id = $1`, channelID)
if err != nil {
return nil, err
}
@@ -205,10 +225,14 @@ func (s *ChannelStore) GetModels(ctx context.Context, channelID string) ([]model
var result []models.ChannelModel
for rows.Next() {
var cm models.ChannelModel
var personaID string
if err := rows.Scan(&cm.ID, &cm.ChannelID, &cm.ModelID, &cm.ProviderConfigID,
&cm.DisplayName, &cm.SystemPrompt, &cm.IsDefault); err != nil {
&personaID, &cm.Handle, &cm.DisplayName, &cm.SystemPrompt, &cm.IsDefault); err != nil {
return nil, err
}
if personaID != "" {
cm.PersonaID = &personaID
}
result = append(result, cm)
}
return result, rows.Err()
@@ -216,15 +240,22 @@ func (s *ChannelStore) GetModels(ctx context.Context, channelID string) ([]model
func (s *ChannelStore) GetModelByID(ctx context.Context, id string) (*models.ChannelModel, error) {
var cm models.ChannelModel
var personaID string
err := DB.QueryRowContext(ctx, `
SELECT id, channel_id, model_id, COALESCE(provider_config_id::text, ''),
COALESCE(display_name, ''), COALESCE(system_prompt, ''), is_default
FROM channel_models WHERE id = $1`, id).Scan(
SELECT cm.id, cm.channel_id, cm.model_id, COALESCE(cm.provider_config_id::text, ''),
COALESCE(cm.persona_id::text, ''), COALESCE(p.handle, ''), COALESCE(cm.display_name, ''),
COALESCE(cm.system_prompt, ''), cm.is_default
FROM channel_models cm
LEFT JOIN personas p ON p.id = cm.persona_id
WHERE cm.id = $1`, id).Scan(
&cm.ID, &cm.ChannelID, &cm.ModelID, &cm.ProviderConfigID,
&cm.DisplayName, &cm.SystemPrompt, &cm.IsDefault)
&personaID, &cm.Handle, &cm.DisplayName, &cm.SystemPrompt, &cm.IsDefault)
if err != nil {
return nil, err
}
if personaID != "" {
cm.PersonaID = &personaID
}
return &cm, nil
}
@@ -287,3 +318,99 @@ func (s *ChannelStore) ResolveWorkspaceID(ctx context.Context, channelID string)
}
return NullableString(wsID), nil
}
// ── Channel Participants (ICD §3.7) ──────────
func (s *ChannelStore) AddParticipant(ctx context.Context, p *models.ChannelParticipant) error {
_, err := DB.ExecContext(ctx, `
INSERT INTO channel_participants (channel_id, participant_type, participant_id, role, display_name, avatar_url)
VALUES ($1, $2, $3, $4, $5, $6)`,
p.ChannelID, p.ParticipantType, p.ParticipantID, p.Role, p.DisplayName, p.AvatarURL)
return err
}
func (s *ChannelStore) ListParticipants(ctx context.Context, channelID string) ([]models.ChannelParticipant, error) {
rows, err := DB.QueryContext(ctx, `
SELECT id, channel_id, participant_type, participant_id, role,
display_name, avatar_url, joined_at
FROM channel_participants WHERE channel_id = $1 ORDER BY joined_at`, channelID)
if err != nil {
return nil, err
}
defer rows.Close()
var result []models.ChannelParticipant
for rows.Next() {
var p models.ChannelParticipant
if err := rows.Scan(&p.ID, &p.ChannelID, &p.ParticipantType, &p.ParticipantID,
&p.Role, &p.DisplayName, &p.AvatarURL, &p.JoinedAt); err != nil {
return nil, err
}
result = append(result, p)
}
return result, rows.Err()
}
func (s *ChannelStore) GetParticipantByID(ctx context.Context, id string) (*models.ChannelParticipant, error) {
var p models.ChannelParticipant
err := DB.QueryRowContext(ctx, `
SELECT id, channel_id, participant_type, participant_id, role,
display_name, avatar_url, joined_at
FROM channel_participants WHERE id = $1`, id).Scan(
&p.ID, &p.ChannelID, &p.ParticipantType, &p.ParticipantID,
&p.Role, &p.DisplayName, &p.AvatarURL, &p.JoinedAt)
if err != nil {
return nil, err
}
return &p, nil
}
func (s *ChannelStore) UpdateParticipantRole(ctx context.Context, id, role string) error {
_, err := DB.ExecContext(ctx, `UPDATE channel_participants SET role = $1 WHERE id = $2`, role, id)
return err
}
func (s *ChannelStore) RemoveParticipant(ctx context.Context, id string) error {
res, err := DB.ExecContext(ctx, `DELETE FROM channel_participants WHERE id = $1`, id)
if err != nil {
return err
}
n, _ := res.RowsAffected()
if n == 0 {
return sql.ErrNoRows
}
return nil
}
func (s *ChannelStore) IsParticipant(ctx context.Context, channelID, pType, pID string) (bool, error) {
var exists bool
err := DB.QueryRowContext(ctx, `
SELECT EXISTS(SELECT 1 FROM channel_participants
WHERE channel_id = $1 AND participant_type = $2 AND participant_id = $3)`,
channelID, pType, pID).Scan(&exists)
return exists, err
}
func (s *ChannelStore) GetParticipantRole(ctx context.Context, channelID, pType, pID string) (string, error) {
var role string
err := DB.QueryRowContext(ctx, `
SELECT role FROM channel_participants
WHERE channel_id = $1 AND participant_type = $2 AND participant_id = $3`,
channelID, pType, pID).Scan(&role)
return role, err
}
func (s *ChannelStore) CountParticipantsByRole(ctx context.Context, channelID, role string) (int, error) {
var count int
err := DB.QueryRowContext(ctx, `
SELECT COUNT(*) FROM channel_participants WHERE channel_id = $1 AND role = $2`,
channelID, role).Scan(&count)
return count, err
}
func (s *ChannelStore) DeleteModelByPersona(ctx context.Context, channelID, personaID string) error {
_, err := DB.ExecContext(ctx,
`DELETE FROM channel_models WHERE channel_id = $1 AND persona_id = $2`,
channelID, personaID)
return err
}