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 */}
-
+