diff --git a/internal/provider/gemini-web/conversation/lookup.go b/internal/provider/gemini-web/conversation/lookup.go index 18debc51..d9b3fb8a 100644 --- a/internal/provider/gemini-web/conversation/lookup.go +++ b/internal/provider/gemini-web/conversation/lookup.go @@ -35,6 +35,30 @@ func BuildStorageHashes(model string, msgs []Message) []PrefixHash { } model = NormalizeModel(model) sanitized := SanitizeAssistantMessages(msgs) - hash := HashConversationGlobal(model, ToStoredMessages(sanitized)) - return []PrefixHash{{Hash: hash, PrefixLen: len(sanitized)}} + if len(sanitized) == 0 { + return nil + } + result := make([]PrefixHash, 0, len(sanitized)) + seen := make(map[string]struct{}, len(sanitized)) + for start := 0; start < len(sanitized); start++ { + segment := sanitized[start:] + if len(segment) < 2 { + continue + } + tailRole := strings.ToLower(strings.TrimSpace(segment[len(segment)-1].Role)) + if tailRole != "assistant" && tailRole != "system" { + continue + } + hash := HashConversationGlobal(model, ToStoredMessages(segment)) + if _, exists := seen[hash]; exists { + continue + } + seen[hash] = struct{}{} + result = append(result, PrefixHash{Hash: hash, PrefixLen: len(segment)}) + } + if len(result) == 0 { + hash := HashConversationGlobal(model, ToStoredMessages(sanitized)) + return []PrefixHash{{Hash: hash, PrefixLen: len(sanitized)}} + } + return result } diff --git a/internal/provider/gemini-web/state.go b/internal/provider/gemini-web/state.go index e9ee8f13..b642d2b1 100644 --- a/internal/provider/gemini-web/state.go +++ b/internal/provider/gemini-web/state.go @@ -553,12 +553,45 @@ func (s *GeminiWebState) persistConversation(modelName string, prep *geminiWebPr stableHash := conversation.HashConversationForAccount(rec.ClientID, prep.underlying, rec.Messages) accountHash := conversation.HashConversationForAccount(s.accountID, prep.underlying, rec.Messages) + suffixSeen := make(map[string]struct{}) + suffixSeen["hash:"+stableHash] = struct{}{} + if accountHash != stableHash { + suffixSeen["hash:"+accountHash] = struct{}{} + } + s.convMu.Lock() s.convData[stableHash] = rec s.convIndex["hash:"+stableHash] = stableHash if accountHash != stableHash { s.convIndex["hash:"+accountHash] = stableHash } + + sanitizedHistory := conversation.SanitizeAssistantMessages(conversation.StoredToMessages(rec.Messages)) + for start := 1; start < len(sanitizedHistory); start++ { + segment := sanitizedHistory[start:] + if len(segment) < 2 { + continue + } + tailRole := strings.ToLower(strings.TrimSpace(segment[len(segment)-1].Role)) + if tailRole != "assistant" && tailRole != "system" { + continue + } + storedSegment := conversation.ToStoredMessages(segment) + segmentStableHash := conversation.HashConversationForAccount(rec.ClientID, prep.underlying, storedSegment) + keyStable := "hash:" + segmentStableHash + if _, exists := suffixSeen[keyStable]; !exists { + s.convIndex[keyStable] = stableHash + suffixSeen[keyStable] = struct{}{} + } + segmentAccountHash := conversation.HashConversationForAccount(s.accountID, prep.underlying, storedSegment) + if segmentAccountHash != segmentStableHash { + keyAccount := "hash:" + segmentAccountHash + if _, exists := suffixSeen[keyAccount]; !exists { + s.convIndex[keyAccount] = stableHash + suffixSeen[keyAccount] = struct{}{} + } + } + } dataSnapshot := make(map[string]ConversationRecord, len(s.convData)) for k, v := range s.convData { dataSnapshot[k] = v