Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 16 additions & 3 deletions tests/integrations/config.json
Original file line number Diff line number Diff line change
Expand Up @@ -151,7 +151,11 @@
}
},
"config_store": {
"enabled": false
"enabled": true,
"type": "sqlite",
"config": {
"path": "../../tests/integrations/config.db"
}
},
"logs_store": {
"enabled": true,
Expand All @@ -160,15 +164,24 @@
"path": "./logs.db"
}
},
"governance": {
"virtual_keys": [
{
"id": "vk-test",
"value": "sk-bf-test-key",
"is_active": true
}
]
},
"client": {
"drop_excess_requests": false,
"initial_pool_size": 300,
"allowed_origins": [
"*"
],
"enable_logging": true,
"enable_governance": false,
"enforce_governance_header": false,
"enable_governance": true,
"enforce_governance_header": true,
"allow_direct_keys": false,
"max_request_body_size_mb": 100,
"enable_litellm_fallbacks": false
Expand Down
6 changes: 6 additions & 0 deletions tests/integrations/config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -695,6 +695,12 @@ environments:
simple: 20
complex: 40

# Virtual key testing configuration
# When enabled, cross-provider tests will run twice: with and without the x-bf-vk header
virtual_key:
enabled: true
value: "sk-bf-test-key"

# Logging configuration
logging:
level: "INFO"
Expand Down
276 changes: 158 additions & 118 deletions tests/integrations/tests/test_openai.py

Large diffs are not rendered by default.

34 changes: 34 additions & 0 deletions tests/integrations/tests/utils/config_loader.py
Original file line number Diff line number Diff line change
Expand Up @@ -423,6 +423,30 @@ def get_scenario_capability(self, scenario: str) -> str:

return self._config["scenario_capabilities"].get(scenario, "chat")

def get_virtual_key(self) -> str:
"""Get the virtual key value for testing

Returns:
Virtual key string or empty string if not configured
"""
if "virtual_key" not in self._config:
return ""

vk_config = self._config["virtual_key"]
if not vk_config.get("enabled", False):
return ""

return vk_config.get("value", "")

def is_virtual_key_configured(self) -> bool:
"""Check if virtual key testing is enabled and configured

Returns:
True if virtual key is available for testing
"""
vk = self.get_virtual_key()
return vk is not None and vk.strip() != ""


# Global configuration instance
_config_loader = None
Expand Down Expand Up @@ -480,6 +504,16 @@ def get_providers_for_scenario(scenario: str) -> List[str]:
return get_config().get_providers_for_scenario(scenario)


def get_virtual_key() -> str:
"""Convenience function to get virtual key"""
return get_config().get_virtual_key()


def is_virtual_key_configured() -> bool:
"""Convenience function to check if virtual key is configured"""
return get_config().is_virtual_key_configured()


if __name__ == "__main__":
# Print configuration summary when run directly
config = get_config()
Expand Down
78 changes: 77 additions & 1 deletion tests/integrations/tests/utils/parametrize.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
with automatic scenario-based filtering.
"""

from typing import List, Tuple
from typing import List, Tuple, Union
from .config_loader import get_config


Expand Down Expand Up @@ -47,6 +47,82 @@ def get_cross_provider_params_for_scenario(
return params


def get_cross_provider_params_with_vk_for_scenario(
scenario: str,
include_providers: List[str] | None = None,
exclude_providers: List[str] | None = None,
) -> List[Tuple[str, str, bool]]:
"""
Get cross-provider parameters with virtual key flag for pytest parametrization.

When virtual key is configured, each provider/model combo is tested twice:
once without VK (vk_enabled=False) and once with VK (vk_enabled=True).

Args:
scenario: Test scenario name
include_providers: Optional list of providers to include
exclude_providers: Optional list of providers to exclude

Returns:
List of (provider, model, vk_enabled) tuples

Example:
When VK is configured:
[
("openai", "gpt-4o", False),
("openai", "gpt-4o", True),
("anthropic", "claude-3", False),
("anthropic", "claude-3", True),
]
"""
config = get_config()

# Get base params without VK
base_params = get_cross_provider_params_for_scenario(
scenario, include_providers, exclude_providers
)

# Handle the dummy tuple case
if base_params == [("_no_providers_", "_no_model_")]:
return [("_no_providers_", "_no_model_", False)]

# Build params list with VK flag
params = []
vk_configured = config.is_virtual_key_configured()

for provider, model in base_params:
# Always add the non-VK variant
params.append((provider, model, False))

# Add VK variant only if VK is configured
if vk_configured:
params.append((provider, model, True))

return params


def format_vk_test_id(provider: str, model: str, vk_enabled: bool) -> str:
"""
Format test ID for virtual key parameterized tests.

Args:
provider: Provider name
model: Model name
vk_enabled: Whether VK is enabled

Returns:
Formatted test ID string

Example:
>>> format_vk_test_id("openai", "gpt-4o", True)
"openai-gpt-4o-with_vk"
>>> format_vk_test_id("openai", "gpt-4o", False)
"openai-gpt-4o-no_vk"
"""
vk_suffix = "with_vk" if vk_enabled else "no_vk"
return f"{provider}-{model}-{vk_suffix}"


def format_provider_model(provider: str, model: str) -> str:
"""
Format provider and model into the standard "provider/model" format.
Expand Down
2 changes: 1 addition & 1 deletion transports/bifrost-http/handlers/plugins.go
Original file line number Diff line number Diff line change
Expand Up @@ -313,7 +313,7 @@ func (h *PluginsHandler) updatePlugin(ctx *fasthttp.RequestCtx) {
Enabled: false,
Config: map[string]any{},
Path: nil,
IsCustom: true,
IsCustom: false,
}
if err := h.configStore.CreatePlugin(ctx, plugin); err != nil {
logger.Error("failed to create plugin: %v", err)
Expand Down
2 changes: 1 addition & 1 deletion transports/bifrost-http/lib/ctx.go
Original file line number Diff line number Diff line change
Expand Up @@ -207,7 +207,7 @@ func ConvertToBifrostContext(ctx *fasthttp.RequestCtx, allowDirectKeys bool, hea
}
// Handle virtual key header (x-bf-vk, authorization, x-api-key, x-goog-api-key headers)
if keyStr == string(schemas.BifrostContextKeyVirtualKey) {
bifrostCtx = context.WithValue(bifrostCtx, schemas.BifrostContextKey(keyStr), string(value))
bifrostCtx = context.WithValue(bifrostCtx, schemas.BifrostContextKeyVirtualKey, string(value))
return true
}
if keyStr == "authorization" {
Expand Down
2 changes: 0 additions & 2 deletions transports/bifrost-http/server/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -913,15 +913,13 @@ func (s *BifrostHTTPServer) RemovePlugin(ctx context.Context, name string) error
if oldPlugins != nil {
oldPluginsSlice = *oldPlugins
}

// Create new slice without the removed plugin
newPlugins := make([]schemas.Plugin, 0, len(oldPluginsSlice))
for _, existing := range oldPluginsSlice {
if existing.GetName() != name {
newPlugins = append(newPlugins, existing)
}
}

// Atomic compare-and-swap
if s.Config.Plugins.CompareAndSwap(oldPlugins, &newPlugins) {
s.PluginsMutex.Lock()
Expand Down