Add /ai persona command, debug logging toggle, context trimming, persona override
This commit is contained in:
@@ -2,6 +2,7 @@ package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"sort"
|
||||
"strings"
|
||||
|
||||
"github.com/mattermost/mattermost/server/public/model"
|
||||
@@ -16,6 +17,7 @@ const helpText = `### Matty -- AI Chat Agent
|
||||
|
||||
- @matty <prompt> -- Chat with the default model
|
||||
- @matty model <name> <prompt> -- Use a specific model
|
||||
- @matty persona <name> -- Switch persona (e.g. "trump", "carlin", "sysadmin")
|
||||
- @matty new -- Reset conversation context
|
||||
- @matty model list -- List available models
|
||||
- @matty help -- Show this help
|
||||
@@ -52,6 +54,15 @@ func (p *Plugin) ExecuteCommand(_ *plugin.Context, args *model.CommandArgs) (*mo
|
||||
p.engagementEngine.Sleep(args.UserId, args.ChannelId)
|
||||
return p.response("Conversation reset. @matty to start again."), nil
|
||||
|
||||
case parts[0] == "persona" && len(parts) == 1:
|
||||
return p.handlePersonaShow(args)
|
||||
|
||||
case parts[0] == "persona" && len(parts) >= 2 && parts[1] == "list":
|
||||
return p.handlePersonaList(args)
|
||||
|
||||
case parts[0] == "persona" && len(parts) >= 2:
|
||||
return p.handlePersonaSwitch(args, parts[1])
|
||||
|
||||
case parts[0] == "model" && len(parts) >= 2 && parts[1] == "list":
|
||||
return p.handleModelList(args)
|
||||
|
||||
@@ -113,6 +124,57 @@ func (p *Plugin) handlePrompt(args *model.CommandArgs, modelName, prompt string)
|
||||
}, nil
|
||||
}
|
||||
|
||||
// handlePersonaShow shows the currently active persona.
|
||||
func (p *Plugin) handlePersonaShow(args *model.CommandArgs) (*model.CommandResponse, *model.AppError) {
|
||||
override := p.GetPersonaOverride(args.UserId, args.ChannelId)
|
||||
if override != "" {
|
||||
return p.response("Active persona: " + override + "."), nil
|
||||
}
|
||||
cfg := p.getConfiguration()
|
||||
if cfg.CustomPersona != "" {
|
||||
return p.response("Active persona: custom."), nil
|
||||
}
|
||||
return p.response("Active persona: " + cfg.Persona + "."), nil
|
||||
}
|
||||
|
||||
// handlePersonaList lists available personas with descriptions.
|
||||
func (p *Plugin) handlePersonaList(args *model.CommandArgs) (*model.CommandResponse, *model.AppError) {
|
||||
return p.response(p.listPersonas()), nil
|
||||
}
|
||||
|
||||
// handlePersonaSwitch persists a persona override for the user+channel.
|
||||
func (p *Plugin) handlePersonaSwitch(args *model.CommandArgs, personaID string) (*model.CommandResponse, *model.AppError) {
|
||||
resolved := resolvePersona(personaID)
|
||||
if resolved == "" || resolved == personaLibrary["assistant"] {
|
||||
return p.response("Unknown persona. Available: " + p.listPersonas()), nil
|
||||
}
|
||||
if err := p.SavePersonaOverride(args.UserId, args.ChannelId, personaID); err != nil {
|
||||
return p.response("Failed to save persona: " + err.Error()), nil
|
||||
}
|
||||
return p.response("Persona switched to: " + personaID + ". @matty to test."), nil
|
||||
}
|
||||
|
||||
// listPersonas returns a formatted list of persona IDs with short descriptions.
|
||||
func (p *Plugin) listPersonas() string {
|
||||
ids := make([]string, 0, len(personaLibrary))
|
||||
for id := range personaLibrary {
|
||||
ids = append(ids, id)
|
||||
}
|
||||
sort.Strings(ids)
|
||||
var b strings.Builder
|
||||
b.WriteString("Available personas:\n")
|
||||
for _, id := range ids {
|
||||
desc := personaLibrary[id]
|
||||
// Truncate to ~60 chars for a compact preview.
|
||||
if len(desc) > 60 {
|
||||
desc = desc[:57] + "..."
|
||||
}
|
||||
b.WriteString(fmt.Sprintf(" %-12s %s\n", id, desc))
|
||||
}
|
||||
b.WriteString("Use /ai persona <name> to switch.")
|
||||
return b.String()
|
||||
}
|
||||
|
||||
// handleModelList queries Ollama for available models and returns them.
|
||||
func (p *Plugin) handleModelList(args *model.CommandArgs) (*model.CommandResponse, *model.AppError) {
|
||||
models, err := p.ollamaClient.ListModels()
|
||||
@@ -127,6 +189,28 @@ func (p *Plugin) handleModelList(args *model.CommandArgs) (*model.CommandRespons
|
||||
return p.response(b.String()), nil
|
||||
}
|
||||
|
||||
// personaOverrideKey returns the KV store key for a user+channel persona override.
|
||||
func personaOverrideKey(userID, channelID string) string {
|
||||
return "persona_" + userID + "_" + channelID
|
||||
}
|
||||
|
||||
// SavePersonaOverride persists a persona override to the KV store.
|
||||
func (p *Plugin) SavePersonaOverride(userID, channelID, personaID string) error {
|
||||
if appErr := p.API.KVSet(personaOverrideKey(userID, channelID), []byte(personaID)); appErr != nil {
|
||||
return fmt.Errorf("kvset: %w", appErr)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// GetPersonaOverride returns the persisted persona override for a user+channel.
|
||||
func (p *Plugin) GetPersonaOverride(userID, channelID string) string {
|
||||
data, appErr := p.API.KVGet(personaOverrideKey(userID, channelID))
|
||||
if appErr != nil || data == nil {
|
||||
return ""
|
||||
}
|
||||
return string(data)
|
||||
}
|
||||
|
||||
// parseArgs splits a command string into tokens.
|
||||
func parseArgs(input string) []string {
|
||||
return strings.Fields(input)
|
||||
|
||||
Reference in New Issue
Block a user