| package jsonparser |
|
|
| import ( |
| "encoding/json" |
| "strings" |
| "time" |
|
|
| "github.com/maximhq/bifrost/core/schemas" |
| ) |
|
|
| |
| func (p *JsonParserPlugin) getRequestID(ctx *schemas.BifrostContext, result *schemas.BifrostResponse) string { |
|
|
| |
| if result != nil && result.ChatResponse != nil && result.ChatResponse.ID != "" { |
| return result.ChatResponse.ID |
| } |
|
|
| |
| if ctx != nil { |
| if requestID, ok := ctx.Value(schemas.BifrostContextKeyRequestID).(string); ok && requestID != "" { |
| return requestID |
| } |
| } |
|
|
| return "" |
| } |
|
|
| |
| func (p *JsonParserPlugin) shouldRun(ctx *schemas.BifrostContext, requestType schemas.RequestType) bool { |
| |
| if requestType != schemas.ChatCompletionStreamRequest { |
| return false |
| } |
|
|
| switch p.usage { |
| case AllRequests: |
| return true |
| case PerRequest: |
| |
| if ctx != nil { |
| if value, ok := ctx.Value(EnableStreamingJSONParser).(bool); ok { |
| return value |
| } |
| } |
| return false |
| default: |
| return false |
| } |
| } |
|
|
| |
| func (p *JsonParserPlugin) accumulateContent(requestID, newContent string) string { |
| p.mutex.Lock() |
| defer p.mutex.Unlock() |
|
|
| |
| existing := p.accumulatedContent[requestID] |
|
|
| if existing != nil { |
| |
| existing.Content.WriteString(newContent) |
| return existing.Content.String() |
| } else { |
| |
| builder := &strings.Builder{} |
| builder.WriteString(newContent) |
| p.accumulatedContent[requestID] = &AccumulatedContent{ |
| Content: builder, |
| Timestamp: time.Now(), |
| } |
| return builder.String() |
| } |
| } |
|
|
| |
| func (p *JsonParserPlugin) parsePartialJSON(s string) string { |
| |
| s = strings.TrimSpace(s) |
| if s == "" { |
| return "{}" |
| } |
|
|
| |
| if s[0] != '{' && s[0] != '[' { |
| return s |
| } |
|
|
| |
| if p.isValidJSON(s) { |
| return s |
| } |
|
|
| |
| return p.completeJSON(s) |
| } |
|
|
| |
| func (p *JsonParserPlugin) completeJSON(s string) string { |
| |
| capacity := len(s) + 10 |
| result := make([]byte, 0, capacity) |
|
|
| var stack []byte |
| inString := false |
| escaped := false |
|
|
| |
| for i := 0; i < len(s); i++ { |
| char := s[i] |
| result = append(result, char) |
|
|
| if escaped { |
| escaped = false |
| continue |
| } |
|
|
| if char == '\\' { |
| escaped = true |
| continue |
| } |
|
|
| if char == '"' { |
| inString = !inString |
| continue |
| } |
|
|
| if inString { |
| continue |
| } |
|
|
| switch char { |
| case '{', '[': |
| if char == '{' { |
| stack = append(stack, '}') |
| } else { |
| stack = append(stack, ']') |
| } |
| case '}', ']': |
| if len(stack) > 0 && stack[len(stack)-1] == char { |
| stack = stack[:len(stack)-1] |
| } |
| } |
| } |
|
|
| |
| if inString { |
| if escaped { |
| |
| if len(result) > 0 { |
| result = result[:len(result)-1] |
| } |
| } |
| result = append(result, '"') |
| } |
|
|
| |
| for i := len(stack) - 1; i >= 0; i-- { |
| result = append(result, stack[i]) |
| } |
|
|
| |
| if p.isValidJSON(string(result)) { |
| return string(result) |
| } |
|
|
| |
| return p.progressiveTruncation(s, result) |
| } |
|
|
| |
| func (p *JsonParserPlugin) progressiveTruncation(original string, completed []byte) string { |
| |
| |
| left, right := 0, len(completed) |
|
|
| for left < right { |
| mid := (left + right) / 2 |
| candidate := completed[:mid] |
|
|
| if p.isValidJSON(string(candidate)) { |
| left = mid + 1 |
| } else { |
| right = mid |
| } |
| } |
|
|
| |
| if left > 0 && p.isValidJSON(string(completed[:left-1])) { |
| return string(completed[:left-1]) |
| } |
|
|
| |
| return original |
| } |
|
|
| |
| func (p *JsonParserPlugin) isValidJSON(s string) bool { |
| |
| s = strings.TrimSpace(s) |
|
|
| |
| if s == "" { |
| return false |
| } |
|
|
| return json.Valid([]byte(s)) |
| } |
|
|
| |
|
|
| |
| func (p *JsonParserPlugin) deepCopyBifrostResponse(original *schemas.BifrostResponse) *schemas.BifrostResponse { |
| if original == nil { |
| return nil |
| } |
|
|
| |
| result := &schemas.BifrostResponse{} |
|
|
| |
| if original.ChatResponse != nil { |
| result.ChatResponse = p.deepCopyBifrostChatResponse(original.ChatResponse) |
| } |
|
|
| |
| result.TextCompletionResponse = original.TextCompletionResponse |
| result.ResponsesResponse = original.ResponsesResponse |
| result.ResponsesStreamResponse = original.ResponsesStreamResponse |
| result.EmbeddingResponse = original.EmbeddingResponse |
| result.SpeechResponse = original.SpeechResponse |
| result.SpeechStreamResponse = original.SpeechStreamResponse |
| result.TranscriptionResponse = original.TranscriptionResponse |
| result.TranscriptionStreamResponse = original.TranscriptionStreamResponse |
|
|
| return result |
| } |
|
|
| |
| func (p *JsonParserPlugin) deepCopyBifrostChatResponse(original *schemas.BifrostChatResponse) *schemas.BifrostChatResponse { |
| if original == nil { |
| return nil |
| } |
|
|
| result := &schemas.BifrostChatResponse{ |
| ID: original.ID, |
| Created: original.Created, |
| Model: original.Model, |
| Object: original.Object, |
| ServiceTier: original.ServiceTier, |
| SystemFingerprint: original.SystemFingerprint, |
| Usage: original.Usage, |
| ExtraFields: original.ExtraFields, |
| } |
|
|
| |
| if original.Choices != nil { |
| result.Choices = make([]schemas.BifrostResponseChoice, len(original.Choices)) |
| for i, choice := range original.Choices { |
| result.Choices[i] = p.deepCopyBifrostResponseChoice(choice) |
| } |
| } |
|
|
| return result |
| } |
|
|
| |
| func (p *JsonParserPlugin) deepCopyBifrostResponseChoice(original schemas.BifrostResponseChoice) schemas.BifrostResponseChoice { |
| result := schemas.BifrostResponseChoice{ |
| Index: original.Index, |
| FinishReason: original.FinishReason, |
| LogProbs: original.LogProbs, |
| } |
|
|
| |
| if original.ChatStreamResponseChoice != nil { |
| result.ChatStreamResponseChoice = p.deepCopyChatStreamResponseChoice(original.ChatStreamResponseChoice) |
| } |
|
|
| |
| result.ChatNonStreamResponseChoice = original.ChatNonStreamResponseChoice |
| result.TextCompletionResponseChoice = original.TextCompletionResponseChoice |
|
|
| return result |
| } |
|
|
| |
| func (p *JsonParserPlugin) deepCopyChatStreamResponseChoice(original *schemas.ChatStreamResponseChoice) *schemas.ChatStreamResponseChoice { |
| if original == nil { |
| return nil |
| } |
|
|
| result := &schemas.ChatStreamResponseChoice{} |
|
|
| |
| if original.Delta != nil { |
| result.Delta = p.deepCopyChatStreamResponseChoiceDelta(original.Delta) |
| } |
|
|
| return result |
| } |
|
|
| |
| func (p *JsonParserPlugin) deepCopyChatStreamResponseChoiceDelta(original *schemas.ChatStreamResponseChoiceDelta) *schemas.ChatStreamResponseChoiceDelta { |
| if original == nil { |
| return nil |
| } |
|
|
| result := &schemas.ChatStreamResponseChoiceDelta{ |
| Role: original.Role, |
| Reasoning: original.Reasoning, |
| Refusal: original.Refusal, |
| ToolCalls: original.ToolCalls, |
| } |
|
|
| |
| if original.Content != nil { |
| contentCopy := *original.Content |
| result.Content = &contentCopy |
| } |
|
|
| return result |
| } |
|
|