mirror of
https://github.com/mudler/LocalAI.git
synced 2026-11-06 20:43:27 -05:00
A binding with its capabilities ticked still gave nothing to the owner's own allowlisted account: Authorize wanted a pairing or a role even for capabilities that are not sensitive, so the agent had no reminder, memory or talk-back tool. A sender whose ID or @name is an entry of the allowlist now holds every capability that is not sensitive, within the ceiling of the binding and while no switch is off. The decision says Via "allowlist". A sensitive capability still needs a pairing, and a role never grants it. The entry of a channel or group lets its members talk to the bot but does not trust them: the trust is matched against the sender only, and a binding that accepts anyone lists nobody. Such a sender still gets capabilities from a pairing or a role. The capability gate takes the handle of the sender for the same match, and a sensitive request ignores the allowlist as it ignores roles. When a tool is held back for a missing right, the turn now carries a short note with the reason in plain words and tells the model to relay it, instead of saying that it has no such tool. The reasons are kept in the grant of the turn, so a worker reports the same words. The access view lists a "listed" sender apart from one let in through a channel. Assisted-by: Claude:claude-sonnet-5-5
80 lines
3.1 KiB
Go
80 lines
3.1 KiB
Go
package studio
|
|
|
|
import (
|
|
"context"
|
|
|
|
"github.com/mudler/LocalAI/pkg/addons/contract"
|
|
)
|
|
|
|
// The capabilities the host asks about before it lets a connector chat do
|
|
// something on the owner's behalf. The names are the closed list of the
|
|
// contract, shared with the manifest and the binding.
|
|
const (
|
|
CapTriggersPropose = contract.CapTriggersPropose
|
|
CapTriggersRun = contract.CapTriggersRun
|
|
CapProposalsConfirm = contract.CapProposalsConfirm
|
|
)
|
|
|
|
// CapabilityRequest is one question to a CapabilityGate. Binding is the
|
|
// connector binding, Owner its owner and Agent its agent. Sender is the
|
|
// platform ID of the person who wrote, and is empty when the question is not
|
|
// about one message (the tools a turn gets, a run that is due). Kind is the
|
|
// kind of the proposal for CapProposalsConfirm, such as "trigger". Sensitive
|
|
// says that the thing asked for changes what the agent may do or reach, so a
|
|
// gate must not grant it on the word of a platform role alone.
|
|
type CapabilityRequest struct {
|
|
Capability string
|
|
Binding string
|
|
Owner string
|
|
Agent string
|
|
Sender string
|
|
// Username is the handle of the sender on the platform, when the message
|
|
// had one. It lets the gate match an @handle entry of the allowlist.
|
|
Username string
|
|
// Roles are the role IDs, declared by the kind, that the add-on reported
|
|
// for the sender of this message. Empty when Sender is.
|
|
Roles []string
|
|
Kind string
|
|
Sensitive bool
|
|
}
|
|
|
|
// CapabilityGate decides whether a binding, and for a message its sender, may
|
|
// use a capability. It is the one seam between the features that act from a
|
|
// connector chat and the model of who may do what. An implementation reads
|
|
// the switches of the owner (the grants of the agent, the capabilities of the
|
|
// binding, pairing and roles) and answers from them. It must be fast and safe
|
|
// for concurrent use, and it must fail closed: any doubt is false.
|
|
type CapabilityGate interface {
|
|
Allow(ctx context.Context, r CapabilityRequest) bool
|
|
}
|
|
|
|
// CapabilityGateFunc makes a function a CapabilityGate.
|
|
type CapabilityGateFunc func(ctx context.Context, r CapabilityRequest) bool
|
|
|
|
// Allow calls f.
|
|
func (f CapabilityGateFunc) Allow(ctx context.Context, r CapabilityRequest) bool { return f(ctx, r) }
|
|
|
|
// DenyCapabilities is the gate a service has until the application sets
|
|
// another: nothing is allowed. A connector chat therefore gets no trigger
|
|
// tool, no confirmation by text and no run from a trigger it proposed, until
|
|
// something enables them.
|
|
var DenyCapabilities CapabilityGate = CapabilityGateFunc(func(context.Context, CapabilityRequest) bool { return false })
|
|
|
|
// SetCapabilityGate gives the service the gate for the capabilities above.
|
|
// The application calls it once, before the service is shared. Nil restores
|
|
// DenyCapabilities.
|
|
func (s *Service) SetCapabilityGate(g CapabilityGate) {
|
|
if g == nil {
|
|
g = DenyCapabilities
|
|
}
|
|
s.capGate = g
|
|
}
|
|
|
|
// allowCap asks the gate; a service without one denies.
|
|
func (s *Service) allowCap(ctx context.Context, r CapabilityRequest) bool {
|
|
if s.capGate == nil {
|
|
return false
|
|
}
|
|
return s.capGate.Allow(ctx, r)
|
|
}
|