mirror of
https://github.com/tiennm99/goclaw.git
synced 2026-10-20 02:17:24 +00:00
feat(vault): tenant-wide rescan with nullable agent_id + media preview
Vault rescan redesigned from per-agent to tenant-wide:
- POST /v1/vault/rescan replaces POST /v1/agents/{id}/vault/rescan
- agent_id nullable in vault_documents (PG migration 046 + SQLite v14)
- Path-based scope inference: agents/{key}/ → personal, teams/{uuid}/ → team, root → shared
- Interceptor sets agent_id=NULL for team-scoped file writes
- Enrichment worker batch key handles empty agent_id
- web-fetch/ directory excluded from vault scan at any depth
- Media preview: images render via authenticated blob URL, binary files show metadata
- Scan button no longer requires agent selection
This commit is contained in:
1 parent
139b454efd
commit
9f77bfe711
49 files changed
+1550
-312
No files matched your search
+60
-29
@@ -181,17 +181,16 @@ func runGateway() {
|
||||
}
|
||||
setupMemoryEmbeddings(pgStores, providerRegistry)
|
||||
|
||||
// Resolve background provider for consolidation + vault enrichment.
|
||||
// Fallback: background.provider → agent.default_provider → first registered provider.
|
||||
bgProvider, bgModel := resolveBackgroundProvider(cfg, providerRegistry)
|
||||
|
||||
// V3: Wire consolidation pipeline (episodic → semantic → KG → dreaming)
|
||||
if pgStores.Episodic != nil {
|
||||
var consolidationProvider providers.Provider
|
||||
if names := providerRegistry.ListForTenant(providers.MasterTenantID); len(names) > 0 {
|
||||
consolidationProvider, _ = providerRegistry.GetForTenant(providers.MasterTenantID, names[0])
|
||||
}
|
||||
if consolidationProvider != nil {
|
||||
// Create KG extractor for semantic worker (entity/relation extraction from episodic summaries)
|
||||
if bgProvider != nil {
|
||||
var kgExtractor *kg.Extractor
|
||||
if pgStores.KnowledgeGraph != nil {
|
||||
kgExtractor = kg.NewExtractor(consolidationProvider, consolidationProvider.DefaultModel(), 0)
|
||||
kgExtractor = kg.NewExtractor(bgProvider, bgModel, 0)
|
||||
}
|
||||
cleanupConsolidation := consolidation.Register(consolidation.ConsolidationDeps{
|
||||
EpisodicStore: pgStores.Episodic,
|
||||
@@ -199,36 +198,28 @@ func runGateway() {
|
||||
KGStore: pgStores.KnowledgeGraph,
|
||||
SessionStore: pgStores.Sessions,
|
||||
EventBus: domainBus,
|
||||
Provider: consolidationProvider,
|
||||
Model: consolidationProvider.DefaultModel(),
|
||||
Provider: bgProvider,
|
||||
Model: bgModel,
|
||||
Extractor: kgExtractor,
|
||||
// Per-agent dreaming overrides (MemoryConfig.Dreaming JSONB).
|
||||
AgentStore: pgStores.Agents,
|
||||
AgentStore: pgStores.Agents,
|
||||
})
|
||||
defer cleanupConsolidation()
|
||||
slog.Info("consolidation pipeline registered")
|
||||
slog.Info("consolidation pipeline registered", "provider", bgProvider.Name(), "model", bgModel)
|
||||
} else {
|
||||
slog.Warn("consolidation pipeline skipped: no provider available")
|
||||
}
|
||||
}
|
||||
|
||||
// V3: Wire vault enrichment worker (async summary + embedding + auto-linking).
|
||||
// Resolves provider independently from consolidation pipeline.
|
||||
if pgStores.Vault != nil {
|
||||
var vaultProvider providers.Provider
|
||||
if names := providerRegistry.ListForTenant(providers.MasterTenantID); len(names) > 0 {
|
||||
vaultProvider, _ = providerRegistry.GetForTenant(providers.MasterTenantID, names[0])
|
||||
}
|
||||
if vaultProvider != nil {
|
||||
cleanupVaultEnrich := vault.RegisterEnrichWorker(vault.EnrichWorkerDeps{
|
||||
VaultStore: pgStores.Vault,
|
||||
Provider: vaultProvider,
|
||||
Model: vaultProvider.DefaultModel(),
|
||||
EventBus: domainBus,
|
||||
})
|
||||
defer cleanupVaultEnrich()
|
||||
slog.Info("vault enrichment worker registered")
|
||||
}
|
||||
if pgStores.Vault != nil && bgProvider != nil {
|
||||
cleanupVaultEnrich := vault.RegisterEnrichWorker(vault.EnrichWorkerDeps{
|
||||
VaultStore: pgStores.Vault,
|
||||
Provider: bgProvider,
|
||||
Model: bgModel,
|
||||
EventBus: domainBus,
|
||||
})
|
||||
defer cleanupVaultEnrich()
|
||||
slog.Info("vault enrichment worker registered", "provider", bgProvider.Name(), "model", bgModel)
|
||||
}
|
||||
|
||||
loadBootstrapFiles(pgStores, workspace, agentCfg)
|
||||
@@ -319,7 +310,11 @@ func runGateway() {
|
||||
// Wire dependencies for system prompt preview parity.
|
||||
if agentsH != nil {
|
||||
agentsH.SetPreviewDeps(toolsReg, skillsLoader)
|
||||
agentsH.SetPreviewStores(pgStores.Teams, pgStores.AgentLinks)
|
||||
var skillAccess store.SkillAccessStore
|
||||
if pgStores.Skills != nil {
|
||||
skillAccess, _ = pgStores.Skills.(store.SkillAccessStore)
|
||||
}
|
||||
agentsH.SetPreviewStores(pgStores.Teams, pgStores.AgentLinks, skillAccess)
|
||||
}
|
||||
|
||||
// External wake/trigger API
|
||||
@@ -543,3 +538,39 @@ func runGateway() {
|
||||
sigCh: sigCh,
|
||||
})
|
||||
}
|
||||
|
||||
// resolveBackgroundProvider picks the LLM provider+model for background workers
|
||||
// (vault enrichment, consolidation). Fallback chain:
|
||||
//
|
||||
// background.provider/model → agent.default_provider/model → first registered provider.
|
||||
func resolveBackgroundProvider(cfg *config.Config, reg *providers.Registry) (providers.Provider, string) {
|
||||
try := func(name, model string) (providers.Provider, string, bool) {
|
||||
if name == "" {
|
||||
return nil, "", false
|
||||
}
|
||||
p, err := reg.GetForTenant(providers.MasterTenantID, name)
|
||||
if err != nil || p == nil {
|
||||
return nil, "", false
|
||||
}
|
||||
if model == "" {
|
||||
model = p.DefaultModel()
|
||||
}
|
||||
return p, model, true
|
||||
}
|
||||
|
||||
// 1. Explicit background config
|
||||
if p, m, ok := try(cfg.Gateway.BackgroundProvider, cfg.Gateway.BackgroundModel); ok {
|
||||
return p, m
|
||||
}
|
||||
// 2. Agent default provider
|
||||
if p, m, ok := try(cfg.Agents.Defaults.Provider, cfg.Agents.Defaults.Model); ok {
|
||||
return p, m
|
||||
}
|
||||
// 3. First registered provider (legacy fallback)
|
||||
if names := reg.ListForTenant(providers.MasterTenantID); len(names) > 0 {
|
||||
if p, m, ok := try(names[0], ""); ok {
|
||||
return p, m
|
||||
}
|
||||
}
|
||||
return nil, ""
|
||||
}
|
||||
@@ -175,7 +175,7 @@ func (d *gatewayDeps) wireHTTPHandlersOnServer(
|
||||
|
||||
// V3: Knowledge Vault document API
|
||||
if d.pgStores != nil && d.pgStores.Vault != nil {
|
||||
d.server.SetVaultHandler(httpapi.NewVaultHandler(d.pgStores.Vault, d.pgStores.Teams, d.workspace, d.domainBus))
|
||||
d.server.SetVaultHandler(httpapi.NewVaultHandler(d.pgStores.Vault, d.pgStores.Teams, d.workspace, d.domainBus, d.pgStores.Agents, d.pgStores.Teams))
|
||||
}
|
||||
|
||||
// V3: Episodic memory summaries API
|
||||
|
||||
@@ -106,6 +106,10 @@ func seedConfigForContext(ctx context.Context, sc store.SystemConfigStore, cfg *
|
||||
setBool("gateway.tool_status", cfg.Gateway.ToolStatus)
|
||||
setInt("gateway.task_recovery_interval_sec", cfg.Gateway.TaskRecoveryIntervalSec)
|
||||
|
||||
// Background workers
|
||||
set("background.provider", cfg.Gateway.BackgroundProvider)
|
||||
set("background.model", cfg.Gateway.BackgroundModel)
|
||||
|
||||
// Tools
|
||||
set("tools.profile", cfg.Tools.Profile)
|
||||
setInt("tools.rate_limit_per_hour", cfg.Tools.RateLimitPerHour)
|
||||
|
||||
Reference in new issue
Block a user