Files
CLIProxyAPI/internal/cache/antigravity_interactions_session.go
Luis Pater cbc4cd5532 feat(interactions): support antigravity session continuation and model detail lookup
- Add `InteractionsSessionCache` to preserve Antigravity interaction continuations across tool call rounds
- Automatically restore and inject `previous_interaction_id` and `environment_id` in Gemini interactions executor
- Normalize Antigravity tool identity mappings in OpenAI responses translator
- Add `/v1/models/*model` endpoint to support retrieving individual model details

Closes: #5190
2026-10-04 04:23:31 +08:00

77 lines
2.0 KiB
Go

package cache
import (
"sort"
"strings"
"sync"
"time"
)
// InteractionsContinuation contains only upstream continuation identifiers.
type InteractionsContinuation struct {
ID string
Environment string
}
type interactionsContinuationEntry struct {
state InteractionsContinuation
expires time.Time
}
// InteractionsSessionCache is a bounded, process-local cache. Keys must already
// isolate the caller, credential, model, conversation, and pending call IDs.
type InteractionsSessionCache struct {
mu sync.Mutex
entries map[string]interactionsContinuationEntry
now func() time.Time
}
func NewInteractionsSessionCache() *InteractionsSessionCache {
return &InteractionsSessionCache{entries: make(map[string]interactionsContinuationEntry), now: time.Now}
}
// InteractionsCallKey rejects ambiguous or incomplete call sets.
func InteractionsCallKey(ids []string) string {
ids = append([]string(nil), ids...)
sort.Strings(ids)
for i, id := range ids {
if id == "" || strings.ContainsRune(id, '\x00') || (i > 0 && ids[i-1] == id) {
return ""
}
}
return strings.Join(ids, "\x00")
}
func (c *InteractionsSessionCache) Get(key string) (InteractionsContinuation, bool) {
c.mu.Lock()
defer c.mu.Unlock()
entry, ok := c.entries[key]
if !ok || !c.now().Before(entry.expires) {
delete(c.entries, key)
return InteractionsContinuation{}, false
}
return entry.state, true
}
func (c *InteractionsSessionCache) Put(key string, state InteractionsContinuation) {
c.mu.Lock()
defer c.mu.Unlock()
now := c.now()
for k, entry := range c.entries {
if !now.Before(entry.expires) {
delete(c.entries, k)
}
}
if _, exists := c.entries[key]; !exists && len(c.entries) >= 1024 {
var oldestKey string
var oldest time.Time
for k, entry := range c.entries {
if oldest.IsZero() || entry.expires.Before(oldest) {
oldestKey, oldest = k, entry.expires
}
}
delete(c.entries, oldestKey)
}
c.entries[key] = interactionsContinuationEntry{state: state, expires: now.Add(30 * time.Minute)}
}