first commit

This commit is contained in:
Beyhan Oğur
2026-04-26 21:52:23 +03:00
commit 880f412e2c
2662 changed files with 866266 additions and 0 deletions

View File

@@ -0,0 +1,316 @@
package mocker
import (
"context"
"strconv"
"testing"
bifrost "github.com/maximhq/bifrost/core"
"github.com/maximhq/bifrost/core/schemas"
)
// BenchmarkMockerPlugin_PreHook_SimpleRule benchmarks simple rule matching
func BenchmarkMockerPlugin_PreHook_SimpleRule(b *testing.B) {
plugin, err := Init(MockerConfig{
Enabled: true,
Rules: []MockRule{
{
Name: "simple-rule",
Enabled: true,
Priority: 100,
Probability: 1.0,
Conditions: Conditions{
Providers: []string{"openai"},
},
Responses: []Response{
{
Type: ResponseTypeSuccess,
Content: &SuccessResponse{
Message: "Benchmark response",
},
},
},
},
},
})
if err != nil {
b.Fatal(err)
}
req := &schemas.BifrostChatRequest{
Provider: schemas.OpenAI,
Model: "gpt-4",
Input: []schemas.ChatMessage{
{
Role: schemas.ChatMessageRoleUser,
Content: &schemas.ChatMessageContent{
ContentStr: bifrost.Ptr("Hello, benchmark test"),
},
},
},
}
ctx := schemas.NewBifrostContext(context.Background(), schemas.NoDeadline)
b.ResetTimer()
b.ReportAllocs()
// Convert to BifrostRequest for PreLLMHook compatibility
bifrostReq := &schemas.BifrostRequest{
RequestType: schemas.ChatCompletionRequest,
ChatRequest: req,
}
for i := 0; i < b.N; i++ {
_, _, _ = plugin.PreLLMHook(ctx, bifrostReq)
}
}
// BenchmarkMockerPlugin_PreHook_RegexRule benchmarks regex rule matching
func BenchmarkMockerPlugin_PreHook_RegexRule(b *testing.B) {
plugin, err := Init(MockerConfig{
Enabled: true,
Rules: []MockRule{
{
Name: "regex-rule",
Enabled: true,
Priority: 100,
Probability: 1.0,
Conditions: Conditions{
MessageRegex: bifrost.Ptr(`(?i).*hello.*`),
},
Responses: []Response{
{
Type: ResponseTypeSuccess,
Content: &SuccessResponse{
Message: "Regex matched response",
},
},
},
},
},
})
if err != nil {
b.Fatal(err)
}
req := &schemas.BifrostChatRequest{
Provider: schemas.OpenAI,
Model: "gpt-4",
Input: []schemas.ChatMessage{
{
Role: schemas.ChatMessageRoleUser,
Content: &schemas.ChatMessageContent{
ContentStr: bifrost.Ptr("Hello, this should match the regex pattern"),
},
},
},
}
ctx := schemas.NewBifrostContext(context.Background(), schemas.NoDeadline)
b.ResetTimer()
b.ReportAllocs()
// Convert to BifrostRequest for PreLLMHook compatibility
bifrostReq := &schemas.BifrostRequest{
RequestType: schemas.ChatCompletionRequest,
ChatRequest: req,
}
for i := 0; i < b.N; i++ {
_, _, _ = plugin.PreLLMHook(ctx, bifrostReq)
}
}
// BenchmarkMockerPlugin_PreHook_MultipleRules benchmarks multiple rule evaluation
func BenchmarkMockerPlugin_PreHook_MultipleRules(b *testing.B) {
rules := make([]MockRule, 10)
for i := 0; i < 10; i++ {
rules[i] = MockRule{
Name: "rule-" + strconv.Itoa(i),
Enabled: true,
Priority: 100 - i, // Descending priority
Probability: 1.0,
Conditions: Conditions{
Models: []string{"gpt-" + strconv.Itoa(i)},
},
Responses: []Response{
{
Type: ResponseTypeSuccess,
Content: &SuccessResponse{
Message: "Response from rule " + strconv.Itoa(i),
},
},
},
}
}
// Add a matching rule at the end
rules = append(rules, MockRule{
Name: "matching-rule",
Enabled: true,
Priority: 50,
Probability: 1.0,
Conditions: Conditions{
Models: []string{"gpt-4"},
},
Responses: []Response{
{
Type: ResponseTypeSuccess,
Content: &SuccessResponse{
Message: "Matching rule response",
},
},
},
})
plugin, err := Init(MockerConfig{
Enabled: true,
Rules: rules,
})
if err != nil {
b.Fatal(err)
}
req := &schemas.BifrostChatRequest{
Provider: schemas.OpenAI,
Model: "gpt-4",
Input: []schemas.ChatMessage{
{
Role: schemas.ChatMessageRoleUser,
Content: &schemas.ChatMessageContent{
ContentStr: bifrost.Ptr("Test message"),
},
},
},
}
ctx := schemas.NewBifrostContext(context.Background(), schemas.NoDeadline)
b.ResetTimer()
b.ReportAllocs()
// Convert to BifrostRequest for PreLLMHook compatibility
bifrostReq := &schemas.BifrostRequest{
RequestType: schemas.ChatCompletionRequest,
ChatRequest: req,
}
for i := 0; i < b.N; i++ {
_, _, _ = plugin.PreLLMHook(ctx, bifrostReq)
}
}
// BenchmarkMockerPlugin_PreHook_NoMatch benchmarks when no rules match
func BenchmarkMockerPlugin_PreHook_NoMatch(b *testing.B) {
plugin, err := Init(MockerConfig{
Enabled: true,
DefaultBehavior: DefaultBehaviorPassthrough,
Rules: []MockRule{
{
Name: "non-matching-rule",
Enabled: true,
Priority: 100,
Probability: 1.0,
Conditions: Conditions{
Providers: []string{"anthropic"}, // Won't match OpenAI
},
Responses: []Response{
{
Type: ResponseTypeSuccess,
Content: &SuccessResponse{
Message: "This won't match",
},
},
},
},
},
})
if err != nil {
b.Fatal(err)
}
req := &schemas.BifrostChatRequest{
Provider: schemas.OpenAI, // Different from rule condition
Model: "gpt-4",
Input: []schemas.ChatMessage{
{
Role: schemas.ChatMessageRoleUser,
Content: &schemas.ChatMessageContent{
ContentStr: bifrost.Ptr("Test message"),
},
},
},
}
ctx := schemas.NewBifrostContext(context.Background(), schemas.NoDeadline)
b.ResetTimer()
b.ReportAllocs()
// Convert to BifrostRequest for PreLLMHook compatibility
bifrostReq := &schemas.BifrostRequest{
RequestType: schemas.ChatCompletionRequest,
ChatRequest: req,
}
for i := 0; i < b.N; i++ {
_, _, _ = plugin.PreLLMHook(ctx, bifrostReq)
}
}
// BenchmarkMockerPlugin_PreHook_Template benchmarks template processing
func BenchmarkMockerPlugin_PreHook_Template(b *testing.B) {
plugin, err := Init(MockerConfig{
Enabled: true,
Rules: []MockRule{
{
Name: "template-rule",
Enabled: true,
Priority: 100,
Probability: 1.0,
Conditions: Conditions{}, // Match all
Responses: []Response{
{
Type: ResponseTypeSuccess,
Content: &SuccessResponse{
MessageTemplate: bifrost.Ptr("Hello from {{provider}} using model {{model}}!"),
},
},
},
},
},
})
if err != nil {
b.Fatal(err)
}
req := &schemas.BifrostChatRequest{
Provider: schemas.OpenAI,
Model: "gpt-4",
Input: []schemas.ChatMessage{
{
Role: schemas.ChatMessageRoleUser,
Content: &schemas.ChatMessageContent{
ContentStr: bifrost.Ptr("Test message"),
},
},
},
}
ctx := schemas.NewBifrostContext(context.Background(), schemas.NoDeadline)
b.ResetTimer()
b.ReportAllocs()
// Convert to BifrostRequest for PreLLMHook compatibility
bifrostReq := &schemas.BifrostRequest{
RequestType: schemas.ChatCompletionRequest,
ChatRequest: req,
}
for i := 0; i < b.N; i++ {
_, _, _ = plugin.PreLLMHook(ctx, bifrostReq)
}
}

