mirror of
https://github.com/tiennm99/goclaw.git
synced 2026-10-13 02:14:39 +00:00
fix(memory): skip user_id filter in shared memory queries
When share_memory is enabled, memory queries now remove the user_id filter entirely instead of filtering by user_id IS NULL. This matches the existing KG shared behavior and allows all users to see all memory documents (global + per-user from everyone).
This commit is contained in:
1 parent
1f85c73a39
commit
9472819334
2 files changed
+73
-10
No files matched your search
@@ -51,7 +51,16 @@ func (s *PGMemoryStore) GetDocument(ctx context.Context, agentID, userID, path s
|
||||
var content string
|
||||
|
||||
var err error
|
||||
if userID == "" {
|
||||
if store.IsSharedMemory(ctx) {
|
||||
// Shared: no user_id filter
|
||||
tc, tcArgs, _, tcErr := scopeClause(ctx, 3)
|
||||
if tcErr != nil {
|
||||
return "", tcErr
|
||||
}
|
||||
err = s.db.QueryRowContext(ctx,
|
||||
"SELECT content FROM memory_documents WHERE agent_id = $1 AND path = $2"+tc+" ORDER BY updated_at DESC LIMIT 1",
|
||||
append([]any{aid, path}, tcArgs...)...).Scan(&content)
|
||||
} else if userID == "" {
|
||||
tc, tcArgs, _, tcErr := scopeClause(ctx, 3)
|
||||
if tcErr != nil {
|
||||
return "", tcErr
|
||||
@@ -100,7 +109,16 @@ func (s *PGMemoryStore) DeleteDocument(ctx context.Context, agentID, userID, pat
|
||||
aid := mustParseUUID(agentID)
|
||||
var res sql.Result
|
||||
var err error
|
||||
if userID == "" {
|
||||
if store.IsSharedMemory(ctx) {
|
||||
// Shared: delete any matching doc regardless of user_id
|
||||
tc, tcArgs, _, tcErr := scopeClause(ctx, 3)
|
||||
if tcErr != nil {
|
||||
return tcErr
|
||||
}
|
||||
res, err = s.db.ExecContext(ctx,
|
||||
"DELETE FROM memory_documents WHERE agent_id = $1 AND path = $2"+tc,
|
||||
append([]any{aid, path}, tcArgs...)...)
|
||||
} else if userID == "" {
|
||||
tc, tcArgs, _, tcErr := scopeClause(ctx, 3)
|
||||
if tcErr != nil {
|
||||
return tcErr
|
||||
@@ -132,7 +150,16 @@ func (s *PGMemoryStore) ListDocuments(ctx context.Context, agentID, userID strin
|
||||
|
||||
var rows *sql.Rows
|
||||
var err error
|
||||
if userID == "" {
|
||||
if store.IsSharedMemory(ctx) {
|
||||
// Shared: list ALL docs for agent (global + per-user from all users)
|
||||
tc, tcArgs, _, tcErr := scopeClause(ctx, 2)
|
||||
if tcErr != nil {
|
||||
return nil, tcErr
|
||||
}
|
||||
rows, err = s.db.QueryContext(ctx,
|
||||
"SELECT path, hash, user_id, updated_at FROM memory_documents WHERE agent_id = $1"+tc,
|
||||
append([]any{aid}, tcArgs...)...)
|
||||
} else if userID == "" {
|
||||
tc, tcArgs, _, tcErr := scopeClause(ctx, 2)
|
||||
if tcErr != nil {
|
||||
return nil, tcErr
|
||||
@@ -187,7 +214,16 @@ func (s *PGMemoryStore) IndexDocument(ctx context.Context, agentID, userID, path
|
||||
|
||||
// Get document ID
|
||||
var docID uuid.UUID
|
||||
if userID == "" {
|
||||
if store.IsSharedMemory(ctx) {
|
||||
// Shared: no user_id filter
|
||||
tc, tcArgs, _, tcErr := scopeClause(ctx, 3)
|
||||
if tcErr != nil {
|
||||
return tcErr
|
||||
}
|
||||
err = s.db.QueryRowContext(ctx,
|
||||
"SELECT id FROM memory_documents WHERE agent_id = $1 AND path = $2"+tc+" ORDER BY updated_at DESC LIMIT 1",
|
||||
append([]any{aid, path}, tcArgs...)...).Scan(&docID)
|
||||
} else if userID == "" {
|
||||
tc, tcArgs, _, tcErr := scopeClause(ctx, 3)
|
||||
if tcErr != nil {
|
||||
return tcErr
|
||||
|
||||
@@ -81,9 +81,22 @@ func (s *PGMemoryStore) ftsSearch(ctx context.Context, query string, agentID any
|
||||
var q string
|
||||
var args []any
|
||||
|
||||
if userID != "" {
|
||||
if store.IsSharedMemory(ctx) {
|
||||
// Shared: no user_id filter — search ALL chunks for agent
|
||||
tc, tcArgs, _, err := scopeClause(ctx, 4)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
limitN := 4 + len(tcArgs)
|
||||
q = fmt.Sprintf(`SELECT path, start_line, end_line, text, user_id,
|
||||
ts_rank(tsv, plainto_tsquery('simple', $1)) AS score
|
||||
FROM memory_chunks
|
||||
WHERE agent_id = $2 AND tsv @@ plainto_tsquery('simple', $3)%s
|
||||
ORDER BY score DESC LIMIT $%d`, tc, limitN)
|
||||
args = append([]any{query, agentID, query}, tcArgs...)
|
||||
args = append(args, limit)
|
||||
} else if userID != "" {
|
||||
// fixed params: $1=query, $2=agentID, $3=query, $4=userID
|
||||
// tenant clause appended at $5 (if filtered), then LIMIT at $5 or $6
|
||||
tc, tcArgs, _, err := scopeClause(ctx, 5)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -99,7 +112,6 @@ func (s *PGMemoryStore) ftsSearch(ctx context.Context, query string, agentID any
|
||||
args = append(args, limit)
|
||||
} else {
|
||||
// fixed params: $1=query, $2=agentID, $3=query
|
||||
// tenant clause at $4 (if filtered), then LIMIT at $4 or $5
|
||||
tc, tcArgs, _, err := scopeClause(ctx, 4)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -136,9 +148,23 @@ func (s *PGMemoryStore) vectorSearch(ctx context.Context, embedding []float32, a
|
||||
var q string
|
||||
var args []any
|
||||
|
||||
if userID != "" {
|
||||
if store.IsSharedMemory(ctx) {
|
||||
// Shared: no user_id filter — search ALL chunks for agent
|
||||
tc, tcArgs, _, err := scopeClause(ctx, 3)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
orderN := 3 + len(tcArgs)
|
||||
limitN := orderN + 1
|
||||
q = fmt.Sprintf(`SELECT path, start_line, end_line, text, user_id,
|
||||
1 - (embedding <=> $1::vector) AS score
|
||||
FROM memory_chunks
|
||||
WHERE agent_id = $2 AND embedding IS NOT NULL%s
|
||||
ORDER BY embedding <=> $%d::vector LIMIT $%d`, tc, orderN, limitN)
|
||||
args = append([]any{vecStr, agentID}, tcArgs...)
|
||||
args = append(args, vecStr, limit)
|
||||
} else if userID != "" {
|
||||
// fixed params: $1=vec, $2=agentID, $3=userID
|
||||
// tenant clause at $4, then ORDER vec at $4+len(tcArgs), LIMIT after
|
||||
tc, tcArgs, _, err := scopeClause(ctx, 4)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -155,7 +181,6 @@ func (s *PGMemoryStore) vectorSearch(ctx context.Context, embedding []float32, a
|
||||
args = append(args, vecStr, limit)
|
||||
} else {
|
||||
// fixed params: $1=vec, $2=agentID
|
||||
// tenant clause at $3, then ORDER vec at $3+len(tcArgs), LIMIT after
|
||||
tc, tcArgs, _, err := scopeClause(ctx, 3)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -189,6 +214,8 @@ func (s *PGMemoryStore) vectorSearch(ctx context.Context, embedding []float32, a
|
||||
|
||||
// hybridMerge combines FTS and vector results with weighted scoring.
|
||||
// Per-user results get a 1.2x boost. Deduplication: user copy wins over global.
|
||||
// NOTE: when shared memory is active, the 1.2x personal boost still applies —
|
||||
// consider removing it in shared mode if all docs should be treated equally.
|
||||
func hybridMerge(fts, vec []scoredChunk, textWeight, vectorWeight float64, currentUserID string) []store.MemorySearchResult {
|
||||
type key struct {
|
||||
Path string
|
||||
|
||||
Reference in new issue
Block a user