diff --git a/.github/workflows/scripts/validate-helm-config-fields.sh b/.github/workflows/scripts/validate-helm-config-fields.sh
index 352ef88cde..11de5c7356 100755
--- a/.github/workflows/scripts/validate-helm-config-fields.sh
+++ b/.github/workflows/scripts/validate-helm-config-fields.sh
@@ -637,7 +637,6 @@ bifrost:
cache_by_model: true
cache_by_provider: false
exclude_system_prompt: true
- cleanup_on_shutdown: true
vector_store_namespace: "bifrost-cache"
otel:
enabled: true
@@ -710,7 +709,6 @@ assert_field_value 'plugins: semantic_cache conversation_history_threshold' '.pl
assert_field_value 'plugins: semantic_cache cache_by_model' '.plugins.[4].config.cache_by_model' 'true'
assert_field_value 'plugins: semantic_cache cache_by_provider' '.plugins.[4].config.cache_by_provider' 'false'
assert_field_value 'plugins: semantic_cache exclude_system_prompt' '.plugins.[4].config.exclude_system_prompt' 'true'
-assert_field_value 'plugins: semantic_cache cleanup_on_shutdown' '.plugins.[4].config.cleanup_on_shutdown' 'true'
assert_field_value 'plugins: semantic_cache vector_store_namespace' '.plugins.[4].config.vector_store_namespace' '"bifrost-cache"'
# OTEL plugin
diff --git a/core/providers/anthropic/types.go b/core/providers/anthropic/types.go
index 35874f658a..08eb9a7111 100644
--- a/core/providers/anthropic/types.go
+++ b/core/providers/anthropic/types.go
@@ -1200,18 +1200,18 @@ const (
type AnthropicToolName string
const (
- AnthropicToolNameComputer AnthropicToolName = "computer"
- AnthropicToolNameWebSearch AnthropicToolName = "web_search"
- AnthropicToolNameWebFetch AnthropicToolName = "web_fetch"
- AnthropicToolNameBash AnthropicToolName = "bash"
- AnthropicToolNameTextEditor AnthropicToolName = "str_replace_based_edit_tool"
+ AnthropicToolNameComputer AnthropicToolName = "computer"
+ AnthropicToolNameWebSearch AnthropicToolName = "web_search"
+ AnthropicToolNameWebFetch AnthropicToolName = "web_fetch"
+ AnthropicToolNameBash AnthropicToolName = "bash"
+ AnthropicToolNameTextEditor AnthropicToolName = "str_replace_based_edit_tool"
// AnthropicToolNameTextEditorLegacy is the name required for text_editor_20250124
// and text_editor_20250429. Newer text_editor_20250728+ use AnthropicToolNameTextEditor.
AnthropicToolNameTextEditorLegacy AnthropicToolName = "str_replace_editor"
- AnthropicToolNameCodeExecution AnthropicToolName = "code_execution"
- AnthropicToolNameMemory AnthropicToolName = "memory"
- AnthropicToolNameToolSearchBM25 AnthropicToolName = "tool_search_tool_bm25"
- AnthropicToolNameToolSearchRegex AnthropicToolName = "tool_search_tool_regex"
+ AnthropicToolNameCodeExecution AnthropicToolName = "code_execution"
+ AnthropicToolNameMemory AnthropicToolName = "memory"
+ AnthropicToolNameToolSearchBM25 AnthropicToolName = "tool_search_tool_bm25"
+ AnthropicToolNameToolSearchRegex AnthropicToolName = "tool_search_tool_regex"
)
type AnthropicToolComputerUse struct {
diff --git a/docs/deployment-guides/helm/plugins.mdx b/docs/deployment-guides/helm/plugins.mdx
index 887cf85600..c0f8b695b8 100644
--- a/docs/deployment-guides/helm/plugins.mdx
+++ b/docs/deployment-guides/helm/plugins.mdx
@@ -178,7 +178,6 @@ Two modes:
| `bifrost.plugins.semanticCache.config.cache_by_model` | Include model name in cache key | `true` |
| `bifrost.plugins.semanticCache.config.cache_by_provider` | Include provider name in cache key | `true` |
| `bifrost.plugins.semanticCache.config.exclude_system_prompt` | Exclude system prompt from cache key | `false` |
-| `bifrost.plugins.semanticCache.config.cleanup_on_shutdown` | Delete cache data on pod shutdown | `false` |
**Semantic mode (with OpenAI embeddings + Weaviate):**
diff --git a/docs/features/semantic-caching.mdx b/docs/features/semantic-caching.mdx
index 5413649ea2..d2eb9b2cba 100644
--- a/docs/features/semantic-caching.mdx
+++ b/docs/features/semantic-caching.mdx
@@ -198,7 +198,6 @@ bifrostConfig := schemas.BifrostConfig{
"embedding_model": "text-embedding-3-small",
"dimension": 1536,
- "cleanup_on_shutdown": true,
"ttl": "5m",
"threshold": 0.8,
@@ -281,7 +280,6 @@ bifrost:
config:
dimension: 1
ttl: "5m"
- cleanup_on_shutdown: true
cache_by_model: true
cache_by_provider: true
```
@@ -299,7 +297,6 @@ bifrost:
"config": {
"dimension": 1,
"ttl": "5m",
- "cleanup_on_shutdown": true,
"cache_by_model": true,
"cache_by_provider": true
}
@@ -671,16 +668,11 @@ The semantic cache automatically handles cleanup to prevent storage bloat:
- Clear all entries for a cache key
- Restart Bifrost to clear all cache data
-
-The semantic cache namespace and all its cache entries are deleted when Bifrost client shuts down **only if `cleanup_on_shutdown` is set to `true`**. By default (`cleanup_on_shutdown: false`), cache data persists between restarts. DO NOT use the plugin's namespace for external purposes.
-
-
**Dimension / Provider / Model Changes**: A vector store namespace can only hold vectors of **one** dimension. If you change `dimension` (or switch to an embedding `provider`/`model` that produces a different vector size), the existing namespace is **not** recreated automatically — `CreateNamespace` is a no-op when the class/collection already exists. Subsequent writes will fail (vector-size mismatch) and reads will silently miss. Before saving the change, either:
- point `vector_store_namespace` at a fresh name, or
- drop the existing class/index in your vector store, or
-- set `cleanup_on_shutdown: true` and restart so the old namespace is removed first.
---
diff --git a/docs/migration-guides/v1.5.0.mdx b/docs/migration-guides/v1.5.0.mdx
index 95d4a49dd8..0cf4eca096 100644
--- a/docs/migration-guides/v1.5.0.mdx
+++ b/docs/migration-guides/v1.5.0.mdx
@@ -751,6 +751,56 @@ If you parse `cache_debug` and assumed it was either absent or had `cache_hit: t
---
+## Breaking Change 16: Semantic Cache `cleanup_on_shutdown` Removed
+
+The `cleanup_on_shutdown` option on the semantic cache plugin config has been removed. Cache entries and the vector store namespace are no longer deleted when Bifrost shuts down — cache data always persists between restarts.
+
+**Before:**
+```json
+{
+ "plugins": {
+ "semantic_cache": {
+ "config": {
+ "ttl": "5m",
+ "cleanup_on_shutdown": true
+ }
+ }
+ }
+}
+```
+
+**After:**
+```json
+{
+ "plugins": {
+ "semantic_cache": {
+ "config": {
+ "ttl": "5m"
+ }
+ }
+ }
+}
+```
+
+The field is no longer part of the config schema and will be rejected by validation. Remove it from `config.json`, Helm values, and any `PUT /api/config` payloads.
+
+### How to clear cache data
+
+If you previously relied on `cleanup_on_shutdown: true` to drop the cache on restart, use one of the supported invalidation paths instead:
+
+- `DELETE /api/cache/clear/{cacheId}` — invalidate a single entry
+- `DELETE /api/cache/clear-by-key/{cacheKey}` — invalidate all entries for a cache key
+- Drop the vector store class/collection or point `vector_store_namespace` at a fresh name to start clean
+
+### Dimension / provider / model changes
+
+The previous `cleanup_on_shutdown: true` + restart workflow was the documented escape hatch for changing `dimension` (or switching to an embedding `provider`/`model` that produces a different vector size). That option is gone. To rotate the namespace now, either:
+
+- point `vector_store_namespace` at a fresh name, or
+- drop the existing class/index in your vector store before restarting
+
+---
+
## Opting Out: `version: 1` Compatibility Mode
If you are not ready to adopt the new deny-by-default semantics, you can add a single field to `config.json` to restore v1.4.x behavior for all allow-list fields loaded from that file:
@@ -853,6 +903,10 @@ Remove `allow_direct_keys` from `config.json` and any `PUT /api/config` payloads
Replace `DELETE /api/cache/clear/{requestId}` with `DELETE /api/cache/clear/{cacheId}`, and replace `plugin.ClearCacheForRequestID(...)` with `plugin.ClearCacheForCacheID(...)`. Read the cache ID from `extra_fields.cache_debug.cache_id` on the response (now populated on misses too).
+
+
+Drop the `cleanup_on_shutdown` field from the semantic cache plugin config in `config.json`, Helm values, and any API payloads — it is no longer part of the schema. Cache data now always persists across restarts; use the cache clear endpoints or rotate `vector_store_namespace` to drop entries.
+
---
diff --git a/examples/k8s/examples/values-semantic-search-redis.yaml b/examples/k8s/examples/values-semantic-search-redis.yaml
index 4d9cd18224..8867162e2f 100644
--- a/examples/k8s/examples/values-semantic-search-redis.yaml
+++ b/examples/k8s/examples/values-semantic-search-redis.yaml
@@ -18,7 +18,6 @@ bifrost:
cache_by_model: true
cache_by_provider: true
exclude_system_prompt: true
- cleanup_on_shutdown: false
vector_store_namespace: "bifrost-semantic-cache"
vectorStore:
diff --git a/examples/k8s/examples/values-semantic-search-weaviate.yaml b/examples/k8s/examples/values-semantic-search-weaviate.yaml
index 7691106be7..0d00d87d1a 100644
--- a/examples/k8s/examples/values-semantic-search-weaviate.yaml
+++ b/examples/k8s/examples/values-semantic-search-weaviate.yaml
@@ -18,7 +18,6 @@ bifrost:
cache_by_model: true
cache_by_provider: true
exclude_system_prompt: false
- cleanup_on_shutdown: false
vector_store_namespace: "bifrost-semantic-cache"
vectorStore:
diff --git a/helm-charts/bifrost/templates/_helpers.tpl b/helm-charts/bifrost/templates/_helpers.tpl
index 9849b760f4..1e5c6312ed 100644
--- a/helm-charts/bifrost/templates/_helpers.tpl
+++ b/helm-charts/bifrost/templates/_helpers.tpl
@@ -1100,9 +1100,6 @@ false
{{- if hasKey $inputConfig "exclude_system_prompt" }}
{{- $_ := set $scConfig "exclude_system_prompt" $inputConfig.exclude_system_prompt }}
{{- end }}
-{{- if hasKey $inputConfig "cleanup_on_shutdown" }}
-{{- $_ := set $scConfig "cleanup_on_shutdown" $inputConfig.cleanup_on_shutdown }}
-{{- end }}
{{- $plugin := dict "enabled" true "name" "semantic_cache" "config" $scConfig }}
{{- if hasKey .Values.bifrost.plugins.semanticCache "version" }}{{- $_ := set $plugin "version" (.Values.bifrost.plugins.semanticCache.version | int) }}{{- end }}
{{- $plugins = append $plugins $plugin }}
diff --git a/helm-charts/bifrost/values.schema.json b/helm-charts/bifrost/values.schema.json
index 5146bfc353..a8ac1256b3 100644
--- a/helm-charts/bifrost/values.schema.json
+++ b/helm-charts/bifrost/values.schema.json
@@ -818,9 +818,6 @@
"exclude_system_prompt": {
"type": "boolean"
},
- "cleanup_on_shutdown": {
- "type": "boolean"
- },
"vector_store_namespace": {
"type": "string"
},
diff --git a/helm-charts/bifrost/values.yaml b/helm-charts/bifrost/values.yaml
index 3fb87dbf88..c316f441df 100644
--- a/helm-charts/bifrost/values.yaml
+++ b/helm-charts/bifrost/values.yaml
@@ -420,7 +420,6 @@ bifrost:
cache_by_model: true
cache_by_provider: true
exclude_system_prompt: false
- cleanup_on_shutdown: false
vector_store_namespace: ""
otel:
diff --git a/plugins/semanticcache/config_unmarshal_test.go b/plugins/semanticcache/config_unmarshal_test.go
index d38ad31fc6..5d2edd44f1 100644
--- a/plugins/semanticcache/config_unmarshal_test.go
+++ b/plugins/semanticcache/config_unmarshal_test.go
@@ -43,7 +43,6 @@ func TestUnmarshalJSON_AllFields(t *testing.T) {
input := `{
"provider": "openai",
"embedding_model": "text-embedding-3-small",
- "cleanup_on_shutdown": true,
"dimension": 1536,
"ttl": "10m",
"threshold": 0.9,
@@ -66,9 +65,6 @@ func TestUnmarshalJSON_AllFields(t *testing.T) {
if config.EmbeddingModel != "text-embedding-3-small" {
t.Errorf("EmbeddingModel: expected %q, got %q", "text-embedding-3-small", config.EmbeddingModel)
}
- if !config.CleanUpOnShutdown {
- t.Error("CleanUpOnShutdown: expected true")
- }
if config.Dimension != 1536 {
t.Errorf("Dimension: expected 1536, got %d", config.Dimension)
}
@@ -136,32 +132,32 @@ func TestUnmarshalJSON_TTLFormats(t *testing.T) {
func TestUnmarshalJSON_BoolPointerFields(t *testing.T) {
tests := []struct {
- name string
- json string
- expectCacheByModel *bool
- expectCacheByProv *bool
- expectExcludeSys *bool
+ name string
+ json string
+ expectCacheByModel *bool
+ expectCacheByProv *bool
+ expectExcludeSys *bool
}{
{
- name: "all set to true",
- json: `{"dimension": 1536, "cache_by_model": true, "cache_by_provider": true, "exclude_system_prompt": true}`,
- expectCacheByModel: bifrost.Ptr(true),
- expectCacheByProv: bifrost.Ptr(true),
- expectExcludeSys: bifrost.Ptr(true),
+ name: "all set to true",
+ json: `{"dimension": 1536, "cache_by_model": true, "cache_by_provider": true, "exclude_system_prompt": true}`,
+ expectCacheByModel: bifrost.Ptr(true),
+ expectCacheByProv: bifrost.Ptr(true),
+ expectExcludeSys: bifrost.Ptr(true),
},
{
- name: "all set to false",
- json: `{"dimension": 1536, "cache_by_model": false, "cache_by_provider": false, "exclude_system_prompt": false}`,
- expectCacheByModel: bifrost.Ptr(false),
- expectCacheByProv: bifrost.Ptr(false),
- expectExcludeSys: bifrost.Ptr(false),
+ name: "all set to false",
+ json: `{"dimension": 1536, "cache_by_model": false, "cache_by_provider": false, "exclude_system_prompt": false}`,
+ expectCacheByModel: bifrost.Ptr(false),
+ expectCacheByProv: bifrost.Ptr(false),
+ expectExcludeSys: bifrost.Ptr(false),
},
{
- name: "all omitted",
- json: `{"dimension": 1536}`,
- expectCacheByModel: nil,
- expectCacheByProv: nil,
- expectExcludeSys: nil,
+ name: "all omitted",
+ json: `{"dimension": 1536}`,
+ expectCacheByModel: nil,
+ expectCacheByProv: nil,
+ expectExcludeSys: nil,
},
}
diff --git a/plugins/semanticcache/main.go b/plugins/semanticcache/main.go
index 798c98a52d..9fefc497f2 100644
--- a/plugins/semanticcache/main.go
+++ b/plugins/semanticcache/main.go
@@ -31,7 +31,6 @@ type Config struct {
EmbeddingModel string `json:"embedding_model,omitempty"` // Model to use for generating embeddings (optional)
// Plugin behavior settings
- CleanUpOnShutdown bool `json:"cleanup_on_shutdown,omitempty"` // Clean up cache on shutdown (default: false)
TTL time.Duration `json:"ttl,omitempty"` // Time-to-live for cached responses (default: 5min)
Threshold float64 `json:"threshold,omitempty"` // Cosine similarity threshold for semantic matching (0 = unset → default 0.8)
VectorStoreNamespace string `json:"vector_store_namespace,omitempty"` // Namespace for vector store (optional)
@@ -684,43 +683,6 @@ func (plugin *Plugin) Cleanup() error {
// and stopCh is still in memory. This call evicts those before we return.
plugin.cleanupOldStreamAccumulators()
- // Only clean up cache entries if configured to do so
- if !plugin.config.CleanUpOnShutdown {
- plugin.logger.Debug("Cleanup on shutdown is disabled, skipping cache cleanup")
- return nil
- }
-
- // Clean up all cache entries created by this plugin
- ctx, cancel := context.WithTimeout(context.Background(), CacheSetTimeout)
- defer cancel()
-
- plugin.logger.Debug("Starting cleanup of cache entries...")
-
- // Delete all cache entries created by this plugin
- queries := []vectorstore.Query{
- {
- Field: "from_bifrost_semantic_cache_plugin",
- Operator: vectorstore.QueryOperatorEqual,
- Value: true,
- },
- }
-
- results, err := plugin.store.DeleteAll(ctx, plugin.config.VectorStoreNamespace, queries)
- if err != nil {
- return fmt.Errorf("failed to delete cache entries: %w", err)
- }
-
- for _, result := range results {
- if result.Status == vectorstore.DeleteStatusError {
- plugin.logger.Warn("Failed to delete cache entry: %s", result.Error)
- }
- }
- plugin.logger.Debug("Cleanup completed - deleted all cache entries")
-
- if err := plugin.store.DeleteNamespace(ctx, plugin.config.VectorStoreNamespace); err != nil {
- return fmt.Errorf("failed to delete namespace: %w", err)
- }
-
return nil
}
diff --git a/plugins/semanticcache/plugin_api_test.go b/plugins/semanticcache/plugin_api_test.go
index 908e88149d..78dbbd45af 100644
--- a/plugins/semanticcache/plugin_api_test.go
+++ b/plugins/semanticcache/plugin_api_test.go
@@ -16,15 +16,15 @@ import (
// Delete / DeleteAll / DeleteNamespace calls so the tests can assert on the
// public Clear* APIs and on Cleanup teardown behavior.
type observableStore struct {
- mu sync.Mutex
- chunks map[string]vectorstore.SearchResult
- addIDs []string
- deleteIDs []string
- deleteAllQueries [][]vectorstore.Query
- namespaceDeletes int
- deleteAllErr error
- deleteErr error
- deleteAllResults []vectorstore.DeleteResult
+ mu sync.Mutex
+ chunks map[string]vectorstore.SearchResult
+ addIDs []string
+ deleteIDs []string
+ deleteAllQueries [][]vectorstore.Query
+ namespaceDeletes int
+ deleteAllErr error
+ deleteErr error
+ deleteAllResults []vectorstore.DeleteResult
}
func newObservableStore() *observableStore {
@@ -85,10 +85,9 @@ func (s *observableStore) DeleteAll(ctx context.Context, ns string, queries []ve
}
func (s *observableStore) Close(ctx context.Context, ns string) error { return nil }
-func newTestPlugin(t *testing.T, store vectorstore.VectorStore, cleanupOnShutdown bool) *Plugin {
+func newTestPlugin(t *testing.T, store vectorstore.VectorStore) *Plugin {
t.Helper()
cfg := getDefaultTestConfig()
- cfg.CleanUpOnShutdown = cleanupOnShutdown
return &Plugin{
store: store,
config: cfg,
@@ -102,7 +101,7 @@ func newTestPlugin(t *testing.T, store vectorstore.VectorStore, cleanupOnShutdow
// -----------------------------------------------------------------------------
func TestClearCacheForCacheID_EmptyIDRejected(t *testing.T) {
- plugin := newTestPlugin(t, newObservableStore(), false)
+ plugin := newTestPlugin(t, newObservableStore())
if err := plugin.ClearCacheForCacheID(""); err == nil {
t.Fatal("expected error for empty cache ID")
}
@@ -110,7 +109,7 @@ func TestClearCacheForCacheID_EmptyIDRejected(t *testing.T) {
func TestClearCacheForCacheID_PointDelete(t *testing.T) {
store := newObservableStore()
- plugin := newTestPlugin(t, store, false)
+ plugin := newTestPlugin(t, store)
if err := plugin.ClearCacheForCacheID("cache-abc"); err != nil {
t.Fatalf("ClearCacheForCacheID failed: %v", err)
@@ -128,7 +127,7 @@ func TestClearCacheForCacheID_PointDelete(t *testing.T) {
func TestClearCacheForKey_FiltersByCacheKeyAndPluginMarker(t *testing.T) {
store := newObservableStore()
- plugin := newTestPlugin(t, store, false)
+ plugin := newTestPlugin(t, store)
if err := plugin.ClearCacheForKey("session-42"); err != nil {
t.Fatalf("ClearCacheForKey failed: %v", err)
@@ -162,7 +161,7 @@ func TestClearCacheForKey_FiltersByCacheKeyAndPluginMarker(t *testing.T) {
// -----------------------------------------------------------------------------
func TestStampCacheDebugForMiss_AlwaysSetsCacheID(t *testing.T) {
- plugin := newTestPlugin(t, newObservableStore(), false)
+ plugin := newTestPlugin(t, newObservableStore())
state := &cacheState{}
extra := &schemas.BifrostResponseExtraFields{}
@@ -184,7 +183,7 @@ func TestStampCacheDebugForMiss_AlwaysSetsCacheID(t *testing.T) {
}
func TestStampCacheDebugForMiss_AddsTelemetryWhenSemanticRan(t *testing.T) {
- plugin := newTestPlugin(t, newObservableStore(), false)
+ plugin := newTestPlugin(t, newObservableStore())
state := &cacheState{EmbeddingsInputTokens: 42}
extra := &schemas.BifrostResponseExtraFields{}
@@ -199,7 +198,7 @@ func TestStampCacheDebugForMiss_AddsTelemetryWhenSemanticRan(t *testing.T) {
}
func TestStampCacheDebugForMiss_StreamSkipsNonFinalChunks(t *testing.T) {
- plugin := newTestPlugin(t, newObservableStore(), false)
+ plugin := newTestPlugin(t, newObservableStore())
state := &cacheState{}
extra := &schemas.BifrostResponseExtraFields{}
@@ -216,7 +215,7 @@ func TestStampCacheDebugForMiss_StreamSkipsNonFinalChunks(t *testing.T) {
func TestCleanup_SkipsEntryDeletionWhenDisabled(t *testing.T) {
store := newObservableStore()
- plugin := newTestPlugin(t, store, false) // CleanUpOnShutdown=false
+ plugin := newTestPlugin(t, store) // CleanUpOnShutdown=false
if err := plugin.Cleanup(); err != nil {
t.Fatalf("Cleanup failed: %v", err)
@@ -232,26 +231,8 @@ func TestCleanup_SkipsEntryDeletionWhenDisabled(t *testing.T) {
}
}
-func TestCleanup_DeletesEntriesAndNamespaceWhenEnabled(t *testing.T) {
- store := newObservableStore()
- plugin := newTestPlugin(t, store, true) // CleanUpOnShutdown=true
-
- if err := plugin.Cleanup(); err != nil {
- t.Fatalf("Cleanup failed: %v", err)
- }
-
- store.mu.Lock()
- defer store.mu.Unlock()
- if len(store.deleteAllQueries) != 1 {
- t.Fatalf("expected one DeleteAll call, got %d", len(store.deleteAllQueries))
- }
- if store.namespaceDeletes != 1 {
- t.Fatalf("expected one DeleteNamespace call, got %d", store.namespaceDeletes)
- }
-}
-
func TestCleanup_DrainsPendingWriters(t *testing.T) {
- plugin := newTestPlugin(t, newObservableStore(), false)
+ plugin := newTestPlugin(t, newObservableStore())
var done atomic.Bool
plugin.writersWg.Add(1)
@@ -274,7 +255,7 @@ func TestCleanup_DrainsPendingWriters(t *testing.T) {
// -----------------------------------------------------------------------------
func TestCleanupOldCacheStates_ReapsOldEntries(t *testing.T) {
- plugin := newTestPlugin(t, newObservableStore(), false)
+ plugin := newTestPlugin(t, newObservableStore())
plugin.cacheStates.Store("old-1", &cacheState{CreatedAt: time.Now().Add(-2 * cacheStateMaxAge)})
plugin.cacheStates.Store("old-2", &cacheState{CreatedAt: time.Now().Add(-2 * cacheStateMaxAge)})
@@ -298,7 +279,7 @@ func TestCleanupOldCacheStates_ReapsOldEntries(t *testing.T) {
// -----------------------------------------------------------------------------
func TestCleanupOldStreamAccumulators_ReapsByLastSeenAt(t *testing.T) {
- plugin := newTestPlugin(t, newObservableStore(), false)
+ plugin := newTestPlugin(t, newObservableStore())
plugin.streamAccumulators.Store("old", &StreamAccumulator{
RequestID: "old",
@@ -324,7 +305,7 @@ func TestCleanupOldStreamAccumulators_ReapsByLastSeenAt(t *testing.T) {
// -----------------------------------------------------------------------------
func TestBuildStreamingResponseFromResult_ConsumerAbandonment(t *testing.T) {
- plugin := newTestPlugin(t, newObservableStore(), false)
+ plugin := newTestPlugin(t, newObservableStore())
// Build a cached entry with multiple chunks.
chunkJSON := `{"chat_response":{"choices":[]}}`
diff --git a/plugins/semanticcache/plugin_core_test.go b/plugins/semanticcache/plugin_core_test.go
index e0f88464de..a280f662f6 100644
--- a/plugins/semanticcache/plugin_core_test.go
+++ b/plugins/semanticcache/plugin_core_test.go
@@ -581,11 +581,10 @@ func TestInvalidProviderRejection(t *testing.T) {
for _, provider := range unsupportedProviders {
t.Run(string(provider), func(t *testing.T) {
config := &Config{
- Provider: provider,
- EmbeddingModel: "some-model",
- Dimension: 1536,
- Threshold: 0.8,
- CleanUpOnShutdown: false,
+ Provider: provider,
+ EmbeddingModel: "some-model",
+ Dimension: 1536,
+ Threshold: 0.8,
}
// Provider validation was moved to request time (global client handles it).
@@ -608,11 +607,10 @@ func TestValidProviderAccepted(t *testing.T) {
// Test a supported provider (OpenAI)
config := &Config{
- Provider: schemas.OpenAI,
- EmbeddingModel: "text-embedding-3-small",
- Dimension: 1536,
- Threshold: 0.8,
- CleanUpOnShutdown: false,
+ Provider: schemas.OpenAI,
+ EmbeddingModel: "text-embedding-3-small",
+ Dimension: 1536,
+ Threshold: 0.8,
}
// Init should succeed; provider validation happens at request time via the global client.
diff --git a/plugins/semanticcache/plugin_no_mutation_test.go b/plugins/semanticcache/plugin_no_mutation_test.go
index 340b4fdd9a..d0a65b681f 100644
--- a/plugins/semanticcache/plugin_no_mutation_test.go
+++ b/plugins/semanticcache/plugin_no_mutation_test.go
@@ -88,8 +88,6 @@ func TestCachingDoesNotMutateRequestSentToProvider(t *testing.T) {
Threshold: 0.8,
ConversationHistoryThreshold: DefaultConversationHistoryThreshold,
VectorStoreNamespace: SharedTestNamespace,
- // Do NOT clean up on shutdown — other parallel tests share the namespace.
- CleanUpOnShutdown: false,
}
if err := ensureSharedTestNamespace(context.Background(), store, cfg.Dimension); err != nil {
t.Fatalf("ensureSharedTestNamespace: %v", err)
diff --git a/plugins/semanticcache/plugin_paths_test.go b/plugins/semanticcache/plugin_paths_test.go
index 5ca1ac8c7a..fc6f54920b 100644
--- a/plugins/semanticcache/plugin_paths_test.go
+++ b/plugins/semanticcache/plugin_paths_test.go
@@ -21,7 +21,7 @@ import (
func TestPostLLMHook_SkipsOnBifrostError(t *testing.T) {
store := newObservableStore()
- plugin := newTestPlugin(t, store, false)
+ plugin := newTestPlugin(t, store)
ctx := newBaseTestContext()
ctx.SetValue(CacheKey, keyForTest(t, ""))
@@ -61,7 +61,7 @@ func TestPostLLMHook_SkipsOnBifrostError(t *testing.T) {
// -----------------------------------------------------------------------------
func TestShouldSkipCaching_LargePayloadMode(t *testing.T) {
- plugin := newTestPlugin(t, newObservableStore(), false)
+ plugin := newTestPlugin(t, newObservableStore())
ctx := newBaseTestContext()
ctx.SetValue(schemas.BifrostContextKeyLargePayloadMode, true)
@@ -73,7 +73,7 @@ func TestShouldSkipCaching_LargePayloadMode(t *testing.T) {
}
func TestShouldSkipCaching_LargeResponseMode(t *testing.T) {
- plugin := newTestPlugin(t, newObservableStore(), false)
+ plugin := newTestPlugin(t, newObservableStore())
ctx := newBaseTestContext()
ctx.SetValue(schemas.BifrostContextKeyLargeResponseMode, true)
@@ -85,7 +85,7 @@ func TestShouldSkipCaching_LargeResponseMode(t *testing.T) {
}
func TestShouldSkipCaching_CacheHitReplay(t *testing.T) {
- plugin := newTestPlugin(t, newObservableStore(), false)
+ plugin := newTestPlugin(t, newObservableStore())
ctx := newBaseTestContext()
res := &schemas.BifrostResponse{
@@ -102,7 +102,7 @@ func TestShouldSkipCaching_CacheHitReplay(t *testing.T) {
}
func TestShouldSkipCaching_NoStoreFlag(t *testing.T) {
- plugin := newTestPlugin(t, newObservableStore(), false)
+ plugin := newTestPlugin(t, newObservableStore())
ctx := newBaseTestContext()
ctx.SetValue(CacheNoStoreKey, true)
@@ -162,7 +162,7 @@ func TestInit_AllowsDirectOnlyMode(t *testing.T) {
// -----------------------------------------------------------------------------
func TestPreLLMHook_FallsBackToDirectWhenExecutorMissing(t *testing.T) {
- plugin := newTestPlugin(t, newObservableStore(), false)
+ plugin := newTestPlugin(t, newObservableStore())
// Intentionally do NOT set plugin.embeddingRequestExecutor.
req := &schemas.BifrostRequest{
@@ -197,7 +197,7 @@ func TestPreLLMHook_FallsBackToDirectWhenExecutorMissing(t *testing.T) {
func TestExpiredEntry_DetectedAndDeleted(t *testing.T) {
store := newObservableStore()
- plugin := newTestPlugin(t, store, false)
+ plugin := newTestPlugin(t, store)
// Plant an already-expired entry under a deterministic ID.
expiredID := "expired-id-1"
@@ -295,7 +295,7 @@ func TestUnmarshalJSON_RejectsBadDurationString(t *testing.T) {
// -----------------------------------------------------------------------------
func TestStreamReplay_CancelImmediately(t *testing.T) {
- plugin := newTestPlugin(t, newObservableStore(), false)
+ plugin := newTestPlugin(t, newObservableStore())
chunk := `{"chat_response":{"choices":[]}}`
streamArray := []string{chunk, chunk, chunk}
@@ -331,7 +331,7 @@ func TestStreamReplay_CancelImmediately(t *testing.T) {
}
func TestStreamReplay_FullDrain(t *testing.T) {
- plugin := newTestPlugin(t, newObservableStore(), false)
+ plugin := newTestPlugin(t, newObservableStore())
chunk := `{"chat_response":{"choices":[]}}`
streamArray := []string{chunk, chunk, chunk}
@@ -379,7 +379,7 @@ func scopedTestContext(t testing.TB, suffix string) *schemas.BifrostContext {
func TestPreLLMHook_EmitsPluginLogOnEmbeddingFailure(t *testing.T) {
store := newObservableStore()
- plugin := newTestPlugin(t, store, false)
+ plugin := newTestPlugin(t, store)
plugin.SetEmbeddingRequestExecutor(func(_ *schemas.BifrostContext, _ *schemas.BifrostEmbeddingRequest) (*schemas.BifrostEmbeddingResponse, *schemas.BifrostError) {
return nil, &schemas.BifrostError{Error: &schemas.ErrorField{Message: "rate limit exceeded"}}
})
@@ -438,7 +438,7 @@ func TestPreLLMHook_NoDebugLogsOnFlow(t *testing.T) {
// flow (hit/miss). cache_debug already conveys that. Only Warn-level
// failure logs should appear on the response.
store := newObservableStore()
- plugin := newTestPlugin(t, store, false)
+ plugin := newTestPlugin(t, store)
req := &schemas.BifrostRequest{
RequestType: schemas.ChatCompletionRequest,
@@ -461,7 +461,7 @@ func TestPreLLMHook_NoDebugLogsOnFlow(t *testing.T) {
}
func TestResolveCacheTypes_EmitsPluginLogOnInvalidValue(t *testing.T) {
- plugin := newTestPlugin(t, newObservableStore(), false)
+ plugin := newTestPlugin(t, newObservableStore())
ctx := scopedTestContext(t, "")
ctx.SetValue(CacheTypeKey, "not-a-cache-type") // wrong type
@@ -484,7 +484,7 @@ func TestResolveCacheTypes_EmitsPluginLogOnInvalidValue(t *testing.T) {
// -----------------------------------------------------------------------------
func TestGenerateEmbedding_AcceptsInt8Array(t *testing.T) {
- plugin := newTestPlugin(t, newObservableStore(), false)
+ plugin := newTestPlugin(t, newObservableStore())
plugin.SetEmbeddingRequestExecutor(func(_ *schemas.BifrostContext, _ *schemas.BifrostEmbeddingRequest) (*schemas.BifrostEmbeddingResponse, *schemas.BifrostError) {
return &schemas.BifrostEmbeddingResponse{
Data: []schemas.EmbeddingData{{
@@ -507,7 +507,7 @@ func TestGenerateEmbedding_AcceptsInt8Array(t *testing.T) {
}
func TestGenerateEmbedding_AcceptsInt32Array(t *testing.T) {
- plugin := newTestPlugin(t, newObservableStore(), false)
+ plugin := newTestPlugin(t, newObservableStore())
plugin.SetEmbeddingRequestExecutor(func(_ *schemas.BifrostContext, _ *schemas.BifrostEmbeddingRequest) (*schemas.BifrostEmbeddingResponse, *schemas.BifrostError) {
return &schemas.BifrostEmbeddingResponse{
Data: []schemas.EmbeddingData{{
@@ -534,7 +534,7 @@ func TestGenerateEmbedding_AcceptsInt32Array(t *testing.T) {
// -----------------------------------------------------------------------------
func TestPreLLMHook_ConcurrentSameRequestID(t *testing.T) {
- plugin := newTestPlugin(t, newObservableStore(), false)
+ plugin := newTestPlugin(t, newObservableStore())
req := &schemas.BifrostRequest{
RequestType: schemas.ChatCompletionRequest,
diff --git a/plugins/semanticcache/plugin_vectorstore_test.go b/plugins/semanticcache/plugin_vectorstore_test.go
index 6d29f08c8b..3e93cc8baa 100644
--- a/plugins/semanticcache/plugin_vectorstore_test.go
+++ b/plugins/semanticcache/plugin_vectorstore_test.go
@@ -55,7 +55,6 @@ func getDefaultTestConfig() *Config {
EmbeddingModel: "text-embedding-3-small",
Dimension: 1536,
Threshold: 0.8,
- CleanUpOnShutdown: true,
ConversationHistoryThreshold: DefaultConversationHistoryThreshold,
}
}
diff --git a/plugins/semanticcache/test_utils.go b/plugins/semanticcache/test_utils.go
index 6a506f51a4..d9b4084926 100644
--- a/plugins/semanticcache/test_utils.go
+++ b/plugins/semanticcache/test_utils.go
@@ -415,11 +415,10 @@ type TestSetup struct {
// NewTestSetup creates a new test setup with default configuration
func NewTestSetup(t *testing.T) *TestSetup {
return NewTestSetupWithConfig(t, &Config{
- Provider: schemas.OpenAI,
- EmbeddingModel: "text-embedding-3-small",
- Dimension: 1536,
- Threshold: 0.8,
- CleanUpOnShutdown: true,
+ Provider: schemas.OpenAI,
+ EmbeddingModel: "text-embedding-3-small",
+ Dimension: 1536,
+ Threshold: 0.8,
})
}
@@ -462,9 +461,6 @@ func NewTestSetupWithVectorStore(t *testing.T, config *Config, storeType vectors
if config.VectorStoreNamespace == "" {
config.VectorStoreNamespace = SharedTestNamespace
}
- // Tests must NOT delete the shared namespace at cleanup — other parallel
- // tests are still using it. Override any caller default.
- config.CleanUpOnShutdown = false
// Get the appropriate config for the vector store type
var storeConfig interface{}
@@ -750,7 +746,6 @@ func CreateTestSetupWithConversationThreshold(t *testing.T, threshold int) *Test
Provider: schemas.OpenAI,
EmbeddingModel: "text-embedding-3-small",
Dimension: 1536,
- CleanUpOnShutdown: true,
Threshold: 0.8,
ConversationHistoryThreshold: threshold,
}
@@ -764,7 +759,6 @@ func CreateTestSetupWithExcludeSystemPrompt(t *testing.T, excludeSystem bool) *T
Provider: schemas.OpenAI,
EmbeddingModel: "text-embedding-3-small",
Dimension: 1536,
- CleanUpOnShutdown: true,
Threshold: 0.8,
ExcludeSystemPrompt: &excludeSystem,
}
@@ -778,7 +772,6 @@ func CreateTestSetupWithThresholdAndExcludeSystem(t *testing.T, threshold int, e
Provider: schemas.OpenAI,
EmbeddingModel: "text-embedding-3-small",
Dimension: 1536,
- CleanUpOnShutdown: true,
Threshold: 0.8,
ConversationHistoryThreshold: threshold,
ExcludeSystemPrompt: &excludeSystem,
diff --git a/transports/bifrost-http/handlers/plugins.go b/transports/bifrost-http/handlers/plugins.go
index 3dc4f8353c..71613c15ba 100644
--- a/transports/bifrost-http/handlers/plugins.go
+++ b/transports/bifrost-http/handlers/plugins.go
@@ -16,9 +16,9 @@ import (
)
type PluginsLoader interface {
+ GetPluginStatus(ctx context.Context) map[string]schemas.PluginStatus
ReloadPlugin(ctx context.Context, name string, path *string, pluginConfig any, placement *schemas.PluginPlacement, order *int) error
RemovePlugin(ctx context.Context, name string) error
- GetPluginStatus(ctx context.Context) map[string]schemas.PluginStatus
}
// PluginsHandler is the handler for the plugins API
diff --git a/transports/config.schema.json b/transports/config.schema.json
index 8e78f1d1a0..66ee0b5ee5 100644
--- a/transports/config.schema.json
+++ b/transports/config.schema.json
@@ -1440,10 +1440,6 @@
"type": "string",
"description": "Model to use for generating embeddings in provider-backed semantic caching. Required when provider is set and not allowed in direct-only mode."
},
- "cleanup_on_shutdown": {
- "type": "boolean",
- "description": "Clean up cache on shutdown (default: false)"
- },
"ttl": {
"description": "Time-to-live for cached responses (supports duration strings like '5m', '1h' or seconds as number, default: 5min)",
"oneOf": [