View File

73
plugins/mocker/go.mod Normal file
View File

@@ -0,0 +1,73 @@
module github.com/maximhq/bifrost/plugins/mocker
go 1.26.2
require (
github.com/jaswdr/faker/v2 v2.8.0
github.com/maximhq/bifrost/core v1.5.4
)
require (
cloud.google.com/go v0.123.0 // indirect
cloud.google.com/go/compute/metadata v0.9.0 // indirect
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.20.0 // indirect
github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.13.1 // indirect
github.com/Azure/azure-sdk-for-go/sdk/internal v1.11.2 // indirect
github.com/AzureAD/microsoft-authentication-library-for-go v1.6.0 // indirect
github.com/andybalholm/brotli v1.2.0 // indirect
github.com/aws/aws-sdk-go-v2 v1.41.5 // indirect
github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.7.8 // indirect
github.com/aws/aws-sdk-go-v2/config v1.32.11 // indirect
github.com/aws/aws-sdk-go-v2/credentials v1.19.14 // indirect
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.18.21 // indirect
github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.21 // indirect
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.21 // indirect
github.com/aws/aws-sdk-go-v2/internal/ini v1.8.5 // indirect
github.com/aws/aws-sdk-go-v2/internal/v4a v1.4.22 // indirect
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.13.7 // indirect
github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.9.13 // indirect
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.13.21 // indirect
github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.19.21 // indirect
github.com/aws/aws-sdk-go-v2/service/s3 v1.97.3 // indirect
github.com/aws/aws-sdk-go-v2/service/signin v1.0.9 // indirect
github.com/aws/aws-sdk-go-v2/service/sso v1.30.15 // indirect
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.35.19 // indirect
github.com/aws/aws-sdk-go-v2/service/sts v1.41.10 // indirect
github.com/aws/smithy-go v1.24.2 // indirect
github.com/bahlo/generic-list-go v0.2.0 // indirect
github.com/buger/jsonparser v1.1.2 // indirect
github.com/bytedance/gopkg v0.1.3 // indirect
github.com/bytedance/sonic v1.15.0 // indirect
github.com/bytedance/sonic/loader v0.5.0 // indirect
github.com/cloudwego/base64x v0.1.6 // indirect
github.com/golang-jwt/jwt/v5 v5.3.0 // indirect
github.com/google/uuid v1.6.0 // indirect
github.com/invopop/jsonschema v0.13.0 // indirect
github.com/klauspost/compress v1.18.2 // indirect
github.com/klauspost/cpuid/v2 v2.3.0 // indirect
github.com/kylelemons/godebug v1.1.0 // indirect
github.com/mailru/easyjson v0.9.1 // indirect
github.com/mark3labs/mcp-go v0.43.2 // indirect
github.com/mattn/go-colorable v0.1.14 // indirect
github.com/mattn/go-isatty v0.0.20 // indirect
github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c // indirect
github.com/rs/zerolog v1.34.0 // indirect
github.com/spf13/cast v1.10.0 // indirect
github.com/tidwall/gjson v1.18.0 // indirect
github.com/tidwall/match v1.1.1 // indirect
github.com/tidwall/pretty v1.2.0 // indirect
github.com/tidwall/sjson v1.2.5 // indirect
github.com/twitchyliquid64/golang-asm v0.15.1 // indirect
github.com/valyala/bytebufferpool v1.0.0 // indirect
github.com/valyala/fasthttp v1.68.0 // indirect
github.com/wk8/go-ordered-map/v2 v2.1.8 // indirect
github.com/yosida95/uritemplate/v3 v3.0.2 // indirect
go.starlark.net v0.0.0-20260102030733-3fee463870c9 // indirect
golang.org/x/arch v0.23.0 // indirect
golang.org/x/crypto v0.49.0 // indirect
golang.org/x/net v0.52.0 // indirect
golang.org/x/oauth2 v0.36.0 // indirect
golang.org/x/sys v0.42.0 // indirect
golang.org/x/text v0.35.0 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
)

187
plugins/mocker/go.sum Normal file
View File

