Compare commits

...

9 Commits

Author SHA1 Message Date
dependabot[bot] 419a4363c3 chore(deps-dev): bump @types/react from 19.2.13 to 19.2.14 in /webui/react-ui (#420)
chore(deps-dev): bump @types/react in /webui/react-ui

Bumps [@types/react](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/react) from 19.2.13 to 19.2.14.
- [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases)
- [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/react)

---
updated-dependencies:
- dependency-name: "@types/react"
  dependency-version: 19.2.14
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-02-16 22:35:14 +01:00
Ettore Di Giacinto 292d0c9c19 chore: improvements to sink state handling
Signed-off-by: Ettore Di Giacinto <mudler@localai.io>
2026-02-16 19:29:03 +01:00
Ettore Di Giacinto 1147e02844 chore: bump cogito
Signed-off-by: Ettore Di Giacinto <mudler@localai.io>
2026-02-16 17:31:49 +01:00
Ettore Di Giacinto c8e83dc4b9 chore: bump cogito
Signed-off-by: Ettore Di Giacinto <mudler@localai.io>
2026-02-16 15:45:19 +01:00
Ettore Di Giacinto 8b5188c35b chore(cogito): bump
Signed-off-by: Ettore Di Giacinto <mudler@localai.io>
2026-02-15 23:08:10 +00:00
Ettore Di Giacinto 55b631bc51 fix: append user actions
Signed-off-by: Ettore Di Giacinto <mudler@localai.io>
2026-02-15 22:56:44 +01:00
Ettore Di Giacinto c1e2eeb8af chore(cogito): bump
Signed-off-by: Ettore Di Giacinto <mudler@localai.io>
2026-02-15 21:34:59 +00:00
Ettore Di Giacinto 50f168f322 drop unused code, run compaction immediately at start
Signed-off-by: Ettore Di Giacinto <mudler@localai.io>
2026-02-14 23:08:02 +01:00
Ettore Di Giacinto b5dacb0e4f chore: allow to choose how we store things in the vector database (#417)
Signed-off-by: Ettore Di Giacinto <mudler@localai.io>
2026-02-14 23:06:49 +01:00
9 changed files with 102 additions and 58 deletions
+28 -4
View File
@@ -148,10 +148,34 @@ func (a *Agent) saveCurrentConversation(conv Messages) {
xlog.Error("Error storing into memory", "error", err)
}
} else {
for _, message := range conv {
if message.Role == "user" {
if err := a.options.ragdb.Store(message.Content); err != nil {
xlog.Error("Error storing into memory", "error", err)
// Use the conversation storage mode to determine what to store
switch a.options.conversationStorageMode {
case StoreWholeConversation:
// Store the entire conversation as a single block
if len(conv) > 0 {
convStr := Messages(conv).String()
if err := a.options.ragdb.Store(convStr); err != nil {
xlog.Error("Error storing whole conversation into memory", "error", err)
}
}
case StoreUserAndAssistant:
// Store user and assistant messages separately
for _, message := range conv {
if message.Role == "user" || message.Role == "assistant" {
if err := a.options.ragdb.Store(message.Content); err != nil {
xlog.Error("Error storing message into memory", "error", err, "role", message.Role)
}
}
}
case StoreUserOnly:
fallthrough
default:
// Store only user messages (default behavior)
for _, message := range conv {
if message.Role == "user" {
if err := a.options.ragdb.Store(message.Content); err != nil {
xlog.Error("Error storing into memory", "error", err)
}
}
}
}
+34 -39
View File
@@ -10,6 +10,18 @@ import (
type Option func(*options) error
// ConversationStorageMode defines how conversations are stored in the knowledge base
type ConversationStorageMode string
const (
// StoreUserOnly stores only user messages (default)
StoreUserOnly ConversationStorageMode = "user_only"
// StoreUserAndAssistant stores both user and assistant messages separately
StoreUserAndAssistant ConversationStorageMode = "user_and_assistant"
// StoreWholeConversation stores the entire conversation as a single block
StoreWholeConversation ConversationStorageMode = "whole_conversation"
)
type llmOptions struct {
APIURL string
APIKey string
@@ -31,6 +43,7 @@ type options struct {
enableHUD, standaloneJob, showCharacter, enableKB, enableSummaryMemory, enableLongTermMemory bool
stripThinkingTags bool
kbAutoSearch bool
conversationStorageMode ConversationStorageMode
canStopItself bool
initiateConversations bool
@@ -49,11 +62,6 @@ type options struct {
kbResults int
ragdb RAGDB
// KB compaction (when enableKB is true)
enableKBCompaction bool
kbCompactionInterval string // "daily", "weekly", "monthly"
kbCompactionSummarize bool
// Evaluation settings
maxEvaluationLoops int
enableEvaluation bool
@@ -85,12 +93,13 @@ func (o *options) SeparatedMultimodalModel() bool {
func defaultOptions() *options {
return &options{
parallelJobs: 1,
periodicRuns: 15 * time.Minute,
schedulerPollInterval: 30 * time.Second,
maxEvaluationLoops: 2,
enableEvaluation: false,
kbAutoSearch: true, // Default to true to maintain backward compatibility
parallelJobs: 1,
periodicRuns: 15 * time.Minute,
schedulerPollInterval: 30 * time.Second,
maxEvaluationLoops: 2,
enableEvaluation: false,
kbAutoSearch: true, // Default to true to maintain backward compatibility
conversationStorageMode: StoreUserOnly, // Default to user-only for backward compatibility
LLMAPI: llmOptions{
APIURL: "http://localhost:8080",
Model: "gpt-4",
@@ -166,33 +175,6 @@ func EnableKnowledgeBaseWithResults(results int) Option {
}
}
// EnableKBCompaction enables periodic KB compaction (group by date, optionally summarize, store, delete originals).
var EnableKBCompaction = func(o *options) error {
o.enableKBCompaction = true
return nil
}
// WithKBCompactionInterval sets the compaction window: "daily", "weekly", or "monthly".
func WithKBCompactionInterval(interval string) Option {
return func(o *options) error {
switch interval {
case "daily", "weekly", "monthly":
o.kbCompactionInterval = interval
default:
o.kbCompactionInterval = "daily"
}
return nil
}
}
// WithKBCompactionSummarize sets whether compaction uses LLM to summarize (true) or just concatenates (false).
func WithKBCompactionSummarize(summarize bool) Option {
return func(o *options) error {
o.kbCompactionSummarize = summarize
return nil
}
}
func WithLastMessageDuration(duration string) Option {
return func(o *options) error {
d, err := time.ParseDuration(duration)
@@ -269,6 +251,19 @@ func WithRAGDB(db RAGDB) Option {
}
}
// WithConversationStorageMode sets how conversations are stored in the knowledge base
func WithConversationStorageMode(mode ConversationStorageMode) Option {
return func(o *options) error {
switch mode {
case StoreUserOnly, StoreUserAndAssistant, StoreWholeConversation:
o.conversationStorageMode = mode
default:
o.conversationStorageMode = StoreUserOnly
}
return nil
}
}
func WithSystemPrompt(prompt string) Option {
return func(o *options) error {
o.systemPrompt = prompt
@@ -445,7 +440,7 @@ func WithRandomIdentity(guidance ...string) Option {
func WithActions(actions ...types.Action) Option {
return func(o *options) error {
o.userActions = actions
o.userActions = append(o.userActions, actions...)
return nil
}
}
+5
View File
@@ -183,6 +183,11 @@ func RunCompaction(ctx context.Context, client *localrag.WrappedClient, period s
// runCompactionTicker runs compaction on a schedule (daily/weekly/monthly). It stops when ctx is done.
func runCompactionTicker(ctx context.Context, client *localrag.WrappedClient, config *AgentConfig, apiURL, apiKey, model string) {
// Run first compaction immediately on startup
if err := RunCompaction(ctx, client, config.KBCompactionInterval, config.KBCompactionSummarize, apiURL, apiKey, model); err != nil {
xlog.Warn("compaction ticker initial run failed", "collection", client.Collection(), "error", err)
}
interval := 24 * time.Hour
switch config.KBCompactionInterval {
case "weekly":
+17 -3
View File
@@ -97,9 +97,10 @@ type AgentConfig struct {
KnowledgeBaseResults int `json:"kb_results" form:"kb_results"`
CanStopItself bool `json:"can_stop_itself" form:"can_stop_itself"`
SystemPrompt string `json:"system_prompt" form:"system_prompt"`
LongTermMemory bool `json:"long_term_memory" form:"long_term_memory"`
SummaryLongTermMemory bool `json:"summary_long_term_memory" form:"summary_long_term_memory"`
ParallelJobs int `json:"parallel_jobs" form:"parallel_jobs"`
LongTermMemory bool `json:"long_term_memory" form:"long_term_memory"`
SummaryLongTermMemory bool `json:"summary_long_term_memory" form:"summary_long_term_memory"`
ConversationStorageMode string `json:"conversation_storage_mode" form:"conversation_storage_mode"`
ParallelJobs int `json:"parallel_jobs" form:"parallel_jobs"`
StripThinkingTags bool `json:"strip_thinking_tags" form:"strip_thinking_tags"`
EnableEvaluation bool `json:"enable_evaluation" form:"enable_evaluation"`
MaxEvaluationLoops int `json:"max_evaluation_loops" form:"max_evaluation_loops"`
@@ -299,6 +300,19 @@ func NewAgentConfigMeta(
HelpText: "Inject knowledge base search and add actions as tools, allowing the agent to access its memory without manual configuration",
Tags: config.Tags{Section: "MemorySettings"},
},
{
Name: "conversation_storage_mode",
Label: "Conversation Storage Mode",
Type: "select",
DefaultValue: "user_only",
Options: []config.FieldOption{
{Value: "user_only", Label: "User Messages Only"},
{Value: "user_and_assistant", Label: "User and Assistant Messages"},
{Value: "whole_conversation", Label: "Whole Conversation as Block"},
},
HelpText: "Controls what gets stored in the knowledge base: only user messages, user and assistant messages separately, or the entire conversation as a single block",
Tags: config.Tags{Section: "MemorySettings"},
},
{
Name: "system_prompt",
Label: "System Prompt",
+4 -8
View File
@@ -447,6 +447,10 @@ func (a *AgentPool) startAgentWithConfig(name, pooldir string, config *AgentConf
opts = append(opts, EnableSummaryMemory)
}
if config.ConversationStorageMode != "" {
opts = append(opts, WithConversationStorageMode(ConversationStorageMode(config.ConversationStorageMode)))
}
if config.CanStopItself {
opts = append(opts, CanStopItself)
}
@@ -479,14 +483,6 @@ func (a *AgentPool) startAgentWithConfig(name, pooldir string, config *AgentConf
if config.EnableKnowledgeBase {
ragClient = localrag.NewWrappedClient(a.localRAGAPI, a.localRAGKey, name)
opts = append(opts, WithRAGDB(ragClient), EnableKnowledgeBase)
if config.EnableKBCompaction {
interval := config.KBCompactionInterval
if interval == "" {
interval = "daily"
}
summarize := config.KBCompactionSummarize
opts = append(opts, EnableKBCompaction, WithKBCompactionInterval(interval), WithKBCompactionSummarize(summarize))
}
// Set KB auto search option (defaults to true for backward compatibility)
// For backward compatibility: if both new KB fields are false (zero values),
// assume this is an old config and default KBAutoSearch to true
+1 -1
View File
@@ -16,7 +16,7 @@ require (
github.com/google/uuid v1.6.0
github.com/jung-kurt/gofpdf v1.16.2
github.com/modelcontextprotocol/go-sdk v1.1.0
github.com/mudler/cogito v0.8.2-0.20260214201734-da0d4ceb2b44
github.com/mudler/cogito v0.9.1-0.20260216182842-e9820e6bf7b9
github.com/mudler/xlog v0.0.1
github.com/onsi/ginkgo/v2 v2.25.3
github.com/onsi/gomega v1.38.2
+10
View File
@@ -234,6 +234,16 @@ github.com/mudler/cogito v0.8.2-0.20260206153401-a5346975d42b h1:LXHovZzNgP0n/oY
github.com/mudler/cogito v0.8.2-0.20260206153401-a5346975d42b/go.mod h1:6sfja3lcu2nWRzEc0wwqGNu/eCG3EWgij+8s7xyUeQ4=
github.com/mudler/cogito v0.8.2-0.20260214201734-da0d4ceb2b44 h1:joGszpItINnZdoL/0p2077Wz2xnxMGRSRgYN5mS7I4c=
github.com/mudler/cogito v0.8.2-0.20260214201734-da0d4ceb2b44/go.mod h1:6sfja3lcu2nWRzEc0wwqGNu/eCG3EWgij+8s7xyUeQ4=
github.com/mudler/cogito v0.8.2-0.20260215213413-af34921ff561 h1:qA7dGJhF5GjgGKHh0lOITZjl9q2jehjKqxxCnaUR1yg=
github.com/mudler/cogito v0.8.2-0.20260215213413-af34921ff561/go.mod h1:6sfja3lcu2nWRzEc0wwqGNu/eCG3EWgij+8s7xyUeQ4=
github.com/mudler/cogito v0.8.2-0.20260215230740-d1c0dc9bd9dc h1:tBAGwQq5kOSIh+vfLffVr5Th2ajFwrTj0usLgyGM2CQ=
github.com/mudler/cogito v0.8.2-0.20260215230740-d1c0dc9bd9dc/go.mod h1:6sfja3lcu2nWRzEc0wwqGNu/eCG3EWgij+8s7xyUeQ4=
github.com/mudler/cogito v0.9.1-0.20260216144443-c96e8ddc1157 h1:M2Yx84QtKQrhMWlB+K99RKJ+x5s8HoRumegObaLuQlI=
github.com/mudler/cogito v0.9.1-0.20260216144443-c96e8ddc1157/go.mod h1:6sfja3lcu2nWRzEc0wwqGNu/eCG3EWgij+8s7xyUeQ4=
github.com/mudler/cogito v0.9.1-0.20260216163119-07b624cc772f h1:LkCJD11Mlx8ZyHoNRbpxbrw1Znl9GiFKjZbcXYwAKwk=
github.com/mudler/cogito v0.9.1-0.20260216163119-07b624cc772f/go.mod h1:6sfja3lcu2nWRzEc0wwqGNu/eCG3EWgij+8s7xyUeQ4=
github.com/mudler/cogito v0.9.1-0.20260216182842-e9820e6bf7b9 h1:Ek+fAIUt9v5tqBgPZ89QTgGXwOSZcCiElD37NkbdmSk=
github.com/mudler/cogito v0.9.1-0.20260216182842-e9820e6bf7b9/go.mod h1:6sfja3lcu2nWRzEc0wwqGNu/eCG3EWgij+8s7xyUeQ4=
github.com/mudler/xlog v0.0.1 h1:yR3/wszd3ZM6u1n96YITJZ4yUcDgqHSwvQmzUJa+8vg=
github.com/mudler/xlog v0.0.1/go.mod h1:39f5vcd05Qd6GWKM8IjyHNQ7AmOx3ZM0YfhfIGhC18U=
github.com/olekukonko/tablewriter v0.0.5 h1:P2Ga83D34wi1o9J6Wh1mRuqd4mF/x/lgBS7N7AbDhec=
+2 -2
View File
@@ -11,7 +11,7 @@
},
"devDependencies": {
"@eslint/js": "^10.0.1",
"@types/react": "^19.2.13",
"@types/react": "^19.2.14",
"@types/react-dom": "^19.2.3",
"@vitejs/plugin-react": "^5.1.4",
"eslint": "^10.0.0",
@@ -212,7 +212,7 @@
"@types/json-schema": ["@types/json-schema@7.0.15", "", {}, "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA=="],
"@types/react": ["@types/react@19.2.13", "", { "dependencies": { "csstype": "^3.2.2" } }, "sha512-KkiJeU6VbYbUOp5ITMIc7kBfqlYkKA5KhEHVrGMmUUMt7NeaZg65ojdPk+FtNrBAOXNVM5QM72jnADjM+XVRAQ=="],
"@types/react": ["@types/react@19.2.14", "", { "dependencies": { "csstype": "^3.2.2" } }, "sha512-ilcTH/UniCkMdtexkoCN0bI7pMcJDvmQFPvuPvmEaYA/NSfFTAgdUSLAoVjaRJm7+6PvcM+q1zYOwS4wTYMF9w=="],
"@types/react-dom": ["@types/react-dom@19.2.3", "", { "peerDependencies": { "@types/react": "^19.2.0" } }, "sha512-jp2L/eY6fn+KgVVQAOqYItbF0VY/YApe5Mz2F0aykSO8gx31bYCZyvSeYxCHKvzHG5eZjc+zyaS5BrBWya2+kQ=="],
+1 -1
View File
@@ -16,7 +16,7 @@
},
"devDependencies": {
"@eslint/js": "^10.0.1",
"@types/react": "^19.2.13",
"@types/react": "^19.2.14",
"@types/react-dom": "^19.2.3",
"@vitejs/plugin-react": "^5.1.4",
"eslint": "^10.0.0",