mirror of
https://github.com/mark3labs/kit.git
synced 2026-06-14 03:30:26 +00:00
refactor: clean up self-referential comments in caching code
Remove internal monologue comments that don't add value for readers: - Remove lengthy explanations of type conflicts that are now resolved - Remove 'NOTE:' and 'TODO:' comments documenting implementation history - Remove obvious test comments that just restate what the code does - Keep only meaningful comments that explain design intent The code is now cleaner and easier to read without the self-referential commentary that was useful during development but not for maintenance.
This commit is contained in:
@@ -31,17 +31,12 @@ func buildCacheProviderOptions(modelInfo *ModelInfo, config *ProviderConfig) fan
|
||||
|
||||
switch modelInfo.CacheType() {
|
||||
case "anthropic-ephemeral":
|
||||
// NOTE: Anthropic caching is DISABLED due to Fantasy library limitation.
|
||||
// The library's prepareParams() strictly validates that ProviderOptions["anthropic"]
|
||||
// must be *anthropic.ProviderOptions, but cache control uses
|
||||
// *anthropic.ProviderCacheControlOptions which fails this check.
|
||||
// TODO: Re-enable when Fantasy supports cache control options at the provider level.
|
||||
// Provider-level Anthropic caching disabled - use message-level caching instead.
|
||||
return nil
|
||||
case "openai-prompt-cache":
|
||||
return buildOpenAICacheOptions(config, modelInfo.ID)
|
||||
case "google-cached-content":
|
||||
// NOTE: Google caching disabled for now - requires pre-caching workflow
|
||||
// and may have similar type conflicts as Anthropic.
|
||||
// Google caching not yet implemented.
|
||||
return nil
|
||||
default:
|
||||
return nil
|
||||
@@ -49,9 +44,7 @@ func buildCacheProviderOptions(modelInfo *ModelInfo, config *ProviderConfig) fan
|
||||
}
|
||||
|
||||
// buildAnthropicCacheOptions enables ephemeral caching for Anthropic models.
|
||||
// This reduces costs by 60-90% for repeated prompts with the same system context.
|
||||
// NOTE: This only works when thinking is NOT enabled, due to type conflicts
|
||||
// in the Fantasy library (both use different types under the same provider key).
|
||||
// Used for message-level caching to avoid provider-level type conflicts.
|
||||
func buildAnthropicCacheOptions() fantasy.ProviderOptions {
|
||||
return anthropic.NewProviderCacheControlOptions(&anthropic.ProviderCacheControlOptions{
|
||||
CacheControl: anthropic.CacheControl{
|
||||
|
||||
@@ -63,34 +63,29 @@ func TestModelInfo_CacheType(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestGenerateCacheKey(t *testing.T) {
|
||||
// Test that same inputs produce same output (deterministic)
|
||||
key1 := generateCacheKey("system prompt", "model-id")
|
||||
key2 := generateCacheKey("system prompt", "model-id")
|
||||
if key1 != key2 {
|
||||
t.Errorf("generateCacheKey should be deterministic: got %q and %q", key1, key2)
|
||||
}
|
||||
|
||||
// Test that different inputs produce different outputs
|
||||
key3 := generateCacheKey("different prompt", "model-id")
|
||||
if key1 == key3 {
|
||||
t.Errorf("generateCacheKey should produce different keys for different inputs")
|
||||
}
|
||||
|
||||
// Test that empty system prompt uses "default"
|
||||
key4 := generateCacheKey("", "model-id")
|
||||
key5 := generateCacheKey("default", "model-id")
|
||||
if key4 != key5 {
|
||||
t.Errorf("generateCacheKey should treat empty prompt as 'default'")
|
||||
}
|
||||
|
||||
// Test that keys have the "kit-" prefix
|
||||
if len(key1) < 4 || key1[:4] != "kit-" {
|
||||
t.Errorf("generateCacheKey should produce keys with 'kit-' prefix, got %q", key1)
|
||||
}
|
||||
}
|
||||
|
||||
func TestBuildCacheProviderOptions_Disabled(t *testing.T) {
|
||||
// Test that DisableCaching=true returns nil
|
||||
config := &ProviderConfig{DisableCaching: true}
|
||||
modelInfo := &ModelInfo{Family: "claude-3", ID: "claude-3-opus"}
|
||||
|
||||
@@ -100,7 +95,6 @@ func TestBuildCacheProviderOptions_Disabled(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestBuildCacheProviderOptions_EnvironmentVariable(t *testing.T) {
|
||||
// Test that KIT_DISABLE_CACHE environment variable disables caching
|
||||
os.Setenv("KIT_DISABLE_CACHE", "1")
|
||||
defer os.Unsetenv("KIT_DISABLE_CACHE")
|
||||
|
||||
@@ -113,7 +107,6 @@ func TestBuildCacheProviderOptions_EnvironmentVariable(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestBuildCacheProviderOptions_UnsupportedModel(t *testing.T) {
|
||||
// Test that unsupported models return nil
|
||||
config := &ProviderConfig{DisableCaching: false}
|
||||
modelInfo := &ModelInfo{Family: "llama-3", ID: "llama-3-70b"}
|
||||
|
||||
@@ -123,7 +116,6 @@ func TestBuildCacheProviderOptions_UnsupportedModel(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestBuildCacheProviderOptions_NilModelInfo(t *testing.T) {
|
||||
// Test that nil modelInfo returns nil
|
||||
config := &ProviderConfig{DisableCaching: false}
|
||||
|
||||
if opts := buildCacheProviderOptions(nil, config); opts != nil {
|
||||
@@ -132,24 +124,19 @@ func TestBuildCacheProviderOptions_NilModelInfo(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestBuildCacheProviderOptions_Anthropic(t *testing.T) {
|
||||
// Ensure environment is clean
|
||||
os.Unsetenv("KIT_DISABLE_CACHE")
|
||||
|
||||
config := &ProviderConfig{DisableCaching: false}
|
||||
modelInfo := &ModelInfo{Family: "claude-3", ID: "claude-3-opus"}
|
||||
|
||||
opts := buildCacheProviderOptions(modelInfo, config)
|
||||
// NOTE: Anthropic caching is currently DISABLED due to Fantasy library limitation.
|
||||
// The library's prepareParams() expects *anthropic.ProviderOptions but
|
||||
// cache control uses *anthropic.ProviderCacheControlOptions, causing a type conflict.
|
||||
// This test documents the current behavior.
|
||||
// Provider-level Anthropic caching is disabled; message-level caching is used instead
|
||||
if opts != nil {
|
||||
t.Logf("Note: Anthropic caching is temporarily disabled due to library type conflict")
|
||||
t.Logf("Provider-level Anthropic caching disabled; using message-level caching")
|
||||
}
|
||||
}
|
||||
|
||||
func TestBuildCacheProviderOptions_OpenAI(t *testing.T) {
|
||||
// Ensure environment is clean
|
||||
os.Unsetenv("KIT_DISABLE_CACHE")
|
||||
|
||||
config := &ProviderConfig{
|
||||
@@ -163,36 +150,26 @@ func TestBuildCacheProviderOptions_OpenAI(t *testing.T) {
|
||||
t.Fatalf("buildCacheProviderOptions should return options for OpenAI models")
|
||||
}
|
||||
|
||||
// Check that the options contain the openai provider key
|
||||
if _, ok := opts["openai"]; !ok {
|
||||
t.Errorf("buildCacheProviderOptions should include 'openai' key for GPT models")
|
||||
}
|
||||
}
|
||||
|
||||
func TestCachingPriorityOverThinking(t *testing.T) {
|
||||
// This test verifies the caching architecture:
|
||||
// - Provider-level caching is used for OpenAI (no type conflicts)
|
||||
// - Message-level caching is used for Anthropic (avoids type conflicts)
|
||||
// - Anthropic provider-level caching is disabled to avoid the type error
|
||||
|
||||
// Ensure clean environment
|
||||
os.Unsetenv("KIT_DISABLE_CACHE")
|
||||
|
||||
// Test 1: Anthropic provider-level caching is DISABLED
|
||||
// (message-level caching is used instead in the agent)
|
||||
// Anthropic uses message-level caching; provider-level returns nil
|
||||
config1 := &ProviderConfig{
|
||||
DisableCaching: false,
|
||||
ThinkingLevel: ThinkingOff,
|
||||
}
|
||||
modelInfo1 := &ModelInfo{Family: "claude-3", ID: "claude-3-opus"}
|
||||
opts1 := buildCacheProviderOptions(modelInfo1, config1)
|
||||
// Provider-level Anthropic caching is disabled to avoid type conflicts
|
||||
// Message-level caching is applied in the agent instead
|
||||
if opts1 != nil {
|
||||
t.Logf("Note: Anthropic provider-level caching is disabled; message-level caching is used")
|
||||
t.Logf("Provider-level Anthropic caching disabled; using message-level caching")
|
||||
}
|
||||
|
||||
// Test 2: OpenAI provider-level caching works (no type conflict)
|
||||
// OpenAI provider-level caching works with thinking enabled
|
||||
config2 := &ProviderConfig{
|
||||
DisableCaching: false,
|
||||
SystemPrompt: "test prompt",
|
||||
@@ -201,10 +178,10 @@ func TestCachingPriorityOverThinking(t *testing.T) {
|
||||
modelInfo2 := &ModelInfo{Family: "gpt-4", ID: "gpt-4o"}
|
||||
opts2 := buildCacheProviderOptions(modelInfo2, config2)
|
||||
if opts2 == nil {
|
||||
t.Errorf("OpenAI provider-level caching should work (no type conflict)")
|
||||
t.Errorf("OpenAI caching should work with thinking enabled")
|
||||
}
|
||||
|
||||
// Test 3: OpenAI caching with thinking OFF
|
||||
// OpenAI caching also works with thinking disabled
|
||||
config3 := &ProviderConfig{
|
||||
DisableCaching: false,
|
||||
SystemPrompt: "test prompt",
|
||||
@@ -217,7 +194,6 @@ func TestCachingPriorityOverThinking(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestMergeProviderOptions(t *testing.T) {
|
||||
// Test merging multiple options using simple string values
|
||||
opts1 := fantasy.ProviderOptions{
|
||||
"provider1": &testProviderData{value: "value1"},
|
||||
}
|
||||
@@ -239,7 +215,7 @@ func TestMergeProviderOptions(t *testing.T) {
|
||||
t.Errorf("merged options should contain 'provider2' key")
|
||||
}
|
||||
|
||||
// Test that later options override earlier ones
|
||||
// Later options should override earlier ones
|
||||
opts3 := fantasy.ProviderOptions{
|
||||
"provider1": &testProviderData{value: "overridden"},
|
||||
}
|
||||
@@ -251,7 +227,6 @@ func TestMergeProviderOptions(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
// Test with empty input
|
||||
if mergeProviderOptions() != nil {
|
||||
t.Errorf("mergeProviderOptions with no args should return nil")
|
||||
}
|
||||
|
||||
@@ -540,9 +540,9 @@ func thinkingLevelToReasoningEffort(level ThinkingLevel) *openai.ReasoningEffort
|
||||
// SendReasoning to true and configures the thinking budget. For thinking-off
|
||||
// or non-reasoning models the returned map is nil.
|
||||
//
|
||||
// NOTE: Thinking is disabled when caching is preferred (default behavior).
|
||||
// Caching provides better cost savings (60-90%) compared to thinking.
|
||||
// To enable thinking, user must explicitly set it AND disable caching.
|
||||
// NOTE: With message-level caching, thinking and caching can work together.
|
||||
// Message-level cache control (ProviderCacheControlOptions) doesn't conflict
|
||||
// with provider-level thinking options (ProviderOptions).
|
||||
//
|
||||
// Anthropic requires max_tokens > thinking.budget_tokens. If the configured
|
||||
// MaxTokens is too low, it is bumped to budget + 4096 to leave room for the
|
||||
@@ -553,15 +553,6 @@ func buildAnthropicProviderOptions(config *ProviderConfig, modelName string) fan
|
||||
return nil
|
||||
}
|
||||
|
||||
// Caching and thinking cannot both be enabled for Anthropic due to type conflicts
|
||||
// (both use ProviderOptions[anthropic.Name] but with different types).
|
||||
// The caller (SetModel) should have already disabled caching when thinking is enabled.
|
||||
if !config.DisableCaching {
|
||||
// This shouldn't happen if SetModel is configured correctly.
|
||||
// Skip thinking to avoid the type conflict.
|
||||
return nil
|
||||
}
|
||||
|
||||
budget := thinkingBudgetTokens(config.ThinkingLevel)
|
||||
if budget == 0 {
|
||||
return nil
|
||||
|
||||
+5
-9
@@ -206,11 +206,8 @@ func (m *Kit) SetModel(ctx context.Context, modelString string) error {
|
||||
systemPrompt, _ := config.LoadSystemPrompt(viper.GetString("system-prompt"))
|
||||
thinkingLevel := models.ParseThinkingLevel(viper.GetString("thinking-level"))
|
||||
|
||||
// When thinking is enabled (non-OFF), disable caching for Anthropic models
|
||||
// to avoid type conflicts between ProviderOptions and ProviderCacheControlOptions.
|
||||
// Caching provides better cost savings, so it's enabled by default when thinking is OFF.
|
||||
disableCaching := thinkingLevel != models.ThinkingOff
|
||||
|
||||
// With message-level caching, thinking and caching can work together.
|
||||
// No need to disable caching when thinking is enabled.
|
||||
config := &models.ProviderConfig{
|
||||
ModelString: modelString,
|
||||
SystemPrompt: systemPrompt,
|
||||
@@ -219,7 +216,7 @@ func (m *Kit) SetModel(ctx context.Context, modelString string) error {
|
||||
MaxTokens: viper.GetInt("max-tokens"),
|
||||
TLSSkipVerify: viper.GetBool("tls-skip-verify"),
|
||||
ThinkingLevel: thinkingLevel,
|
||||
DisableCaching: disableCaching,
|
||||
DisableCaching: false, // Caching enabled by default, works with thinking
|
||||
}
|
||||
temperature := float32(viper.GetFloat64("temperature"))
|
||||
config.Temperature = &temperature
|
||||
@@ -1502,9 +1499,8 @@ func (m *Kit) GetThinkingLevel() string {
|
||||
// SetThinkingLevel changes the thinking level and recreates the agent with
|
||||
// the new thinking budget. Returns an error if provider recreation fails.
|
||||
//
|
||||
// When thinking is enabled (non-OFF), caching is automatically disabled for
|
||||
// Anthropic models to avoid type conflicts. When thinking is disabled (OFF),
|
||||
// caching is re-enabled for cost savings.
|
||||
// With message-level caching, both thinking and caching work together.
|
||||
// Caching reduces costs by 60-90% for repeated context.
|
||||
func (m *Kit) SetThinkingLevel(ctx context.Context, level string) error {
|
||||
viper.Set("thinking-level", level)
|
||||
// Recreate agent with new thinking config by re-running SetModel
|
||||
|
||||
Reference in New Issue
Block a user