feat(providers): enable Codex prompt cache controls (#1310)

This commit is contained in:
Zezae Oh authored and GitHub committed 2026-06-30 18:25:28 +07:00
1 parent b8726ff4f4
commit 968745bd16
9 files changed
+176 -5

No files matched your search

+39 -2
View File
@@ -2,6 +2,8 @@ package agent
import (
"context"
"crypto/sha256"
"encoding/hex"
"fmt"
"log/slog"
"strings"
@@ -328,8 +330,12 @@ func (l *Loop) makeCallLLM(req *RunRequest, emitRun func(AgentEvent)) func(ctx c
chatReq.Options[providers.OptPeerKind] = req.PeerKind
chatReq.Options[providers.OptLocalKey] = req.LocalKey
chatReq.Options[providers.OptWorkspace] = tools.ToolWorkspaceFromCtx(ctx)
if tid := store.TenantIDFromContext(ctx); tid != uuid.Nil {
chatReq.Options[providers.OptTenantID] = tid.String()
tenantID := store.TenantIDFromContext(ctx)
if tenantID != uuid.Nil {
chatReq.Options[providers.OptTenantID] = tenantID.String()
}
if supportsPromptCacheParams(provider) {
setDefaultPromptCacheOptions(chatReq.Options, tenantID, l.agentUUID, provider.Name(), req.SessionKey)
}
// Reasoning decision: resolve effort level for thinking models (o3, DeepSeek-R1, Kimi).
@@ -697,3 +703,34 @@ func (l *Loop) reserveLLMUsageFor(ctx context.Context, req *RunRequest, iteratio
MaxOutputTokens: l.maxOutputTokensFromRequest(chatReq),
})
}
func supportsPromptCacheParams(provider providers.Provider) bool {
switch provider.(type) {
case *providers.CodexProvider, *providers.ChatGPTOAuthRouter:
return true
default:
return false
}
}
func setDefaultPromptCacheOptions(opts map[string]any, tenantID, agentID uuid.UUID, providerName, sessionKey string) {
if opts == nil {
return
}
if _, ok := opts[providers.OptPromptCacheKey]; !ok {
opts[providers.OptPromptCacheKey] = defaultPromptCacheKey(tenantID, agentID, providerName, sessionKey)
}
if _, ok := opts[providers.OptPromptCacheRetention]; !ok {
opts[providers.OptPromptCacheRetention] = "24h"
}
}
func defaultPromptCacheKey(tenantID, agentID uuid.UUID, providerName, sessionKey string) string {
h := sha256.Sum256([]byte(strings.Join([]string{
tenantID.String(),
agentID.String(),
providerName,
sessionKey,
}, "\x00")))
return "goclaw/" + hex.EncodeToString(h[:16])
}
@@ -2,8 +2,11 @@ package agent
import (
"context"
"strings"
"testing"
"github.com/google/uuid"
"github.com/nextlevelbuilder/goclaw/internal/pipeline"
"github.com/nextlevelbuilder/goclaw/internal/providers"
"github.com/nextlevelbuilder/goclaw/pkg/protocol"
@@ -54,3 +57,48 @@ func TestMakeCallLLM_StreamsFinalThinkingWhenNoThinkingChunkArrives(t *testing.T
t.Fatalf("thinking payload = %+v", thinking[0].Payload)
}
}
func TestPromptCacheOptionsHelpers(t *testing.T) {
tenantID := uuid.MustParse("11111111-1111-1111-1111-111111111111")
agentID := uuid.MustParse("22222222-2222-2222-2222-222222222222")
key1 := defaultPromptCacheKey(tenantID, agentID, "codex", "session-a")
key2 := defaultPromptCacheKey(tenantID, agentID, "codex", "session-a")
key3 := defaultPromptCacheKey(tenantID, agentID, "codex", "session-b")
if key1 != key2 {
t.Fatalf("defaultPromptCacheKey not stable: %q != %q", key1, key2)
}
if key1 == key3 {
t.Fatal("defaultPromptCacheKey should vary by session")
}
if !strings.HasPrefix(key1, "goclaw/") {
t.Fatalf("defaultPromptCacheKey = %q, want goclaw/ prefix", key1)
}
opts := map[string]any{}
setDefaultPromptCacheOptions(opts, tenantID, agentID, "codex", "session-a")
if opts[providers.OptPromptCacheKey] != key1 {
t.Fatalf("prompt cache key = %v, want %s", opts[providers.OptPromptCacheKey], key1)
}
if opts[providers.OptPromptCacheRetention] != "24h" {
t.Fatalf("prompt cache retention = %v, want 24h", opts[providers.OptPromptCacheRetention])
}
opts = map[string]any{
providers.OptPromptCacheKey: "custom-key",
providers.OptPromptCacheRetention: "in_memory",
}
setDefaultPromptCacheOptions(opts, tenantID, agentID, "codex", "session-a")
if opts[providers.OptPromptCacheKey] != "custom-key" || opts[providers.OptPromptCacheRetention] != "in_memory" {
t.Fatalf("custom prompt cache options were overwritten: %+v", opts)
}
}
func TestSupportsPromptCacheParams(t *testing.T) {
if !supportsPromptCacheParams(providers.NewCodexProvider("codex", nil, "", "")) {
t.Fatal("CodexProvider should support prompt cache params")
}
if supportsPromptCacheParams(finalThinkingStreamProvider{}) {
t.Fatal("generic provider should not support prompt cache params")
}
}