@@ -0,0 +1,187 @@
cloud.google.com/go v0.123.0 h1:2NAUJwPR47q+E35uaJeYoNhuNEM9kM8SjgRgdeOJUSE=
cloud.google.com/go v0.123.0/go.mod h1:xBoMV08QcqUGuPW65Qfm1o9Y4zKZBpGS+7bImXLTAZU=
cloud.google.com/go/compute/metadata v0.9.0 h1:pDUj4QMoPejqq20dK0Pg2N4yG9zIkYGdBtwLoEkH9Zs=
cloud.google.com/go/compute/metadata v0.9.0/go.mod h1:E0bWwX5wTnLPedCKqk3pJmVgCBSM6qQI1yTBdEb3C10=
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.20.0 h1:JXg2dwJUmPB9JmtVmdEB16APJ7jurfbY5jnfXpJoRMc=
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.20.0/go.mod h1:YD5h/ldMsG0XiIw7PdyNhLxaM317eFh5yNLccNfGdyw=
github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.13.1 h1:Hk5QBxZQC1jb2Fwj6mpzme37xbCDdNTxU7O9eb5+LB4=
github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.13.1/go.mod h1:IYus9qsFobWIc2YVwe/WPjcnyCkPKtnHAqUYeebc8z0=
github.com/Azure/azure-sdk-for-go/sdk/azidentity/cache v0.3.2 h1:yz1bePFlP5Vws5+8ez6T3HWXPmwOK7Yvq8QxDBD3SKY=
github.com/Azure/azure-sdk-for-go/sdk/azidentity/cache v0.3.2/go.mod h1:Pa9ZNPuoNu/GztvBSKk9J1cDJW6vk/n0zLtV4mgd8N8=
github.com/Azure/azure-sdk-for-go/sdk/internal v1.11.2 h1:9iefClla7iYpfYWdzPCRDozdmndjTm8DXdpCzPajMgA=
github.com/Azure/azure-sdk-for-go/sdk/internal v1.11.2/go.mod h1:XtLgD3ZD34DAaVIIAyG3objl5DynM3CQ/vMcbBNJZGI=
github.com/AzureAD/microsoft-authentication-extensions-for-go/cache v0.1.1 h1:WJTmL004Abzc5wDB5VtZG2PJk5ndYDgVacGqfirKxjM=
github.com/AzureAD/microsoft-authentication-extensions-for-go/cache v0.1.1/go.mod h1:tCcJZ0uHAmvjsVYzEFivsRTN00oz5BEsRgQHu5JZ9WE=
github.com/AzureAD/microsoft-authentication-library-for-go v1.6.0 h1:XRzhVemXdgvJqCH0sFfrBUTnUJSBrBf7++ypk+twtRs=
github.com/AzureAD/microsoft-authentication-library-for-go v1.6.0/go.mod h1:HKpQxkWaGLJ+D/5H8QRpyQXA1eKjxkFlOMwck5+33Jk=
github.com/andybalholm/brotli v1.2.0 h1:ukwgCxwYrmACq68yiUqwIWnGY0cTPox/M94sVwToPjQ=
github.com/andybalholm/brotli v1.2.0/go.mod h1:rzTDkvFWvIrjDXZHkuS16NPggd91W3kUSvPlQ1pLaKY=
github.com/aws/aws-sdk-go-v2 v1.41.5 h1:dj5kopbwUsVUVFgO4Fi5BIT3t4WyqIDjGKCangnV/yY=
github.com/aws/aws-sdk-go-v2 v1.41.5/go.mod h1:mwsPRE8ceUUpiTgF7QmQIJ7lgsKUPQOUl3o72QBrE1o=
github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.7.8 h1:eBMB84YGghSocM7PsjmmPffTa+1FBUeNvGvFou6V/4o=
github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.7.8/go.mod h1:lyw7GFp3qENLh7kwzf7iMzAxDn+NzjXEAGjKS2UOKqI=
github.com/aws/aws-sdk-go-v2/config v1.32.11 h1:ftxI5sgz8jZkckuUHXfC/wMUc8u3fG1vQS0plr2F2Zs=
github.com/aws/aws-sdk-go-v2/config v1.32.11/go.mod h1:twF11+6ps9aNRKEDimksp923o44w/Thk9+8YIlzWMmo=
github.com/aws/aws-sdk-go-v2/credentials v1.19.14 h1:n+UcGWAIZHkXzYt87uMFBv/l8THYELoX6gVcUvgl6fI=
github.com/aws/aws-sdk-go-v2/credentials v1.19.14/go.mod h1:cJKuyWB59Mqi0jM3nFYQRmnHVQIcgoxjEMAbLkpr62w=
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.18.21 h1:NUS3K4BTDArQqNu2ih7yeDLaS3bmHD0YndtA6UP884g=
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.18.21/go.mod h1:YWNWJQNjKigKY1RHVJCuupeWDrrHjRqHm0N9rdrWzYI=
github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.21 h1:Rgg6wvjjtX8bNHcvi9OnXWwcE0a2vGpbwmtICOsvcf4=
github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.21/go.mod h1:A/kJFst/nm//cyqonihbdpQZwiUhhzpqTsdbhDdRF9c=
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.21 h1:PEgGVtPoB6NTpPrBgqSE5hE/o47Ij9qk/SEZFbUOe9A=
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.21/go.mod h1:p+hz+PRAYlY3zcpJhPwXlLC4C+kqn70WIHwnzAfs6ps=
github.com/aws/aws-sdk-go-v2/internal/ini v1.8.5 h1:clHU5fm//kWS1C2HgtgWxfQbFbx4b6rx+5jzhgX9HrI=
github.com/aws/aws-sdk-go-v2/internal/ini v1.8.5/go.mod h1:O3h0IK87yXci+kg6flUKzJnWeziQUKciKrLjcatSNcY=
github.com/aws/aws-sdk-go-v2/internal/v4a v1.4.22 h1:rWyie/PxDRIdhNf4DzRk0lvjVOqFJuNnO8WwaIRVxzQ=
github.com/aws/aws-sdk-go-v2/internal/v4a v1.4.22/go.mod h1:zd/JsJ4P7oGfUhXn1VyLqaRZwPmZwg44Jf2dS84Dm3Y=
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.13.7 h1:5EniKhLZe4xzL7a+fU3C2tfUN4nWIqlLesfrjkuPFTY=
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.13.7/go.mod h1:x0nZssQ3qZSnIcePWLvcoFisRXJzcTVvYpAAdYX8+GI=
github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.9.13 h1:JRaIgADQS/U6uXDqlPiefP32yXTda7Kqfx+LgspooZM=
github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.9.13/go.mod h1:CEuVn5WqOMilYl+tbccq8+N2ieCy0gVn3OtRb0vBNNM=
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.13.21 h1:c31//R3xgIJMSC8S6hEVq+38DcvUlgFY0FM6mSI5oto=
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.13.21/go.mod h1:r6+pf23ouCB718FUxaqzZdbpYFyDtehyZcmP5KL9FkA=
github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.19.21 h1:ZlvrNcHSFFWURB8avufQq9gFsheUgjVD9536obIknfM=
github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.19.21/go.mod h1:cv3TNhVrssKR0O/xxLJVRfd2oazSnZnkUeTf6ctUwfQ=
github.com/aws/aws-sdk-go-v2/service/s3 v1.97.3 h1:HwxWTbTrIHm5qY+CAEur0s/figc3qwvLWsNkF4RPToo=
github.com/aws/aws-sdk-go-v2/service/s3 v1.97.3/go.mod h1:uoA43SdFwacedBfSgfFSjjCvYe8aYBS7EnU5GZ/YKMM=
github.com/aws/aws-sdk-go-v2/service/signin v1.0.9 h1:QKZH0S178gCmFEgst8hN0mCX1KxLgHBKKY/CLqwP8lg=
github.com/aws/aws-sdk-go-v2/service/signin v1.0.9/go.mod h1:7yuQJoT+OoH8aqIxw9vwF+8KpvLZ8AWmvmUWHsGQZvI=
github.com/aws/aws-sdk-go-v2/service/sso v1.30.15 h1:lFd1+ZSEYJZYvv9d6kXzhkZu07si3f+GQ1AaYwa2LUM=
github.com/aws/aws-sdk-go-v2/service/sso v1.30.15/go.mod h1:WSvS1NLr7JaPunCXqpJnWk1Bjo7IxzZXrZi1QQCkuqM=
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.35.19 h1:dzztQ1YmfPrxdrOiuZRMF6fuOwWlWpD2StNLTceKpys=
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.35.19/go.mod h1:YO8TrYtFdl5w/4vmjL8zaBSsiNp3w0L1FfKVKenZT7w=
github.com/aws/aws-sdk-go-v2/service/sts v1.41.10 h1:p8ogvvLugcR/zLBXTXrTkj0RYBUdErbMnAFFp12Lm/U=
github.com/aws/aws-sdk-go-v2/service/sts v1.41.10/go.mod h1:60dv0eZJfeVXfbT1tFJinbHrDfSJ2GZl4Q//OSSNAVw=
github.com/aws/smithy-go v1.24.2 h1:FzA3bu/nt/vDvmnkg+R8Xl46gmzEDam6mZ1hzmwXFng=
github.com/aws/smithy-go v1.24.2/go.mod h1:YE2RhdIuDbA5E5bTdciG9KrW3+TiEONeUWCqxX9i1Fc=
github.com/bahlo/generic-list-go v0.2.0 h1:5sz/EEAK+ls5wF+NeqDpk5+iNdMDXrh3z3nPnH1Wvgk=
github.com/bahlo/generic-list-go v0.2.0/go.mod h1:2KvAjgMlE5NNynlg/5iLrrCCZ2+5xWbdbCW3pNTGyYg=
github.com/buger/jsonparser v1.1.2 h1:frqHqw7otoVbk5M8LlE/L7HTnIq2v9RX6EJ48i9AxJk=
github.com/buger/jsonparser v1.1.2/go.mod h1:6RYKKt7H4d4+iWqouImQ9R2FZql3VbhNgx27UK13J/0=
github.com/bytedance/gopkg v0.1.3 h1:TPBSwH8RsouGCBcMBktLt1AymVo2TVsBVCY4b6TnZ/M=
github.com/bytedance/gopkg v0.1.3/go.mod h1:576VvJ+eJgyCzdjS+c4+77QF3p7ubbtiKARP3TxducM=
github.com/bytedance/sonic v1.15.0 h1:/PXeWFaR5ElNcVE84U0dOHjiMHQOwNIx3K4ymzh/uSE=
github.com/bytedance/sonic v1.15.0/go.mod h1:tFkWrPz0/CUCLEF4ri4UkHekCIcdnkqXw9VduqpJh0k=
github.com/bytedance/sonic/loader v0.5.0 h1:gXH3KVnatgY7loH5/TkeVyXPfESoqSBSBEiDd5VjlgE=
github.com/bytedance/sonic/loader v0.5.0/go.mod h1:AR4NYCk5DdzZizZ5djGqQ92eEhCCcdf5x77udYiSJRo=
github.com/cloudwego/base64x v0.1.6 h1:t11wG9AECkCDk5fMSoxmufanudBtJ+/HemLstXDLI2M=
github.com/cloudwego/base64x v0.1.6/go.mod h1:OFcloc187FXDaYHvrNIjxSe8ncn0OOM8gEHfghB2IPU=
github.com/coreos/go-systemd/v22 v22.5.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM=
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/fasthttp/websocket v1.5.12 h1:e4RGPpWW2HTbL3zV0Y/t7g0ub294LkiuXXUuTOUInlE=
github.com/fasthttp/websocket v1.5.12/go.mod h1:I+liyL7/4moHojiOgUOIKEWm9EIxHqxZChS+aMFltyg=
github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8=
github.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0=
github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
github.com/golang-jwt/jwt/v5 v5.3.0 h1:pv4AsKCKKZuqlgs5sUmn4x8UlGa0kEVt/puTpKx9vvo=
github.com/golang-jwt/jwt/v5 v5.3.0/go.mod h1:fxCRLWMO43lRc8nhHWY6LGqRcf+1gQWArsqaEUEa5bE=
github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=
github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/hajimehoshi/go-mp3 v0.3.4 h1:NUP7pBYH8OguP4diaTZ9wJbUbk3tC0KlfzsEpWmYj68=
github.com/hajimehoshi/go-mp3 v0.3.4/go.mod h1:fRtZraRFcWb0pu7ok0LqyFhCUrPeMsGRSVop0eemFmo=
github.com/invopop/jsonschema v0.13.0 h1:KvpoAJWEjR3uD9Kbm2HWJmqsEaHt8lBUpd0qHcIi21E=
github.com/invopop/jsonschema v0.13.0/go.mod h1:ffZ5Km5SWWRAIN6wbDXItl95euhFz2uON45H2qjYt+0=
github.com/jaswdr/faker/v2 v2.8.0 h1:3AxdXW9U7dJmWckh/P0YgRbNlCcVsTyrUNUnLVP9b3Q=
github.com/jaswdr/faker/v2 v2.8.0/go.mod h1:jZq+qzNQr8/P+5fHd9t3txe2GNPnthrTfohtnJ7B+68=
github.com/keybase/go-keychain v0.0.1 h1:way+bWYa6lDppZoZcgMbYsvC7GxljxrskdNInRtuthU=
github.com/keybase/go-keychain v0.0.1/go.mod h1:PdEILRW3i9D8JcdM+FmY6RwkHGnhHxXwkPPMeUgOK1k=
github.com/klauspost/compress v1.18.2 h1:iiPHWW0YrcFgpBYhsA6D1+fqHssJscY/Tm/y2Uqnapk=
github.com/klauspost/compress v1.18.2/go.mod h1:R0h/fSBs8DE4ENlcrlib3PsXS61voFxhIs2DeRhCvJ4=
github.com/klauspost/cpuid/v2 v2.3.0 h1:S4CRMLnYUhGeDFDqkGriYKdfoFlDnMtqTiI/sFzhA9Y=
github.com/klauspost/cpuid/v2 v2.3.0/go.mod h1:hqwkgyIinND0mEev00jJYCxPNVRVXFQeu1XKlok6oO0=
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc=
github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw=
github.com/mailru/easyjson v0.9.1 h1:LbtsOm5WAswyWbvTEOqhypdPeZzHavpZx96/n553mR8=
github.com/mailru/easyjson v0.9.1/go.mod h1:1+xMtQp2MRNVL/V1bOzuP3aP8VNwRW55fQUto+XFtTU=
github.com/mark3labs/mcp-go v0.43.2 h1:21PUSlWWiSbUPQwXIJ5WKlETixpFpq+WBpbMGDSVy/I=
github.com/mark3labs/mcp-go v0.43.2/go.mod h1:YnJfOL382MIWDx1kMY+2zsRHU/q78dBg9aFb8W6Thdw=
github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg=
github.com/mattn/go-colorable v0.1.14 h1:9A9LHSqF/7dyVVX6g0U9cwm9pG3kP9gSzcuIPHPsaIE=
github.com/mattn/go-colorable v0.1.14/go.mod h1:6LmQG8QLFO4G5z1gPvYEzlUgJ2wF+stgPZH1UqBm1s8=
github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
github.com/maximhq/bifrost/core v1.5.4 h1:hf0BhoHVVpY1EQ4FkyRzW4IBYjrolxdZV0ucgWfHhcE=
github.com/maximhq/bifrost/core v1.5.4/go.mod h1:z1/vOalbDAD7v7sYbXQsqR+2qIFP0jKOSIStw6Q4P4U=
github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c h1:+mdjkGKdHQG3305AYmdv1U2eRNDiU2ErMBj1gwrq8eQ=
github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c/go.mod h1:7rwL4CYBLnjLxUqIJNnCWiEdr3bn6IUYi15bNlnbCCU=
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U=
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ=
github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc=
github.com/rs/xid v1.6.0/go.mod h1:7XoLgs4eV+QndskICGsho+ADou8ySMSjJKDIan90Nz0=
github.com/rs/zerolog v1.34.0 h1:k43nTLIwcTVQAncfCw4KZ2VY6ukYoZaBPNOE8txlOeY=
github.com/rs/zerolog v1.34.0/go.mod h1:bJsvje4Z08ROH4Nhs5iH600c3IkWhwp44iRc54W6wYQ=
github.com/savsgio/gotils v0.0.0-20250408102913-196191ec6287 h1:qIQ0tWF9vxGtkJa24bR+2i53WBCz1nW/Pc47oVYauC4=
github.com/savsgio/gotils v0.0.0-20250408102913-196191ec6287/go.mod h1:sM7Mt7uEoCeFSCBM+qBrqvEo+/9vdmj19wzp3yzUhmg=
github.com/spf13/cast v1.10.0 h1:h2x0u2shc1QuLHfxi+cTJvs30+ZAHOGRic8uyGTDWxY=
github.com/spf13/cast v1.10.0/go.mod h1:jNfB8QC9IA6ZuY2ZjDp0KtFO2LZZlg4S/7bzP6qqeHo=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA=
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U=
github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U=
github.com/tidwall/gjson v1.14.2/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk=
github.com/tidwall/gjson v1.18.0 h1:FIDeeyB800efLX89e5a8Y0BNH+LOngJyGrIWxG2FKQY=
github.com/tidwall/gjson v1.18.0/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk=
github.com/tidwall/match v1.1.1 h1:+Ho715JplO36QYgwN9PGYNhgZvoUSc9X2c80KVTi+GA=
github.com/tidwall/match v1.1.1/go.mod h1:eRSPERbgtNPcGhD8UCthc6PmLEQXEWd3PRB5JTxsfmM=
github.com/tidwall/pretty v1.2.0 h1:RWIZEg2iJ8/g6fDDYzMpobmaoGh5OLl4AXtGUGPcqCs=
github.com/tidwall/pretty v1.2.0/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU=
github.com/tidwall/sjson v1.2.5 h1:kLy8mja+1c9jlljvWTlSazM7cKDRfJuR/bOJhcY5NcY=
github.com/tidwall/sjson v1.2.5/go.mod h1:Fvgq9kS/6ociJEDnK0Fk1cpYF4FIW6ZF7LAe+6jwd28=
github.com/twitchyliquid64/golang-asm v0.15.1 h1:SU5vSMR7hnwNxj24w34ZyCi/FmDZTkS4MhqMhdFk5YI=
github.com/twitchyliquid64/golang-asm v0.15.1/go.mod h1:a1lVb/DtPvCB8fslRZhAngC2+aY1QWCk3Cedj/Gdt08=
github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw=
github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc=
github.com/valyala/fasthttp v1.68.0 h1:v12Nx16iepr8r9ySOwqI+5RBJ/DqTxhOy1HrHoDFnok=
github.com/valyala/fasthttp v1.68.0/go.mod h1:5EXiRfYQAoiO/khu4oU9VISC/eVY6JqmSpPJoHCKsz4=
github.com/wk8/go-ordered-map/v2 v2.1.8 h1:5h/BUHu93oj4gIdvHHHGsScSTMijfx5PeYkE/fJgbpc=
github.com/wk8/go-ordered-map/v2 v2.1.8/go.mod h1:5nJHM5DyteebpVlHnWMV0rPz6Zp7+xBAnxjb1X5vnTw=
github.com/xyproto/randomstring v1.0.5 h1:YtlWPoRdgMu3NZtP45drfy1GKoojuR7hmRcnhZqKjWU=
github.com/xyproto/randomstring v1.0.5/go.mod h1:rgmS5DeNXLivK7YprL0pY+lTuhNQW3iGxZ18UQApw/E=
github.com/yosida95/uritemplate/v3 v3.0.2 h1:Ed3Oyj9yrmi9087+NczuL5BwkIc4wvTb5zIM+UJPGz4=
github.com/yosida95/uritemplate/v3 v3.0.2/go.mod h1:ILOh0sOhIJR3+L/8afwt/kE++YT040gmv5BQTMR2HP4=
go.starlark.net v0.0.0-20260102030733-3fee463870c9 h1:nV1OyvU+0CYrp5eKfQ3rD03TpFYYhH08z31NK1HmtTk=
go.starlark.net v0.0.0-20260102030733-3fee463870c9/go.mod h1:YKMCv9b1WrfWmeqdV5MAuEHWsu5iC+fe6kYl2sQjdI8=
golang.org/x/arch v0.23.0 h1:lKF64A2jF6Zd8L0knGltUnegD62JMFBiCPBmQpToHhg=
golang.org/x/arch v0.23.0/go.mod h1:dNHoOeKiyja7GTvF9NJS1l3Z2yntpQNzgrjh1cU103A=
golang.org/x/crypto v0.49.0 h1:+Ng2ULVvLHnJ/ZFEq4KdcDd/cfjrrjjNSXNzxg0Y4U4=
golang.org/x/crypto v0.49.0/go.mod h1:ErX4dUh2UM+CFYiXZRTcMpEcN8b/1gxEuv3nODoYtCA=
golang.org/x/net v0.52.0 h1:He/TN1l0e4mmR3QqHMT2Xab3Aj3L9qjbhRm78/6jrW0=
golang.org/x/net v0.52.0/go.mod h1:R1MAz7uMZxVMualyPXb+VaqGSa3LIaUqk0eEt3w36Sw=
golang.org/x/oauth2 v0.36.0 h1:peZ/1z27fi9hUOFCAZaHyrpWG5lwe0RJEEEeH0ThlIs=
golang.org/x/oauth2 v0.36.0/go.mod h1:YDBUJMTkDnJS+A4BP4eZBjCqtokkg1hODuPjwiGPO7Q=
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.42.0 h1:omrd2nAlyT5ESRdCLYdm3+fMfNFE/+Rf4bDIQImRJeo=
golang.org/x/sys v0.42.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw=
golang.org/x/text v0.35.0 h1:JOVx6vVDFokkpaq1AEptVzLTpDe9KGpj5tR4/X+ybL8=
golang.org/x/text v0.35.0/go.mod h1:khi/HExzZJ2pGnjenulevKNX1W67CUy0AsXcNubPGCA=
google.golang.org/protobuf v1.36.11 h1:fV6ZwhNocDyBLK0dj+fg8ektcVegBBuEolpbTQyBNVE=
google.golang.org/protobuf v1.36.11/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=

