Changeset 0.25.0 (#160)
This commit is contained in:
@@ -287,12 +287,21 @@ func (h *CompletionHandler) Complete(c *gin.Context) {
|
||||
var personaID string // tracks active persona for KB scoping
|
||||
var personaThinkingBudget *int // persona-level thinking budget for hook injection (v0.22.1)
|
||||
|
||||
// v0.25.0: Query channel metadata once — used by group leader check, tool context,
|
||||
// and execution context. Avoids redundant SELECTs on channels.
|
||||
var channelType string
|
||||
var channelTeamID *string
|
||||
_ = database.DB.QueryRowContext(c.Request.Context(), database.Q(`
|
||||
SELECT COALESCE(type, 'direct'), team_id FROM channels WHERE id = $1
|
||||
`), channelID).Scan(&channelType, &channelTeamID)
|
||||
|
||||
teamID := ""
|
||||
if channelTeamID != nil {
|
||||
teamID = *channelTeamID
|
||||
}
|
||||
|
||||
// v0.23.2: Group leader default — when type=group and no @mention, use the leader persona
|
||||
if req.PersonaID == "" && extractFirstMention(req.Content) == "" {
|
||||
var channelType string
|
||||
_ = database.DB.QueryRowContext(c.Request.Context(), database.Q(`
|
||||
SELECT COALESCE(type, 'direct') FROM channels WHERE id = $1
|
||||
`), channelID).Scan(&channelType)
|
||||
if channelType == "group" {
|
||||
// Find the leader persona via channel_participants → persona_group_members
|
||||
var leaderPersonaID string
|
||||
@@ -493,6 +502,16 @@ func (h *CompletionHandler) Complete(c *gin.Context) {
|
||||
// Resolve effective workspace: channel.workspace_id > project.workspace_id
|
||||
workspaceID, _ := h.stores.Channels.ResolveWorkspaceID(c.Request.Context(), channelID)
|
||||
|
||||
// ── Tool context (v0.25.0) ─────────────────
|
||||
// Uses channel metadata queried above (channelType, teamID).
|
||||
tctx := tools.ToolContext{
|
||||
ChannelType: channelType,
|
||||
WorkspaceID: workspaceID,
|
||||
TeamID: teamID,
|
||||
IsVisitor: isSessionAuth(c),
|
||||
// PersonaID set below after persona resolution
|
||||
}
|
||||
|
||||
// ── @mention routing (v0.23.0 / v0.23.1) ─────────────────────────────────
|
||||
// Resolve @persona-handle, @model-id, or @username.
|
||||
// - User mention → skip completion, notify recipient (v0.23.1)
|
||||
@@ -637,9 +656,10 @@ func (h *CompletionHandler) Complete(c *gin.Context) {
|
||||
}
|
||||
|
||||
// Attach tool definitions if model supports tool calling and tools are available
|
||||
tctx.PersonaID = personaID // finalize after all persona resolution (@mention, group leader, etc.)
|
||||
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, workspaceID)
|
||||
provReq.Tools = h.buildToolDefs(c.Request.Context(), userID, hasBrowserTools, req.DisabledTools, tctx, personaID)
|
||||
}
|
||||
|
||||
// ── Permission pre-flight ─────────────────────────────────────────────
|
||||
@@ -787,7 +807,12 @@ 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, workspaceID)
|
||||
tctx := tools.ToolContext{
|
||||
WorkspaceID: workspaceID,
|
||||
PersonaID: personaID,
|
||||
IsVisitor: isSessionAuth(c),
|
||||
}
|
||||
provReq.Tools = h.buildToolDefs(c.Request.Context(), userID, hasBrowserTools, req.DisabledTools, tctx, personaID)
|
||||
}
|
||||
|
||||
// Apply provider-specific request hooks (v0.22.1)
|
||||
@@ -823,25 +848,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.
|
||||
// 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 {
|
||||
// v0.25.0: Tools self-declare availability via predicates on ToolContext.
|
||||
// v0.25.0: Persona tool grants apply as a second-pass allowlist when personaID
|
||||
// has explicit grants. Empty grants = inherit all (backward compat).
|
||||
func (h *CompletionHandler) buildToolDefs(ctx context.Context, userID string, includeBrowser bool, disabledTools []string, tctx tools.ToolContext, personaID 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 + git tools
|
||||
if workspaceID == "" {
|
||||
for _, name := range tools.WorkspaceToolNames() {
|
||||
disabled[name] = true
|
||||
}
|
||||
for _, name := range tools.GitToolNames() {
|
||||
disabled[name] = true
|
||||
}
|
||||
}
|
||||
|
||||
allTools := tools.AllDefinitionsFiltered(disabled)
|
||||
// v0.25.0: Tools self-declare availability via predicates.
|
||||
// AvailableFor evaluates each tool's Availability() against tctx,
|
||||
// then applies the disabled set. Replaces manual WorkspaceToolNames()
|
||||
// and GitToolNames() suppression.
|
||||
allTools := tools.AvailableFor(tctx, disabled)
|
||||
defs := make([]providers.ToolDef, 0, len(allTools))
|
||||
for _, t := range allTools {
|
||||
defs = append(defs, providers.ToolDef{
|
||||
@@ -892,6 +913,28 @@ func (h *CompletionHandler) buildToolDefs(ctx context.Context, userID string, in
|
||||
}
|
||||
}
|
||||
|
||||
// v0.25.0: Persona tool grants — second-pass allowlist.
|
||||
// If the active persona has explicit tool grants, restrict to only those tools.
|
||||
// Empty grants = persona inherits all context-available tools (backward compat).
|
||||
if personaID != "" && h.stores.Personas != nil {
|
||||
grants, err := h.stores.Personas.GetToolGrants(ctx, personaID)
|
||||
if err != nil {
|
||||
log.Printf("⚠️ Failed to load tool grants for persona %s: %v", personaID, err)
|
||||
} else if len(grants) > 0 {
|
||||
allowed := make(map[string]bool, len(grants))
|
||||
for _, g := range grants {
|
||||
allowed[g] = true
|
||||
}
|
||||
filtered := defs[:0]
|
||||
for _, d := range defs {
|
||||
if allowed[d.Function.Name] {
|
||||
filtered = append(filtered, d)
|
||||
}
|
||||
}
|
||||
defs = filtered
|
||||
}
|
||||
}
|
||||
|
||||
return defs
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user