diff --git a/core/schemas/bifrost.go b/core/schemas/bifrost.go index b932dc1aee..9a9a538237 100644 --- a/core/schemas/bifrost.go +++ b/core/schemas/bifrost.go @@ -250,8 +250,8 @@ const ( BifrostContextKeyRequestHeaders BifrostContextKey = "bifrost-request-headers" // map[string]string (all request headers with lowercased keys) BifrostContextKeySkipListModelsGovernanceFiltering BifrostContextKey = "bifrost-skip-list-models-governance-filtering" // bool (set by bifrost - DO NOT SET THIS MANUALLY)) BifrostContextKeySCIMClaims BifrostContextKey = "scim_claims" - BifrostContextKeyUserID BifrostContextKey = "bifrost-user-id" // string (to store the user ID (set by enterprise auth middleware - DO NOT SET THIS MANUALLY)) - BifrostContextKeyUserName BifrostContextKey = "bifrost-user-name" // string (to store the user name (set by enterprise auth middleware - DO NOT SET THIS MANUALLY)) + BifrostContextKeyUserID BifrostContextKey = "bifrost-user-id" // string (to store the user ID (set by enterprise auth middleware - DO NOT SET THIS MANUALLY)) + BifrostContextKeyUserName BifrostContextKey = "bifrost-user-name" // string (to store the user name (set by enterprise auth middleware - DO NOT SET THIS MANUALLY)) BifrostContextKeyTargetUserID BifrostContextKey = "target_user_id" BifrostContextKeyIsAzureUserAgent BifrostContextKey = "bifrost-is-azure-user-agent" // bool (set by bifrost - DO NOT SET THIS MANUALLY)) - whether the request is an Azure user agent (only used in gateway) BifrostContextKeyVideoOutputRequested BifrostContextKey = "bifrost-video-output-requested" diff --git a/docs/features/async-inference.mdx b/docs/features/async-inference.mdx index 23c9fcab1f..3d0428d5cf 100644 --- a/docs/features/async-inference.mdx +++ b/docs/features/async-inference.mdx @@ -50,6 +50,7 @@ Streaming is not supported on async endpoints. | Image generations | `/v1/async/images/generations` | `/v1/async/images/generations/{job_id}` | | Image edits | `/v1/async/images/edits` | `/v1/async/images/edits/{job_id}` | | Image variations | `/v1/async/images/variations` | `/v1/async/images/variations/{job_id}` | +| OCR | `/v1/async/ocr` | `/v1/async/ocr/{job_id}` | | Rerank | `/v1/async/rerank` | `/v1/async/rerank/{job_id}` | ## Submitting a Request diff --git a/framework/logstore/migrations.go b/framework/logstore/migrations.go index 9993e2f9df..957d8a1a14 100644 --- a/framework/logstore/migrations.go +++ b/framework/logstore/migrations.go @@ -239,6 +239,9 @@ func triggerMigrations(ctx context.Context, db *gorm.DB) error { if err := migrationAddUserNameColumn(ctx, db); err != nil { return err } + if err := migrationAddOCRInputColumn(ctx, db); err != nil { + return err + } return nil } @@ -2579,3 +2582,36 @@ func migrationAddSelectedPromptColumns(ctx context.Context, db *gorm.DB) error { } return nil } + +// migrationAddOCRInputColumn adds the ocr_input column to the logs table. +func migrationAddOCRInputColumn(ctx context.Context, db *gorm.DB) error { + opts := *migrator.DefaultOptions + opts.UseTransaction = true + m := migrator.New(db, &opts, []*migrator.Migration{{ + ID: "logs_add_ocr_input_column", + Migrate: func(tx *gorm.DB) error { + tx = tx.WithContext(ctx) + mig := tx.Migrator() + if !mig.HasColumn(&Log{}, "ocr_input") { + if err := mig.AddColumn(&Log{}, "ocr_input"); err != nil { + return err + } + } + return nil + }, + Rollback: func(tx *gorm.DB) error { + tx = tx.WithContext(ctx) + mig := tx.Migrator() + if mig.HasColumn(&Log{}, "ocr_input") { + if err := mig.DropColumn(&Log{}, "ocr_input"); err != nil { + return err + } + } + return nil + }, + }}) + if err := m.Migrate(); err != nil { + return fmt.Errorf("error while adding ocr_input column: %s", err.Error()) + } + return nil +} diff --git a/framework/logstore/payload.go b/framework/logstore/payload.go index 7d2287b6a2..2efefae494 100644 --- a/framework/logstore/payload.go +++ b/framework/logstore/payload.go @@ -20,6 +20,8 @@ var payloadFields = []string{ "responses_output", "embedding_output", "rerank_output", + "ocr_input", + "ocr_output", "params", "tools", "tool_calls", @@ -58,6 +60,8 @@ func ExtractPayload(l *Log) map[string]string { m["responses_output"] = l.ResponsesOutput m["embedding_output"] = l.EmbeddingOutput m["rerank_output"] = l.RerankOutput + m["ocr_input"] = l.OCRInput + m["ocr_output"] = l.OCROutput m["params"] = l.Params m["tools"] = l.Tools m["tool_calls"] = l.ToolCalls @@ -100,6 +104,8 @@ func ClearPayload(l *Log) { l.ResponsesOutput = "" l.EmbeddingOutput = "" l.RerankOutput = "" + l.OCRInput = "" + l.OCROutput = "" l.Params = "" l.Tools = "" l.ToolCalls = "" @@ -134,6 +140,8 @@ func ClearPayload(l *Log) { l.ResponsesOutputParsed = nil l.EmbeddingOutputParsed = nil l.RerankOutputParsed = nil + l.OCRInputParsed = nil + l.OCROutputParsed = nil l.ParamsParsed = nil l.ToolsParsed = nil l.ToolCallsParsed = nil @@ -183,6 +191,12 @@ func MergePayloadFromJSON(l *Log, data []byte) error { if v, ok := m["rerank_output"]; ok && v != "" { l.RerankOutput = v } + if v, ok := m["ocr_input"]; ok && v != "" { + l.OCRInput = v + } + if v, ok := m["ocr_output"]; ok && v != "" { + l.OCROutput = v + } if v, ok := m["params"]; ok && v != "" { l.Params = v } @@ -504,6 +518,12 @@ func clearPayloadField(l *Log, name string) { case "rerank_output": l.RerankOutput = "" l.RerankOutputParsed = nil + case "ocr_input": + l.OCRInput = "" + l.OCRInputParsed = nil + case "ocr_output": + l.OCROutput = "" + l.OCROutputParsed = nil case "params": l.Params = "" l.ParamsParsed = nil diff --git a/framework/logstore/tables.go b/framework/logstore/tables.go index 1d7446597e..b72398ed3f 100644 --- a/framework/logstore/tables.go +++ b/framework/logstore/tables.go @@ -144,6 +144,7 @@ type Log struct { ToolCalls string `gorm:"type:text" json:"-"` // JSON serialized []schemas.ToolCall (For backward compatibility, tool calls are now in the content) SpeechInput string `gorm:"type:text" json:"-"` // JSON serialized *schemas.SpeechInput TranscriptionInput string `gorm:"type:text" json:"-"` // JSON serialized *schemas.TranscriptionInput + OCRInput string `gorm:"type:text" json:"-"` // JSON serialized *schemas.OCRDocument ImageGenerationInput string `gorm:"type:text" json:"-"` // JSON serialized *schemas.ImageGenerationInput ImageEditInput string `gorm:"type:text" json:"-"` // JSON serialized *schemas.ImageEditInput ImageVariationInput string `gorm:"type:text" json:"-"` // JSON serialized *schemas.ImageVariationInput @@ -200,6 +201,7 @@ type Log struct { ErrorDetailsParsed *schemas.BifrostError `gorm:"-" json:"error_details,omitempty"` SpeechInputParsed *schemas.SpeechInput `gorm:"-" json:"speech_input,omitempty"` TranscriptionInputParsed *schemas.TranscriptionInput `gorm:"-" json:"transcription_input,omitempty"` + OCRInputParsed *schemas.OCRDocument `gorm:"-" json:"ocr_input,omitempty"` ImageGenerationInputParsed *schemas.ImageGenerationInput `gorm:"-" json:"image_generation_input,omitempty"` ImageEditInputParsed *schemas.ImageEditInput `gorm:"-" json:"image_edit_input,omitempty"` ImageVariationInputParsed *schemas.ImageVariationInput `gorm:"-" json:"image_variation_input,omitempty"` @@ -337,6 +339,14 @@ func (l *Log) SerializeFields() error { } } + if l.OCRInputParsed != nil { + if data, err := sonic.Marshal(l.OCRInputParsed); err != nil { + return err + } else { + l.OCRInput = string(data) + } + } + if l.ImageGenerationInputParsed != nil { if data, err := sonic.Marshal(l.ImageGenerationInputParsed); err != nil { return err @@ -676,6 +686,12 @@ func (l *Log) DeserializeFields() error { } } + if l.OCRInput != "" { + if err := sonic.Unmarshal([]byte(l.OCRInput), &l.OCRInputParsed); err != nil { + l.OCRInputParsed = nil + } + } + if l.ImageGenerationInput != "" { if err := sonic.Unmarshal([]byte(l.ImageGenerationInput), &l.ImageGenerationInputParsed); err != nil { // Log error but don't fail the operation - initialize as nil diff --git a/plugins/logging/main.go b/plugins/logging/main.go index 19d385374a..0b7d0162a8 100644 --- a/plugins/logging/main.go +++ b/plugins/logging/main.go @@ -214,6 +214,7 @@ type InitialLogData struct { Params any SpeechInput *schemas.SpeechInput TranscriptionInput *schemas.TranscriptionInput + OCRInput *schemas.OCRDocument ImageGenerationInput *schemas.ImageGenerationInput ImageEditInput *schemas.ImageEditInput ImageVariationInput *schemas.ImageVariationInput @@ -489,6 +490,7 @@ func (p *LoggerPlugin) PreLLMHook(ctx *schemas.BifrostContext, req *schemas.Bifr initialData.Params = req.RerankRequest.Params case schemas.OCRRequest: initialData.Params = req.OCRRequest.Params + initialData.OCRInput = &req.OCRRequest.Document case schemas.SpeechRequest, schemas.SpeechStreamRequest: initialData.Params = req.SpeechRequest.Params initialData.SpeechInput = req.SpeechRequest.Input diff --git a/plugins/logging/operations.go b/plugins/logging/operations.go index a89e5afe09..9333abc112 100644 --- a/plugins/logging/operations.go +++ b/plugins/logging/operations.go @@ -43,6 +43,7 @@ func (p *LoggerPlugin) insertInitialLogEntry( ToolsParsed: data.Tools, SpeechInputParsed: data.SpeechInput, TranscriptionInputParsed: data.TranscriptionInput, + OCRInputParsed: data.OCRInput, ImageGenerationInputParsed: data.ImageGenerationInput, ImageEditInputParsed: data.ImageEditInput, ImageVariationInputParsed: data.ImageVariationInput, diff --git a/plugins/logging/utils.go b/plugins/logging/utils.go index bc074f14b5..4d1abbbde5 100644 --- a/plugins/logging/utils.go +++ b/plugins/logging/utils.go @@ -545,29 +545,6 @@ func (p *LoggerPlugin) extractInputHistory(request *schemas.BifrostRequest) ([]s }, }, []schemas.ResponsesMessage{} } - if request.OCRRequest != nil { - var docRef string - if request.OCRRequest.Document.DocumentURL != nil { - docRef = *request.OCRRequest.Document.DocumentURL - } else if request.OCRRequest.Document.ImageURL != nil { - docRef = *request.OCRRequest.Document.ImageURL - } - // Strip query parameters to avoid logging sensitive tokens (e.g., pre-signed URLs) - if idx := strings.Index(docRef, "?"); idx != -1 { - docRef = docRef[:idx] - } - if docRef == "" { - return []schemas.ChatMessage{}, []schemas.ResponsesMessage{} - } - return []schemas.ChatMessage{ - { - Role: schemas.ChatMessageRoleUser, - Content: &schemas.ChatMessageContent{ - ContentStr: &docRef, - }, - }, - }, []schemas.ResponsesMessage{} - } if request.CountTokensRequest != nil && len(request.CountTokensRequest.Input) > 0 { return []schemas.ChatMessage{}, request.CountTokensRequest.Input } diff --git a/plugins/logging/writer.go b/plugins/logging/writer.go index 93a20d2827..fe34bc4a8f 100644 --- a/plugins/logging/writer.go +++ b/plugins/logging/writer.go @@ -316,9 +316,11 @@ func buildCompleteLogEntryFromPending(pending *PendingLogData) *logstore.Log { ToolsParsed: pending.InitialData.Tools, SpeechInputParsed: pending.InitialData.SpeechInput, TranscriptionInputParsed: pending.InitialData.TranscriptionInput, + OCRInputParsed: pending.InitialData.OCRInput, ImageGenerationInputParsed: pending.InitialData.ImageGenerationInput, ImageEditInputParsed: pending.InitialData.ImageEditInput, ImageVariationInputParsed: pending.InitialData.ImageVariationInput, + VideoGenerationInputParsed: pending.InitialData.VideoGenerationInput, PassthroughRequestBody: pending.InitialData.PassthroughRequestBody, } if pending.ParentRequestID != "" { diff --git a/transports/bifrost-http/handlers/mcpserver.go b/transports/bifrost-http/handlers/mcpserver.go index 9db9d674ff..b03488ea6f 100644 --- a/transports/bifrost-http/handlers/mcpserver.go +++ b/transports/bifrost-http/handlers/mcpserver.go @@ -95,11 +95,8 @@ func injectMCPSessionIdentity(bifrostCtx *schemas.BifrostContext, session *table if session.AccessToken != "" { bifrostCtx.SetValue(schemas.BifrostContextKeyMCPUserSession, session.AccessToken) } - if session.VirtualKeyID != nil && *session.VirtualKeyID != "" { - bifrostCtx.SetValue(schemas.BifrostContextKeyGovernanceVirtualKeyID, *session.VirtualKeyID) - if session.VirtualKey != nil && session.VirtualKey.Name != "" { - bifrostCtx.SetValue(schemas.BifrostContextKeyGovernanceVirtualKeyName, session.VirtualKey.Name) - } + if session.VirtualKeyID != nil && *session.VirtualKeyID != "" && session.VirtualKey != nil && session.VirtualKey.Value != "" { + bifrostCtx.SetValue(schemas.BifrostContextKeyVirtualKey, session.VirtualKey.Value) } if session.UserID != nil && *session.UserID != "" { bifrostCtx.SetValue(schemas.BifrostContextKeyUserID, *session.UserID) diff --git a/ui/app/_fallbacks/enterprise/components/mcp-tool-groups/mcpToolGroups.tsx b/ui/app/_fallbacks/enterprise/components/mcp-tool-groups/mcpToolGroups.tsx index 05a95013dd..8e6dfc089d 100644 --- a/ui/app/_fallbacks/enterprise/components/mcp-tool-groups/mcpToolGroups.tsx +++ b/ui/app/_fallbacks/enterprise/components/mcp-tool-groups/mcpToolGroups.tsx @@ -1,16 +1,15 @@ -import { CardDescription, CardHeader, CardTitle } from "@/components/ui/card"; import { ToolCase } from "lucide-react"; import ContactUsView from "../views/contactUsView"; export default function MCPToolGroups() { return ( <> - - -

MCP tool groups

-
- Configure tool groups for MCP servers to organize and govern tools. -
+
+
+

MCP tool groups

+

Configure tool groups for MCP servers to organize and govern tools.

+
+
+
); diff --git a/ui/app/workspace/config/views/mcpView.tsx b/ui/app/workspace/config/views/mcpView.tsx index e246c8aa39..551555a22b 100644 --- a/ui/app/workspace/config/views/mcpView.tsx +++ b/ui/app/workspace/config/views/mcpView.tsx @@ -147,7 +147,7 @@ export default function MCPView() { return (
@@ -158,7 +158,7 @@ export default function MCPView() {
{/* Max Agent Depth */} -
+
{/* Tool Execution Timeout */} -
+