1260
plugins/mocker/main.go Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,532 @@
package mocker
import (
"context"
"testing"
bifrost "github.com/maximhq/bifrost/core"
"github.com/maximhq/bifrost/core/schemas"
)
// BaseAccount implements the schemas.Account interface for testing purposes.
// It provides mock implementations of the required methods to test the Mocker plugin
// with a basic OpenAI configuration.
type BaseAccount struct{}
// GetConfiguredProviders returns a list of supported providers for testing.
func (baseAccount *BaseAccount) GetConfiguredProviders() ([]schemas.ModelProvider, error) {
return []schemas.ModelProvider{schemas.OpenAI, schemas.Anthropic}, nil
}
// GetKeysForProvider returns a dummy API key configuration for testing.
// Since we're testing the mocker plugin, these keys should never be used
// as the plugin intercepts requests before they reach the actual providers.
func (baseAccount *BaseAccount) GetKeysForProvider(ctx context.Context, providerKey schemas.ModelProvider) ([]schemas.Key, error) {
return []schemas.Key{
{
Value: *schemas.NewEnvVar("dummy-api-key-for-testing"), // Dummy key
Models: []string{"gpt-4", "gpt-4-turbo", "claude-3"},
Weight: 1.0,
},
}, nil
}
// GetConfigForProvider returns default provider configuration for testing.
func (baseAccount *BaseAccount) GetConfigForProvider(providerKey schemas.ModelProvider) (*schemas.ProviderConfig, error) {
return &schemas.ProviderConfig{
NetworkConfig: schemas.DefaultNetworkConfig,
ConcurrencyAndBufferSize: schemas.DefaultConcurrencyAndBufferSize,
}, nil
}
// TestMockerPlugin_GetName tests the plugin name
func TestMockerPlugin_GetName(t *testing.T) {
plugin, err := Init(MockerConfig{})
if err != nil {
t.Fatalf("Expected no error creating plugin, got: %v", err)
}
if plugin.GetName() != PluginName {
t.Errorf("Expected '%s', got '%s'", PluginName, plugin.GetName())
}
}
// TestMockerPlugin_Disabled tests that disabled plugin doesn't interfere
func TestMockerPlugin_Disabled(t *testing.T) {
ctx := schemas.NewBifrostContext(context.Background(), schemas.NoDeadline)
config := MockerConfig{
Enabled: false,
}
plugin, err := Init(config)
if err != nil {
t.Fatalf("Expected no error creating plugin, got: %v", err)
}
account := BaseAccount{}
client, err := bifrost.Init(ctx, schemas.BifrostConfig{
Account: &account,
LLMPlugins: []schemas.LLMPlugin{plugin},
Logger: bifrost.NewDefaultLogger(schemas.LogLevelError),
})
if err != nil {
t.Fatalf("Error initializing Bifrost: %v", err)
}
defer client.Shutdown()
// This should pass through to the real provider (but will fail due to dummy key)
_, bifrostErr := client.ChatCompletionRequest(ctx, &schemas.BifrostChatRequest{
Provider: schemas.OpenAI,
Model: "gpt-4",
Input: []schemas.ChatMessage{
{
Role: schemas.ChatMessageRoleUser,
Content: &schemas.ChatMessageContent{
ContentStr: bifrost.Ptr("Hello, test message"),
},
},
},
})
// Should get an authentication error from OpenAI, not a mock response
// This proves the plugin is disabled and not intercepting requests
if bifrostErr == nil {
t.Error("Expected error from real provider with dummy API key")
}
}
// TestMockerPlugin_DefaultMockRule tests the default catch-all rule
func TestMockerPlugin_DefaultMockRule(t *testing.T) {
ctx := schemas.NewBifrostContext(context.Background(), schemas.NoDeadline)
config := MockerConfig{
Enabled: true, // No rules provided, should create default rule
}
plugin, err := Init(config)
if err != nil {
t.Fatalf("Expected no error creating plugin, got: %v", err)
}
account := BaseAccount{}
client, err := bifrost.Init(ctx, schemas.BifrostConfig{
Account: &account,
LLMPlugins: []schemas.LLMPlugin{plugin},
Logger: bifrost.NewDefaultLogger(schemas.LogLevelError),
})
if err != nil {
t.Fatalf("Error initializing Bifrost: %v", err)
}
defer client.Shutdown()
response, bifrostErr := client.ChatCompletionRequest(ctx, &schemas.BifrostChatRequest{
Provider: schemas.OpenAI,
Model: "gpt-4",
Input: []schemas.ChatMessage{
{
Role: schemas.ChatMessageRoleUser,
Content: &schemas.ChatMessageContent{
ContentStr: bifrost.Ptr("Hello, test message"),
},
},
},
})
if bifrostErr != nil {
t.Fatalf("Expected no error, got: %v", bifrostErr)
}
if response == nil {
t.Fatal("Expected response")
}
if len(response.Choices) == 0 {
t.Fatal("Expected at least one choice")
}
if response.Choices[0].ChatNonStreamResponseChoice.Message.Content.ContentStr == nil {
t.Fatal("Expected content string")
}
if *response.Choices[0].ChatNonStreamResponseChoice.Message.Content.ContentStr != "This is a mock response from the Mocker plugin" {
t.Errorf("Expected default mock message, got: %s", *response.Choices[0].ChatNonStreamResponseChoice.Message.Content.ContentStr)
}
}
// TestMockerPlugin_CustomSuccessRule tests custom success response
func TestMockerPlugin_CustomSuccessRule(t *testing.T) {
ctx := schemas.NewBifrostContext(context.Background(), schemas.NoDeadline)
config := MockerConfig{
Enabled: true,
Rules: []MockRule{
{
Name: "openai-success",
Enabled: true,
Priority: 100,
Probability: 1.0,
Conditions: Conditions{
Providers: []string{"openai"},
},
Responses: []Response{
{
Type: ResponseTypeSuccess,
Content: &SuccessResponse{
Message: "Custom OpenAI mock response",
Usage: &Usage{
PromptTokens: 15,
CompletionTokens: 25,
TotalTokens: 40,
},
},
},
},
},
},
}
plugin, err := Init(config)
if err != nil {
t.Fatalf("Expected no error creating plugin, got: %v", err)
}
account := BaseAccount{}
client, err := bifrost.Init(ctx, schemas.BifrostConfig{
Account: &account,
LLMPlugins: []schemas.LLMPlugin{plugin},
Logger: bifrost.NewDefaultLogger(schemas.LogLevelError),
})
if err != nil {
t.Fatalf("Error initializing Bifrost: %v", err)
}
defer client.Shutdown()
response, bifrostErr := client.ChatCompletionRequest(ctx, &schemas.BifrostChatRequest{
Provider: schemas.OpenAI,
Model: "gpt-4",
Input: []schemas.ChatMessage{
{
Role: schemas.ChatMessageRoleUser,
Content: &schemas.ChatMessageContent{
ContentStr: bifrost.Ptr("Hello, test message"),
},
},
},
})
if bifrostErr != nil {
t.Fatalf("Expected no error, got: %v", bifrostErr)
}
if response == nil {
t.Fatal("Expected response")
}
if len(response.Choices) == 0 {
t.Fatal("Expected at least one choice")
}
if response.Choices[0].ChatNonStreamResponseChoice.Message.Content.ContentStr == nil {
t.Fatal("Expected content string")
}
if *response.Choices[0].ChatNonStreamResponseChoice.Message.Content.ContentStr != "Custom OpenAI mock response" {
t.Errorf("Expected custom message, got: %s", *response.Choices[0].ChatNonStreamResponseChoice.Message.Content.ContentStr)
}
if response.Usage.TotalTokens != 40 {
t.Errorf("Expected 40 total tokens, got %d", response.Usage.TotalTokens)
}
}
// TestMockerPlugin_ErrorResponse tests error response generation
func TestMockerPlugin_ErrorResponse(t *testing.T) {
ctx := schemas.NewBifrostContext(context.Background(), schemas.NoDeadline)
allowFallbacks := false
config := MockerConfig{
Enabled: true,
Rules: []MockRule{
{
Name: "rate-limit-error",
Enabled: true,
Priority: 100,
Probability: 1.0,
Conditions: Conditions{
Providers: []string{"openai"},
},
Responses: []Response{
{
Type: ResponseTypeError,
AllowFallbacks: &allowFallbacks,
Error: &ErrorResponse{
Message: "Rate limit exceeded",
Type: bifrost.Ptr("rate_limit"),
Code: bifrost.Ptr("429"),
StatusCode: bifrost.Ptr(429),
},
},
},
},
},
}
plugin, err := Init(config)
if err != nil {
t.Fatalf("Expected no error creating plugin, got: %v", err)
}
account := BaseAccount{}
client, err := bifrost.Init(ctx, schemas.BifrostConfig{
Account: &account,
LLMPlugins: []schemas.LLMPlugin{plugin},
Logger: bifrost.NewDefaultLogger(schemas.LogLevelError),
})
if err != nil {
t.Fatalf("Error initializing Bifrost: %v", err)
}
defer client.Shutdown()
_, bifrostErr := client.ChatCompletionRequest(ctx, &schemas.BifrostChatRequest{
Provider: schemas.OpenAI,
Model: "gpt-4",
Input: []schemas.ChatMessage{
{
Role: schemas.ChatMessageRoleUser,
Content: &schemas.ChatMessageContent{
ContentStr: bifrost.Ptr("Hello, test message"),
},
},
},
})
if bifrostErr == nil {
t.Fatal("Expected error response")
}
if bifrostErr.Error.Message != "Rate limit exceeded" {
t.Errorf("Expected 'Rate limit exceeded', got: %s", bifrostErr.Error.Message)
}
if bifrostErr.StatusCode == nil || *bifrostErr.StatusCode != 429 {
t.Errorf("Expected status code 429, got: %v", bifrostErr.StatusCode)
}
}
// TestMockerPlugin_MessageTemplate tests template variable substitution
func TestMockerPlugin_MessageTemplate(t *testing.T) {
ctx := schemas.NewBifrostContext(context.Background(), schemas.NoDeadline)
config := MockerConfig{
Enabled: true,
Rules: []MockRule{
{
Name: "template-test",
Enabled: true,
Priority: 100,
Probability: 1.0,
Conditions: Conditions{}, // Match all
Responses: []Response{
{
Type: ResponseTypeSuccess,
Content: &SuccessResponse{
MessageTemplate: bifrost.Ptr("Hello from {{provider}} using model {{model}}"),
},
},
},
},
},
}
plugin, err := Init(config)
if err != nil {
t.Fatalf("Expected no error creating plugin, got: %v", err)
}
account := BaseAccount{}
client, err := bifrost.Init(ctx, schemas.BifrostConfig{
Account: &account,
LLMPlugins: []schemas.LLMPlugin{plugin},
Logger: bifrost.NewDefaultLogger(schemas.LogLevelError),
})
if err != nil {
t.Fatalf("Error initializing Bifrost: %v", err)
}
defer client.Shutdown()
response, bifrostErr := client.ChatCompletionRequest(ctx, &schemas.BifrostChatRequest{
Provider: schemas.Anthropic,
Model: "claude-3",
Input: []schemas.ChatMessage{
{
Role: schemas.ChatMessageRoleUser,
Content: &schemas.ChatMessageContent{
ContentStr: bifrost.Ptr("Hello, test message"),
},
},
},
})
if bifrostErr != nil {
t.Fatalf("Expected no error, got: %v", bifrostErr)
}
if response == nil {
t.Fatal("Expected response")
}
if len(response.Choices) == 0 {
t.Fatal("Expected at least one choice")
}
if response.Choices[0].ChatNonStreamResponseChoice.Message.Content.ContentStr == nil {
t.Fatal("Expected content string")
}
expectedMessage := "Hello from anthropic using model claude-3"
if *response.Choices[0].ChatNonStreamResponseChoice.Message.Content.ContentStr != expectedMessage {
t.Errorf("Expected '%s', got: %s", expectedMessage, *response.Choices[0].ChatNonStreamResponseChoice.Message.Content.ContentStr)
}
}
// TestMockerPlugin_Statistics tests plugin statistics tracking
func TestMockerPlugin_Statistics(t *testing.T) {
ctx := schemas.NewBifrostContext(context.Background(), schemas.NoDeadline)
config := MockerConfig{
Enabled: true,
Rules: []MockRule{
{
Name: "stats-test",
Enabled: true,
Priority: 100,
Probability: 1.0,
Conditions: Conditions{}, // Match all
Responses: []Response{
{
Type: ResponseTypeSuccess,
Content: &SuccessResponse{
Message: "Stats test response",
},
},
},
},
},
}
plugin, err := Init(config)
if err != nil {
t.Fatalf("Expected no error creating plugin, got: %v", err)
}
account := BaseAccount{}
client, err := bifrost.Init(ctx, schemas.BifrostConfig{
Account: &account,
LLMPlugins: []schemas.LLMPlugin{plugin},
Logger: bifrost.NewDefaultLogger(schemas.LogLevelError),
})
if err != nil {
t.Fatalf("Error initializing Bifrost: %v", err)
}
defer client.Shutdown()
// Make multiple requests
for i := 0; i < 3; i++ {
_, _ = client.ChatCompletionRequest(ctx, &schemas.BifrostChatRequest{
Provider: schemas.OpenAI,
Model: "gpt-4",
Input: []schemas.ChatMessage{
{
Role: schemas.ChatMessageRoleUser,
Content: &schemas.ChatMessageContent{
ContentStr: bifrost.Ptr("Hello, test message"),
},
},
},
})
}
// Check statistics
stats := plugin.GetStats()
if stats.TotalRequests != 3 {
t.Errorf("Expected 3 total requests, got %d", stats.TotalRequests)
}
if stats.MockedRequests != 3 {
t.Errorf("Expected 3 mocked requests, got %d", stats.MockedRequests)
}
if stats.ResponsesGenerated != 3 {
t.Errorf("Expected 3 responses generated, got %d", stats.ResponsesGenerated)
}
if stats.RuleHits["stats-test"] != 3 {
t.Errorf("Expected 3 hits for 'stats-test' rule, got %d", stats.RuleHits["stats-test"])
}
}
// TestMockerPlugin_ValidationErrors tests configuration validation
func TestMockerPlugin_ValidationErrors(t *testing.T) {
tests := []struct {
name string
config MockerConfig
expectError bool
}{
{
name: "invalid default behavior",
config: MockerConfig{
Enabled: true,
DefaultBehavior: "invalid",
},
expectError: true,
},
{
name: "missing rule name",
config: MockerConfig{
Enabled: true,
Rules: []MockRule{
{
Name: "", // Missing name
Enabled: true,
Responses: []Response{
{
Type: ResponseTypeSuccess,
Content: &SuccessResponse{
Message: "test",
},
},
},
},
},
},
expectError: true,
},
{
name: "invalid probability",
config: MockerConfig{
Enabled: true,
Rules: []MockRule{
{
Name: "test",
Enabled: true,
Probability: 1.5, // Invalid probability > 1
Responses: []Response{
{
Type: ResponseTypeSuccess,
Content: &SuccessResponse{
Message: "test",
},
},
},
},
},
},
expectError: true,
},
{
name: "valid configuration",
config: MockerConfig{
Enabled: true,
DefaultBehavior: DefaultBehaviorPassthrough,
Rules: []MockRule{
{
Name: "valid-rule",
Enabled: true,
Probability: 0.5,
Responses: []Response{
{
Type: ResponseTypeSuccess,
Content: &SuccessResponse{
Message: "Valid response",
},
},
},
},
},
},
expectError: false,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
_, err := Init(tt.config)
if tt.expectError && err == nil {
t.Error("Expected error but got none")
}
if !tt.expectError && err != nil {
t.Errorf("Expected no error but got: %v", err)
}
})
}
}

1
plugins/mocker/version Normal file
View File

@@ -0,0 +1 @@
1.5.4