Changeset 0.22.0.1 (#94)

This commit is contained in:
2026-03-02 01:55:19 +00:00
parent 12e03cec8b
commit 06c4e2a5a1
33 changed files with 1929 additions and 418 deletions

View File

@@ -0,0 +1,90 @@
package sqlite
import (
"context"
"git.gobha.me/xcaliber/chat-switchboard/models"
"git.gobha.me/xcaliber/chat-switchboard/store"
)
type CapOverrideStore struct{}
func NewCapOverrideStore() *CapOverrideStore { return &CapOverrideStore{} }
func (s *CapOverrideStore) Set(ctx context.Context, o *models.CapabilityOverride) error {
id := store.NewID()
_, err := DB.ExecContext(ctx, `
INSERT INTO capability_overrides (id, provider_config_id, model_id, field, value, set_by)
VALUES (?, ?, ?, ?, ?, ?)
ON CONFLICT (provider_config_id, model_id, field) DO UPDATE SET
value = excluded.value,
set_by = excluded.set_by,
created_at = datetime('now')
`, id, o.ProviderConfigID, o.ModelID, o.Field, o.Value, o.SetBy)
return err
}
func (s *CapOverrideStore) Delete(ctx context.Context, id string) error {
_, err := DB.ExecContext(ctx, `DELETE FROM capability_overrides WHERE id = ?`, id)
return err
}
func (s *CapOverrideStore) ListForModel(ctx context.Context, modelID string) ([]models.CapabilityOverride, error) {
return s.query(ctx, `
SELECT id, provider_config_id, model_id, field, value, set_by, created_at
FROM capability_overrides
WHERE model_id = ?
ORDER BY provider_config_id, field
`, modelID)
}
func (s *CapOverrideStore) ListForProviderModel(ctx context.Context, providerConfigID, modelID string) ([]models.CapabilityOverride, error) {
return s.query(ctx, `
SELECT id, provider_config_id, model_id, field, value, set_by, created_at
FROM capability_overrides
WHERE model_id = ? AND (provider_config_id = ? OR provider_config_id IS NULL)
ORDER BY provider_config_id, field
`, modelID, providerConfigID)
}
func (s *CapOverrideStore) ListAll(ctx context.Context) ([]models.CapabilityOverride, error) {
return s.query(ctx, `
SELECT id, provider_config_id, model_id, field, value, set_by, created_at
FROM capability_overrides
ORDER BY model_id, provider_config_id, field
`)
}
func (s *CapOverrideStore) DeleteForProvider(ctx context.Context, providerConfigID string) error {
_, err := DB.ExecContext(ctx, `
DELETE FROM capability_overrides WHERE provider_config_id = ?
`, providerConfigID)
return err
}
func (s *CapOverrideStore) query(ctx context.Context, q string, args ...interface{}) ([]models.CapabilityOverride, error) {
rows, err := DB.QueryContext(ctx, q, args...)
if err != nil {
return nil, err
}
defer rows.Close()
var result []models.CapabilityOverride
for rows.Next() {
var o models.CapabilityOverride
if err := rows.Scan(&o.ID, &o.ProviderConfigID, &o.ModelID, &o.Field, &o.Value, &o.SetBy, &o.CreatedAt); err != nil {
return nil, err
}
result = append(result, o)
}
return result, rows.Err()
}
// ensure interface compliance (compile-time check)
var _ store.CapabilityOverrideStore = (*CapOverrideStore)(nil)
// also check the health store interface at this package level
// (health.Store is in the health package, but we verify via the sql.Rows pattern)
func init() {
// compile-time interface checks happen via the var _ lines above
}

View File

@@ -0,0 +1,125 @@
package sqlite
import (
"context"
"database/sql"
"time"
"git.gobha.me/xcaliber/chat-switchboard/models"
"git.gobha.me/xcaliber/chat-switchboard/store"
)
type HealthStore struct{}
func NewHealthStore() *HealthStore { return &HealthStore{} }
func (s *HealthStore) UpsertWindow(ctx context.Context, w *models.ProviderHealthWindow) error {
id := store.NewID()
windowStr := w.WindowStart.Format(timeFmt)
_, err := DB.ExecContext(ctx, `
INSERT INTO provider_health (id, provider_config_id, window_start,
request_count, error_count, timeout_count,
total_latency_ms, max_latency_ms, last_error, last_error_at, updated_at)
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, datetime('now'))
ON CONFLICT (provider_config_id, window_start) DO UPDATE SET
request_count = provider_health.request_count + excluded.request_count,
error_count = provider_health.error_count + excluded.error_count,
timeout_count = provider_health.timeout_count + excluded.timeout_count,
total_latency_ms = provider_health.total_latency_ms + excluded.total_latency_ms,
max_latency_ms = MAX(provider_health.max_latency_ms, excluded.max_latency_ms),
last_error = COALESCE(excluded.last_error, provider_health.last_error),
last_error_at = COALESCE(excluded.last_error_at, provider_health.last_error_at),
updated_at = datetime('now')
`, id, w.ProviderConfigID, windowStr,
w.RequestCount, w.ErrorCount, w.TimeoutCount,
w.TotalLatencyMs, w.MaxLatencyMs, w.LastError, w.LastErrorAt,
)
return err
}
func (s *HealthStore) GetCurrentWindow(ctx context.Context, providerConfigID string) (*models.ProviderHealthWindow, error) {
windowStr := time.Now().UTC().Truncate(time.Hour).Format(timeFmt)
var w models.ProviderHealthWindow
var windowStartStr string
err := DB.QueryRowContext(ctx, `
SELECT id, provider_config_id, window_start,
request_count, error_count, timeout_count,
total_latency_ms, max_latency_ms, last_error, last_error_at
FROM provider_health
WHERE provider_config_id = ? AND window_start = ?
`, providerConfigID, windowStr).Scan(
&w.ID, &w.ProviderConfigID, &windowStartStr,
&w.RequestCount, &w.ErrorCount, &w.TimeoutCount,
&w.TotalLatencyMs, &w.MaxLatencyMs, &w.LastError, &w.LastErrorAt,
)
if err == sql.ErrNoRows {
return nil, nil
}
if err != nil {
return nil, err
}
w.WindowStart, _ = time.Parse(timeFmt, windowStartStr)
return &w, nil
}
func (s *HealthStore) ListWindows(ctx context.Context, providerConfigID string, hours int) ([]models.ProviderHealthWindow, error) {
rows, err := DB.QueryContext(ctx, `
SELECT id, provider_config_id, window_start,
request_count, error_count, timeout_count,
total_latency_ms, max_latency_ms, last_error, last_error_at
FROM provider_health
WHERE provider_config_id = ?
ORDER BY window_start DESC
LIMIT ?
`, providerConfigID, hours)
if err != nil {
return nil, err
}
defer rows.Close()
return scanHealthRows(rows)
}
func (s *HealthStore) ListAllCurrent(ctx context.Context) ([]models.ProviderHealthWindow, error) {
windowStr := time.Now().UTC().Truncate(time.Hour).Format(timeFmt)
rows, err := DB.QueryContext(ctx, `
SELECT id, provider_config_id, window_start,
request_count, error_count, timeout_count,
total_latency_ms, max_latency_ms, last_error, last_error_at
FROM provider_health
WHERE window_start = ?
ORDER BY provider_config_id
`, windowStr)
if err != nil {
return nil, err
}
defer rows.Close()
return scanHealthRows(rows)
}
func (s *HealthStore) Prune(ctx context.Context, before time.Time) (int64, error) {
result, err := DB.ExecContext(ctx, `
DELETE FROM provider_health WHERE window_start < ?
`, before.Format(timeFmt))
if err != nil {
return 0, err
}
return result.RowsAffected()
}
func scanHealthRows(rows *sql.Rows) ([]models.ProviderHealthWindow, error) {
var result []models.ProviderHealthWindow
for rows.Next() {
var w models.ProviderHealthWindow
var windowStartStr string
if err := rows.Scan(
&w.ID, &w.ProviderConfigID, &windowStartStr,
&w.RequestCount, &w.ErrorCount, &w.TimeoutCount,
&w.TotalLatencyMs, &w.MaxLatencyMs, &w.LastError, &w.LastErrorAt,
); err != nil {
return nil, err
}
w.WindowStart, _ = time.Parse(timeFmt, windowStartStr)
result = append(result, w)
}
return result, rows.Err()
}

View File

@@ -37,5 +37,6 @@ func NewStores(db *sql.DB) store.Stores {
NotifPrefs: NewNotificationPreferenceStore(),
Workspaces: NewWorkspaceStore(),
GitCredentials: &GitCredentialStore{},
CapOverrides: NewCapOverrideStore(),
}
}