| package jsonparser |
|
|
| import ( |
| "context" |
| "os" |
| "testing" |
| "time" |
|
|
| bifrost "github.com/maximhq/bifrost/core" |
| "github.com/maximhq/bifrost/core/schemas" |
| ) |
|
|
| |
| |
| |
| type BaseAccount struct{} |
|
|
| |
| |
| func (baseAccount *BaseAccount) GetConfiguredProviders() ([]schemas.ModelProvider, error) { |
| return []schemas.ModelProvider{schemas.OpenAI}, nil |
| } |
|
|
| |
| |
| func (baseAccount *BaseAccount) GetKeysForProvider(ctx context.Context, providerKey schemas.ModelProvider) ([]schemas.Key, error) { |
| return []schemas.Key{ |
| { |
| Value: *schemas.NewEnvVar("env.OPENAI_API_KEY"), |
| Models: []string{"gpt-4o-mini", "gpt-4-turbo"}, |
| Weight: 1.0, |
| }, |
| }, nil |
| } |
|
|
| |
| |
| func (baseAccount *BaseAccount) GetConfigForProvider(providerKey schemas.ModelProvider) (*schemas.ProviderConfig, error) { |
| return &schemas.ProviderConfig{ |
| NetworkConfig: schemas.DefaultNetworkConfig, |
| ConcurrencyAndBufferSize: schemas.DefaultConcurrencyAndBufferSize, |
| }, nil |
| } |
|
|
| func (baseAccount *BaseAccount) GetGlobalKeys(ctx context.Context) ([]schemas.Key, error) { |
| return nil, nil |
| } |
|
|
| |
| |
| |
| |
| |
| |
| |
| |
| |
| func TestJsonParserPluginEndToEnd(t *testing.T) { |
| ctx := schemas.NewBifrostContext(context.Background(), schemas.NoDeadline) |
| |
| if os.Getenv("OPENAI_API_KEY") == "" { |
| t.Skip("OPENAI_API_KEY is not set, skipping end-to-end test") |
| } |
|
|
| |
| plugin, err := Init(PluginConfig{ |
| Usage: AllRequests, |
| CleanupInterval: 5 * time.Minute, |
| MaxAge: 30 * time.Minute, |
| }) |
| if err != nil { |
| t.Fatalf("Error initializing JSON parser plugin: %v", err) |
| } |
|
|
| account := BaseAccount{} |
|
|
| |
| client, err := bifrost.Init(ctx, schemas.BifrostConfig{ |
| Account: &account, |
| LLMPlugins: []schemas.LLMPlugin{plugin}, |
| Logger: bifrost.NewDefaultLogger(schemas.LogLevelDebug), |
| }) |
| if err != nil { |
| t.Fatalf("Error initializing Bifrost: %v", err) |
| } |
| defer client.Shutdown() |
|
|
| |
| |
| var responseFormat interface{} = map[string]interface{}{ |
| "type": "json_object", |
| } |
|
|
| request := &schemas.BifrostChatRequest{ |
| Provider: schemas.OpenAI, |
| Model: "gpt-4o-mini", |
| Input: []schemas.ChatMessage{ |
| { |
| Role: schemas.ChatMessageRoleUser, |
| Content: &schemas.ChatMessageContent{ |
| ContentStr: bifrost.Ptr("Return a JSON object with name, age, and city fields. Example: {\"name\": \"John\", \"age\": 30, \"city\": \"New York\"}"), |
| }, |
| }, |
| }, |
| Params: &schemas.ChatParameters{ |
| ResponseFormat: &responseFormat, |
| }, |
| } |
| |
| responseChan, bifrostErr := client.ChatCompletionStreamRequest(ctx, request) |
|
|
| if bifrostErr != nil { |
| t.Fatalf("Error in Bifrost request: %v", bifrostErr) |
| } |
|
|
| |
| if responseChan != nil { |
| t.Logf("Streaming response channel received") |
|
|
| |
| responseCount := 0 |
|
|
| for streamResponse := range responseChan { |
| responseCount++ |
|
|
| if streamResponse.BifrostError != nil { |
| t.Logf("Streaming response error: %v", streamResponse.BifrostError) |
| } |
|
|
| if streamResponse.BifrostChatResponse != nil { |
| if streamResponse.BifrostChatResponse.Choices != nil { |
| for _, outputMsg := range streamResponse.BifrostChatResponse.Choices { |
| if outputMsg.ChatStreamResponseChoice != nil && outputMsg.ChatStreamResponseChoice.Delta.Content != nil { |
| content := *outputMsg.ChatStreamResponseChoice.Delta.Content |
| if content != "" { |
| t.Logf("Chunk %d: %s", responseCount, content) |
| } |
| } |
| } |
| } |
| } |
| } |
|
|
| t.Logf("Stream completed after %d responses", responseCount) |
| } else { |
| t.Logf("No streaming response channel received") |
| } |
|
|
| t.Log("End-to-end test completed - check logs for JSON parsing behavior") |
| } |
|
|
| |
| |
| |
| |
| |
| func TestJsonParserPluginPerRequest(t *testing.T) { |
| ctx := context.Background() |
| |
| if os.Getenv("OPENAI_API_KEY") == "" { |
| t.Skip("OPENAI_API_KEY is not set, skipping per-request test") |
| } |
|
|
| |
| plugin, err := Init(PluginConfig{ |
| Usage: PerRequest, |
| CleanupInterval: 5 * time.Minute, |
| MaxAge: 30 * time.Minute, |
| }) |
| if err != nil { |
| t.Fatalf("Error initializing JSON parser plugin: %v", err) |
| } |
|
|
| account := BaseAccount{} |
|
|
| |
| client, err := bifrost.Init(ctx, schemas.BifrostConfig{ |
| Account: &account, |
| LLMPlugins: []schemas.LLMPlugin{plugin}, |
| Logger: bifrost.NewDefaultLogger(schemas.LogLevelDebug), |
| }) |
| if err != nil { |
| t.Fatalf("Error initializing Bifrost: %v", err) |
| } |
| defer client.Shutdown() |
|
|
| |
| var responseFormat interface{} = map[string]interface{}{ |
| "type": "json_object", |
| } |
|
|
| request := &schemas.BifrostChatRequest{ |
| Provider: schemas.OpenAI, |
| Model: "gpt-4o-mini", |
| Input: []schemas.ChatMessage{ |
| { |
| Role: schemas.ChatMessageRoleUser, |
| Content: &schemas.ChatMessageContent{ |
| ContentStr: bifrost.Ptr("Return a JSON object with name and age fields."), |
| }, |
| }, |
| }, |
| Params: &schemas.ChatParameters{ |
| ResponseFormat: &responseFormat, |
| }, |
| } |
|
|
| |
| newContext := schemas.NewBifrostContext(context.Background(), schemas.NoDeadline).WithValue(EnableStreamingJSONParser, true) |
|
|
| |
| responseChan, bifrostErr := client.ChatCompletionStreamRequest(newContext, request) |
|
|
| if bifrostErr != nil { |
| t.Logf("Error in Bifrost request: %v", bifrostErr) |
| } |
|
|
| |
| if responseChan != nil { |
| t.Logf("Streaming response channel received for per-request test") |
|
|
| |
| responseCount := 0 |
|
|
| for streamResponse := range responseChan { |
| responseCount++ |
|
|
| if streamResponse.BifrostError != nil { |
| t.Logf("Streaming response error: %v", streamResponse.BifrostError) |
| } |
|
|
| if streamResponse.BifrostChatResponse != nil { |
| for _, choice := range streamResponse.BifrostChatResponse.Choices { |
| if choice.ChatStreamResponseChoice != nil && choice.ChatStreamResponseChoice.Delta.Content != nil { |
| content := *choice.ChatStreamResponseChoice.Delta.Content |
| if content != "" { |
| t.Logf("Per-request chunk %d: %s", responseCount, content) |
| } |
| } |
| } |
| } |
| } |
|
|
| t.Logf("Per-request stream completed after %d responses", responseCount) |
| } else { |
| t.Logf("No streaming response channel received for per-request test") |
| } |
|
|
| t.Log("Per-request test completed - check logs for JSON parsing behavior") |
| } |
|
|
| func TestParsePartialJSON(t *testing.T) { |
| plugin, err := Init(PluginConfig{ |
| Usage: AllRequests, |
| CleanupInterval: 5 * time.Minute, |
| MaxAge: 30 * time.Minute, |
| }) |
| if err != nil { |
| t.Fatalf("Error initializing JSON parser plugin: %v", err) |
| } |
|
|
| tests := []struct { |
| name string |
| input string |
| expected string |
| }{ |
| { |
| name: "Already valid JSON object", |
| input: `{"name": "John", "age": 30}`, |
| expected: `{"name": "John", "age": 30}`, |
| }, |
| { |
| name: "Partial JSON object missing closing brace", |
| input: `{"name": "John", "age": 30, "city": "New York"`, |
| expected: `{"name": "John", "age": 30, "city": "New York"}`, |
| }, |
| { |
| name: "Partial JSON array missing closing bracket", |
| input: `["apple", "banana", "cherry"`, |
| expected: `["apple", "banana", "cherry"]`, |
| }, |
| { |
| name: "Nested partial JSON", |
| input: `{"user": {"name": "John", "details": {"age": 30, "city": "NY"`, |
| expected: `{"user": {"name": "John", "details": {"age": 30, "city": "NY"}}}`, |
| }, |
| { |
| name: "Partial JSON with string containing newline", |
| input: `{"message": "Hello\nWorld"`, |
| expected: `{"message": "Hello\nWorld"}`, |
| }, |
| { |
| name: "Empty string", |
| input: "", |
| expected: "{}", |
| }, |
| { |
| name: "Whitespace only", |
| input: " \n\t ", |
| expected: "{}", |
| }, |
| { |
| name: "Non-JSON string", |
| input: "This is not JSON", |
| expected: "This is not JSON", |
| }, |
| { |
| name: "Partial JSON with escaped quotes", |
| input: `{"message": "He said \"Hello\""`, |
| expected: `{"message": "He said \"Hello\""}`, |
| }, |
| { |
| name: "Complex nested structure", |
| input: `{"data": {"users": [{"id": 1, "name": "John"}, {"id": 2, "name": "Jane"`, |
| expected: `{"data": {"users": [{"id": 1, "name": "John"}, {"id": 2, "name": "Jane"}]}}`, |
| }, |
| } |
|
|
| for _, tt := range tests { |
| t.Run(tt.name, func(t *testing.T) { |
| result := plugin.parsePartialJSON(tt.input) |
| if result != tt.expected { |
| t.Errorf("parsePartialJSON(%q) = %q, want %q", tt.input, result, tt.expected) |
| } |
| }) |
| } |
| } |
|
|