Changeset 0.29.0 (#195)

This commit is contained in:
2026-03-17 16:28:47 +00:00
parent 128cbb8174
commit 5d637d3a90
129 changed files with 9418 additions and 3016 deletions

View File

@@ -18,6 +18,7 @@ import (
"git.gobha.me/xcaliber/chat-switchboard/crypto"
"git.gobha.me/xcaliber/chat-switchboard/database"
"git.gobha.me/xcaliber/chat-switchboard/events"
"git.gobha.me/xcaliber/chat-switchboard/filters"
"git.gobha.me/xcaliber/chat-switchboard/health"
"git.gobha.me/xcaliber/chat-switchboard/knowledge"
"git.gobha.me/xcaliber/chat-switchboard/models"
@@ -55,6 +56,7 @@ type CompletionHandler struct {
health HealthRecorder // provider health tracking (v0.22.0, nil = disabled)
healthStore HealthStatusQuerier // health status queries for routing (v0.22.2, nil = disabled)
router *routing.Evaluator // routing policy evaluator (v0.22.2, nil = disabled)
filterChain *filters.Chain // pre-completion filter chain (v0.29.0, nil = disabled)
}
// HealthRecorder is the interface for recording provider call outcomes.
@@ -94,6 +96,11 @@ func (h *CompletionHandler) SetRoutingEvaluator(r *routing.Evaluator) {
h.router = r
}
// SetFilterChain attaches the pre-completion filter chain (v0.29.0).
func (h *CompletionHandler) SetFilterChain(fc *filters.Chain) {
h.filterChain = fc
}
// evaluateRouting applies routing policies to select the best provider config
// for this request. Returns the winning config details and a routing decision
// for observability. If routing is disabled or no policies match, returns
@@ -430,7 +437,7 @@ func (h *CompletionHandler) Complete(c *gin.Context) {
}
// ── Team policy: require_private_providers ──
if err := enforcePrivateProviderPolicy(userID, configID); err != nil {
if err := enforcePrivateProviderPolicy(c.Request.Context(), h.stores, userID, configID); err != nil {
c.JSON(http.StatusForbidden, gin.H{"error": err.Error()})
return
}
@@ -1092,7 +1099,7 @@ func escapeJSON(s string) string {
// getModelCapabilities looks up capabilities from model_catalog DB,
// then overlays with known model defaults and heuristic detection.
func (h *CompletionHandler) getModelCapabilities(c *gin.Context, model, apiConfigID string) models.ModelCapabilities {
return ResolveModelCaps(c, model, apiConfigID)
return ResolveModelCaps(h.stores.Catalog, model, apiConfigID)
}
// ── Multimodal Assembly ─────────────────────
@@ -1513,7 +1520,7 @@ func extractFirstMention(content string) string {
// Priority: request.provider_config_id → chat.provider_config_id → user's first active config
func (h *CompletionHandler) resolveConfig(userID string, channelID string, req completionRequest) (providers.ProviderConfig, string, string, string, string, error) {
res, err := ResolveProviderConfig(h.vault, userID, channelID, req.ProviderConfigID, req.Model)
res, err := ResolveProviderConfig(h.stores, h.vault, userID, channelID, req.ProviderConfigID, req.Model)
if err != nil {
return providers.ProviderConfig{}, "", "", "", "", err
}
@@ -1576,12 +1583,22 @@ func (h *CompletionHandler) loadConversation(channelID, userID, personaSystemPro
}
}
// ── KB hint (nudge LLM to use kb_search when KBs are active) ──
if kbHint := BuildKBHint(context.Background(), h.stores, channelID, userID, personaID); kbHint != "" {
messages = append(messages, providers.Message{
Role: "system",
Content: kbHint,
})
// ── Pre-completion filter chain (v0.29.0) ──
// Runs registered filters (KB auto-inject, future extension filters).
// Replaces the inline BuildKBHint call. Each filter contributes zero
// or more system messages. Failures are logged and skipped.
if h.filterChain != nil && h.filterChain.Len() > 0 {
cc := &filters.CompletionContext{
ChannelID: channelID,
UserID: userID,
PersonaID: personaID,
}
for _, injected := range h.filterChain.Execute(context.Background(), cc) {
messages = append(messages, providers.Message{
Role: injected.Role,
Content: injected.Content,
})
}
}
// ── Memory hint (inject known facts about this user — v0.18.0) ──