Changeset 0.21.1.1 (#87)

This commit is contained in:
2026-03-01 16:40:26 +00:00
parent 70aa78e486
commit 09c7281552
20 changed files with 853 additions and 102 deletions

View File

@@ -219,6 +219,10 @@ func (h *CompletionHandler) Complete(c *gin.Context) {
}
}
// ── Workspace resolution (v0.21.1) ────────
// Resolve effective workspace: channel.workspace_id > project.workspace_id
workspaceID, _ := h.stores.Channels.ResolveWorkspaceID(c.Request.Context(), channelID)
// ── Multi-model @mention routing (v0.20.0) ──────────
// Check if the message @mentions specific channel models.
// If multiple models are targeted, fan out sequentially.
@@ -227,7 +231,7 @@ func (h *CompletionHandler) Complete(c *gin.Context) {
parsed := mentions.Parse(req.Content, roster)
targets := mentions.ResolvedModels(parsed)
if len(targets) > 1 {
h.multiModelStream(c, targets, messages, channelID, userID, personaID, presetSystemPrompt, req)
h.multiModelStream(c, targets, messages, channelID, userID, personaID, presetSystemPrompt, workspaceID, req)
return
}
}
@@ -255,7 +259,7 @@ func (h *CompletionHandler) Complete(c *gin.Context) {
// Attach tool definitions if model supports tool calling and tools are available
hasBrowserTools := h.hub != nil && h.hub.IsConnected(userID)
if caps.ToolCalling && (tools.HasTools() || hasBrowserTools) {
provReq.Tools = h.buildToolDefs(c.Request.Context(), userID, hasBrowserTools, req.DisabledTools)
provReq.Tools = h.buildToolDefs(c.Request.Context(), userID, hasBrowserTools, req.DisabledTools, workspaceID)
}
// Determine streaming
@@ -265,9 +269,9 @@ func (h *CompletionHandler) Complete(c *gin.Context) {
}
if stream {
h.streamCompletion(c, provider, providerCfg, provReq, channelID, userID, model, configID, providerScope, personaID)
h.streamCompletion(c, provider, providerCfg, provReq, channelID, userID, model, configID, providerScope, personaID, workspaceID)
} else {
h.syncCompletion(c, provider, providerCfg, provReq, channelID, userID, model, configID, providerScope, personaID)
h.syncCompletion(c, provider, providerCfg, provReq, channelID, userID, model, configID, providerScope, personaID, workspaceID)
}
}
@@ -280,7 +284,7 @@ func (h *CompletionHandler) multiModelStream(
c *gin.Context,
targets []models.ChannelModel,
messages []providers.Message,
channelID, userID, personaID, presetSystemPrompt string,
channelID, userID, personaID, presetSystemPrompt, workspaceID string,
req completionRequest,
) {
// Set SSE headers once for the entire multi-model stream
@@ -363,11 +367,11 @@ func (h *CompletionHandler) multiModelStream(
}
hasBrowserTools := h.hub != nil && h.hub.IsConnected(userID)
if caps.ToolCalling && (tools.HasTools() || hasBrowserTools) {
provReq.Tools = h.buildToolDefs(c.Request.Context(), userID, hasBrowserTools, req.DisabledTools)
provReq.Tools = h.buildToolDefs(c.Request.Context(), userID, hasBrowserTools, req.DisabledTools, workspaceID)
}
// Stream this model's response using the shared streaming core
result := streamModelResponse(c, provider, providerCfg, &provReq, model, target.DisplayName, userID, channelID, personaID, h.hub)
result := streamModelResponse(c, provider, providerCfg, &provReq, model, target.DisplayName, userID, channelID, personaID, workspaceID, h.hub)
// Persist assistant message with model attribution
if result.Content != "" {
@@ -390,13 +394,21 @@ func (h *CompletionHandler) multiModelStream(
// buildToolDefs converts registered tools to provider-format tool definitions.
// If includeBrowser is true, also fetches tool schemas from enabled browser extensions.
// Tools whose names appear in disabledTools are excluded.
func (h *CompletionHandler) buildToolDefs(ctx context.Context, userID string, includeBrowser bool, disabledTools []string) []providers.ToolDef {
// Workspace tools are excluded when workspaceID is empty (no workspace bound).
func (h *CompletionHandler) buildToolDefs(ctx context.Context, userID string, includeBrowser bool, disabledTools []string, workspaceID string) []providers.ToolDef {
// Build disabled set for O(1) lookup
disabled := make(map[string]bool, len(disabledTools))
for _, name := range disabledTools {
disabled[name] = true
}
// If no workspace is bound, disable workspace tools
if workspaceID == "" {
for _, name := range tools.WorkspaceToolNames() {
disabled[name] = true
}
}
allTools := tools.AllDefinitionsFiltered(disabled)
defs := make([]providers.ToolDef, 0, len(allTools))
for _, t := range allTools {
@@ -519,9 +531,9 @@ func (h *CompletionHandler) streamCompletion(
provider providers.Provider,
cfg providers.ProviderConfig,
req providers.CompletionRequest,
channelID, userID, model, configID, providerScope, personaID string,
channelID, userID, model, configID, providerScope, personaID, workspaceID string,
) {
result := streamWithToolLoop(c, provider, cfg, &req, model, userID, channelID, personaID, h.hub)
result := streamWithToolLoop(c, provider, cfg, &req, model, userID, channelID, personaID, workspaceID, h.hub)
// Persist assistant response
if result.Content != "" {
@@ -543,7 +555,7 @@ func (h *CompletionHandler) syncCompletion(
provider providers.Provider,
cfg providers.ProviderConfig,
req providers.CompletionRequest,
channelID, userID, model, configID, providerScope, personaID string,
channelID, userID, model, configID, providerScope, personaID, workspaceID string,
) {
var totalInput, totalOutput int
var totalCacheCreation, totalCacheRead int
@@ -607,9 +619,10 @@ func (h *CompletionHandler) syncCompletion(
req.Messages = append(req.Messages, assistantMsg)
execCtx := tools.ExecutionContext{
UserID: userID,
ChannelID: channelID,
PersonaID: personaID,
UserID: userID,
ChannelID: channelID,
PersonaID: personaID,
WorkspaceID: workspaceID,
}
for _, tc := range resp.ToolCalls {
call := tools.ToolCall{