Changeset 0.11.0 (#62)

This commit is contained in:
2026-02-25 13:29:15 +00:00
parent d2ec55b16d
commit c9d8e9457e
56 changed files with 5664 additions and 91 deletions

View File

@@ -12,6 +12,7 @@ import (
"github.com/gin-gonic/gin"
"git.gobha.me/xcaliber/chat-switchboard/database"
"git.gobha.me/xcaliber/chat-switchboard/events"
"git.gobha.me/xcaliber/chat-switchboard/models"
"git.gobha.me/xcaliber/chat-switchboard/providers"
"git.gobha.me/xcaliber/chat-switchboard/crypto"
@@ -39,11 +40,12 @@ type completionRequest struct {
type CompletionHandler struct {
vault *crypto.KeyResolver
stores store.Stores
hub *events.Hub // WebSocket hub for browser tool bridge
}
// NewCompletionHandler creates a new handler.
func NewCompletionHandler(vault *crypto.KeyResolver, stores store.Stores) *CompletionHandler {
return &CompletionHandler{vault: vault, stores: stores}
func NewCompletionHandler(vault *crypto.KeyResolver, stores store.Stores, hub *events.Hub) *CompletionHandler {
return &CompletionHandler{vault: vault, stores: stores, hub: hub}
}
// ── Chat Completion ─────────────────────────
@@ -171,8 +173,9 @@ func (h *CompletionHandler) Complete(c *gin.Context) {
}
// Attach tool definitions if model supports tool calling and tools are available
if caps.ToolCalling && tools.HasTools() {
provReq.Tools = h.buildToolDefs()
hasBrowserTools := h.hub != nil && h.hub.IsConnected(userID)
if caps.ToolCalling && (tools.HasTools() || hasBrowserTools) {
provReq.Tools = h.buildToolDefs(c.Request.Context(), userID, hasBrowserTools)
}
// Determine streaming
@@ -189,19 +192,56 @@ func (h *CompletionHandler) Complete(c *gin.Context) {
}
// buildToolDefs converts registered tools to provider-format tool definitions.
func (h *CompletionHandler) buildToolDefs() []providers.ToolDef {
// If includeBrowser is true, also fetches tool schemas from enabled browser extensions.
func (h *CompletionHandler) buildToolDefs(ctx context.Context, userID string, includeBrowser bool) []providers.ToolDef {
allTools := tools.AllDefinitions()
defs := make([]providers.ToolDef, len(allTools))
for i, t := range allTools {
defs[i] = providers.ToolDef{
defs := make([]providers.ToolDef, 0, len(allTools))
for _, t := range allTools {
defs = append(defs, providers.ToolDef{
Type: "function",
Function: providers.FunctionDef{
Name: t.Name,
Description: t.Description,
Parameters: t.Parameters,
},
})
}
// Append browser-defined tool schemas from extensions
if includeBrowser {
exts, err := h.stores.Extensions.ListForUser(ctx, userID)
if err != nil {
log.Printf("⚠️ Failed to load extensions for tools: %v", err)
return defs
}
for _, ext := range exts {
if ext.Tier != "browser" {
continue
}
var manifest struct {
Tools []struct {
Name string `json:"name"`
Description string `json:"description"`
Parameters json.RawMessage `json:"parameters"`
Tier string `json:"tier"`
} `json:"tools"`
}
if err := json.Unmarshal(ext.Manifest, &manifest); err != nil {
continue
}
for _, t := range manifest.Tools {
defs = append(defs, providers.ToolDef{
Type: "function",
Function: providers.FunctionDef{
Name: t.Name,
Description: t.Description,
Parameters: t.Parameters,
},
})
}
}
}
return defs
}
@@ -216,7 +256,7 @@ func (h *CompletionHandler) streamCompletion(
req providers.CompletionRequest,
channelID, userID, model, configID, providerScope string,
) {
result := streamWithToolLoop(c, provider, cfg, &req, model, userID, channelID)
result := streamWithToolLoop(c, provider, cfg, &req, model, userID, channelID, h.hub)
// Persist assistant response
if result.Content != "" {