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,12 @@
.PHONY: build clean
build:
@echo "Building Multi-Interface plugin..."
@mkdir -p build
@go build -buildmode=plugin -o build/multi-interface.so main.go
@echo "Plugin built successfully: build/multi-interface.so"
clean:
@echo "Cleaning build directory..."
@rm -rf build
@echo "Clean complete"

View File

@@ -0,0 +1,179 @@
# Multi-Interface Plugin Example
This example demonstrates a plugin that implements **all plugin interfaces**:
- `HTTPTransportPlugin`
- `LLMPlugin`
- `MCPPlugin`
- `ObservabilityPlugin`
## Features
### HTTPTransportPlugin
- Tracks request count across all requests
- Adds request number header
- Calculates HTTP request duration
- Stores HTTP metadata in context for other hooks
### LLMPlugin
- Accesses HTTP context metadata
- Adds dynamic system prompts
- Tracks LLM call duration
- Logs request/response details
### MCPPlugin
- Accesses HTTP context metadata
- Logs all MCP tool/resource calls
- Tracks MCP call duration
- Implements governance for MCP calls
### ObservabilityPlugin
- Receives completed traces asynchronously
- Formats traces as JSON
- Ready for integration with OTEL, Datadog, Jaeger, etc.
- Demonstrates end-to-end request tracking
## Context Flow
This plugin demonstrates how context flows through different hooks:
1. **HTTPTransportPreHook** → Stores HTTP metadata
2. **PreLLMHook/PreMCPHook** → Accesses HTTP metadata, stores LLM/MCP metadata
3. **PostLLMHook/PostMCPHook** → Accesses stored timing data
4. **HTTPTransportPostHook** → Adds final headers
5. **Inject** → Receives complete trace asynchronously
## Use Cases
- **Full-stack observability** - Track requests from HTTP to LLM/MCP and back
- **Unified governance** - Apply policies at multiple layers
- **Performance monitoring** - Measure duration at each layer
- **Audit trails** - Complete request/response logging
- **Custom analytics** - Correlate HTTP, LLM, and MCP metrics
## Building
```bash
make build
```
This creates `build/multi-interface.so`
## Configuration
Add to your Bifrost config:
```json
{
"plugins": [
{
"path": "/path/to/multi-interface.so",
"name": "multi-interface",
"display_name": "Full-Stack Observability",
"enabled": true,
"type": "auto",
"config": {
"enable_http_hooks": true,
"enable_llm_hooks": true,
"enable_mcp_hooks": true,
"enable_observability": true,
"enable_logging": true,
"track_requests": true,
"inject_uptime": true,
"custom_header_prefix": "X-Multi-Plugin"
}
}
]
}
```
**Note:**
- `name` is the system identifier (from `GetName()`) and is **not editable**
- `display_name` is shown in the UI and is **editable** by users
### Configuration Options
| Option | Type | Default | Description |
|--------|------|---------|-------------|
| `enable_http_hooks` | boolean | `true` | Enable HTTP transport layer hooks |
| `enable_llm_hooks` | boolean | `true` | Enable LLM request/response hooks |
| `enable_mcp_hooks` | boolean | `true` | Enable MCP request/response hooks |
| `enable_observability` | boolean | `true` | Enable observability/trace injection |
| `enable_logging` | boolean | `true` | Enable detailed logging |
| `track_requests` | boolean | `true` | Track and count requests |
| `inject_uptime` | boolean | `true` | Inject server uptime in LLM system messages |
| `custom_header_prefix` | string | `"X-Multi-Plugin"` | Custom prefix for HTTP response headers |
### Example Configurations
**LLM-only mode:**
```json
{
"config": {
"enable_http_hooks": false,
"enable_llm_hooks": true,
"enable_mcp_hooks": false,
"enable_observability": false
}
}
```
**Observability-focused:**
```json
{
"config": {
"enable_http_hooks": true,
"enable_llm_hooks": true,
"enable_mcp_hooks": true,
"enable_observability": true,
"enable_logging": false,
"track_requests": true
}
}
```
**Minimal overhead:**
```json
{
"config": {
"enable_logging": false,
"track_requests": false,
"inject_uptime": false
}
}
```
**Custom headers:**
```json
{
"config": {
"custom_header_prefix": "X-Custom-Plugin"
}
}
```
## Hook Execution Order
For a typical LLM request:
1. `HTTPTransportPreHook` (HTTP layer entry)
2. `PreLLMHook` (Before LLM provider)
3. *LLM Provider Call*
4. `PostLLMHook` (After LLM provider)
5. `HTTPTransportPostHook` (HTTP layer exit)
6. `Inject` (Asynchronous trace delivery)
For an MCP request:
1. `HTTPTransportPreHook` (HTTP layer entry)
2. `PreMCPHook` (Before MCP server)
3. *MCP Server Call*
4. `PostMCPHook` (After MCP server)
5. `HTTPTransportPostHook` (HTTP layer exit)
6. `Inject` (Asynchronous trace delivery)
## Notes
- This plugin tracks state across requests (request count, start time)
- Context metadata flows from HTTP → LLM/MCP hooks
- `Inject` is called asynchronously after response is sent
- Perfect template for building comprehensive observability solutions

View File

@@ -0,0 +1,32 @@
module github.com/maximhq/bifrost/examples/plugins/multi-interface
go 1.26.2
replace github.com/maximhq/bifrost/core => ../../../core
require github.com/maximhq/bifrost/core v0.0.0-00010101000000-000000000000
require (
github.com/andybalholm/brotli v1.2.0 // 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/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/mailru/easyjson v0.9.1 // indirect
github.com/mark3labs/mcp-go v0.43.2 // indirect
github.com/spf13/cast v1.10.0 // 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
golang.org/x/arch v0.23.0 // indirect
golang.org/x/sys v0.42.0 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
)

View File

@@ -0,0 +1,82 @@
github.com/andybalholm/brotli v1.2.0 h1:ukwgCxwYrmACq68yiUqwIWnGY0cTPox/M94sVwToPjQ=
github.com/andybalholm/brotli v1.2.0/go.mod h1:rzTDkvFWvIrjDXZHkuS16NPggd91W3kUSvPlQ1pLaKY=
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.1 h1:2PnMjfWD7wBILjqQbt530v576A/cAbQvEW9gGIpYMUs=
github.com/buger/jsonparser v1.1.1/go.mod h1:6RYKKt7H4d4+iWqouImQ9R2FZql3VbhNgx27UK13J/0=
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.14.2 h1:k1twIoe97C1DtYUo+fZQy865IuHia4PR5RPiuGPPIIE=
github.com/bytedance/sonic v1.14.2/go.mod h1:T80iDELeHiHKSc0C9tubFygiuXoGzrkjKzX2quAx980=
github.com/bytedance/sonic v1.15.0/go.mod h1:tFkWrPz0/CUCLEF4ri4UkHekCIcdnkqXw9VduqpJh0k=
github.com/bytedance/sonic/loader v0.4.0 h1:olZ7lEqcxtZygCK9EKYKADnpQoYkRQxaeY2NYzevs+o=
github.com/bytedance/sonic/loader v0.4.0/go.mod h1:AR4NYCk5DdzZizZ5djGqQ92eEhCCcdf5x77udYiSJRo=
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/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/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8=
github.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0=
github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38=
github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
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/invopop/jsonschema v0.13.0 h1:KvpoAJWEjR3uD9Kbm2HWJmqsEaHt8lBUpd0qHcIi21E=
github.com/invopop/jsonschema v0.13.0/go.mod h1:ffZ5Km5SWWRAIN6wbDXItl95euhFz2uON45H2qjYt+0=
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/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/maximhq/bifrost/core v1.3.11 h1:dMfPxS83BGwjeaK6BUdVHtoJu55dVRcqw0fB+qkqPYE=
github.com/maximhq/bifrost/core v1.3.11/go.mod h1:abKQRnJQPZz8/UMxCcbuNHEyq19Db+IX4KlGJdlLY8E=
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/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/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=
golang.org/x/arch v0.23.0 h1:lKF64A2jF6Zd8L0knGltUnegD62JMFBiCPBmQpToHhg=
golang.org/x/arch v0.23.0/go.mod h1:dNHoOeKiyja7GTvF9NJS1l3Z2yntpQNzgrjh1cU103A=
golang.org/x/sys v0.39.0 h1:CvCKL8MeisomCi6qNZ+wbb0DN9E5AATixKsvNtMoMFk=
golang.org/x/sys v0.39.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
golang.org/x/sys v0.42.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
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=

View File

@@ -0,0 +1,314 @@
package main
import (
"context"
"encoding/json"
"fmt"
"time"
"github.com/maximhq/bifrost/core/schemas"
)
// Plugin configuration
type PluginConfig struct {
EnableHTTPHooks bool `json:"enable_http_hooks"` // Enable HTTP transport hooks
EnableLLMHooks bool `json:"enable_llm_hooks"` // Enable LLM hooks
EnableMCPHooks bool `json:"enable_mcp_hooks"` // Enable MCP hooks
EnableObservability bool `json:"enable_observability"` // Enable observability/trace injection
EnableLogging bool `json:"enable_logging"` // Enable detailed logging
TrackRequests bool `json:"track_requests"` // Track request count
InjectUptime bool `json:"inject_uptime"` // Inject server uptime in system messages
CustomHeaderPrefix string `json:"custom_header_prefix"` // Custom prefix for plugin headers
}
var (
// Default configuration
pluginConfig = &PluginConfig{
EnableHTTPHooks: true,
EnableLLMHooks: true,
EnableMCPHooks: true,
EnableObservability: true,
EnableLogging: true,
TrackRequests: true,
InjectUptime: true,
CustomHeaderPrefix: "X-Multi-Plugin",
}
// Plugin state
requestCount int64
startTime time.Time
)
// Init is called when the plugin is loaded (optional)
func Init(config any) error {
fmt.Println("[Multi-Interface Plugin] Init called")
startTime = time.Now()
// Parse configuration
if configMap, ok := config.(map[string]interface{}); ok {
if enableHTTP, ok := configMap["enable_http_hooks"].(bool); ok {
pluginConfig.EnableHTTPHooks = enableHTTP
}
if enableLLM, ok := configMap["enable_llm_hooks"].(bool); ok {
pluginConfig.EnableLLMHooks = enableLLM
}
if enableMCP, ok := configMap["enable_mcp_hooks"].(bool); ok {
pluginConfig.EnableMCPHooks = enableMCP
}
if enableObs, ok := configMap["enable_observability"].(bool); ok {
pluginConfig.EnableObservability = enableObs
}
if enableLogging, ok := configMap["enable_logging"].(bool); ok {
pluginConfig.EnableLogging = enableLogging
}
if trackReq, ok := configMap["track_requests"].(bool); ok {
pluginConfig.TrackRequests = trackReq
}
if injectUptime, ok := configMap["inject_uptime"].(bool); ok {
pluginConfig.InjectUptime = injectUptime
}
if headerPrefix, ok := configMap["custom_header_prefix"].(string); ok {
pluginConfig.CustomHeaderPrefix = headerPrefix
}
}
fmt.Printf("[Multi-Interface Plugin] Configuration loaded:\n")
fmt.Printf(" HTTP Hooks: %v\n", pluginConfig.EnableHTTPHooks)
fmt.Printf(" LLM Hooks: %v\n", pluginConfig.EnableLLMHooks)
fmt.Printf(" MCP Hooks: %v\n", pluginConfig.EnableMCPHooks)
fmt.Printf(" Observability: %v\n", pluginConfig.EnableObservability)
fmt.Printf(" Request Tracking: %v\n", pluginConfig.TrackRequests)
return nil
}
// GetName returns the name of the plugin (required)
// This is the system identifier - not editable by users
// Users can set a custom display_name in the config for the UI
func GetName() string {
return "multi-interface"
}
// ============================================================================
// HTTPTransportPlugin Interface
// ============================================================================
// HTTPTransportPreHook handles HTTP-layer request interception
func HTTPTransportPreHook(ctx *schemas.BifrostContext, req *schemas.HTTPRequest) (*schemas.HTTPResponse, error) {
if !pluginConfig.EnableHTTPHooks {
return nil, nil
}
if pluginConfig.EnableLogging {
fmt.Println("[Multi-Interface Plugin] HTTPTransportPreHook called")
}
// Add request tracking (configurable)
if pluginConfig.TrackRequests {
requestCount++
req.Headers[fmt.Sprintf("%s-Request-Number", pluginConfig.CustomHeaderPrefix)] = fmt.Sprintf("%d", requestCount)
}
// Store HTTP metadata in context for later hooks
ctx.SetValue(schemas.BifrostContextKey("multi-http-request-time"), time.Now())
ctx.SetValue(schemas.BifrostContextKey("multi-http-path"), req.Path)
return nil, nil
}
// HTTPTransportPostHook handles HTTP-layer response interception
func HTTPTransportPostHook(ctx *schemas.BifrostContext, req *schemas.HTTPRequest, resp *schemas.HTTPResponse) error {
if !pluginConfig.EnableHTTPHooks {
return nil
}
if pluginConfig.EnableLogging {
fmt.Println("[Multi-Interface Plugin] HTTPTransportPostHook called")
}
// Calculate HTTP duration
if startTime, ok := ctx.Value(schemas.BifrostContextKey("multi-http-request-time")).(time.Time); ok {
duration := time.Since(startTime)
resp.Headers[fmt.Sprintf("%s-Duration-Ms", pluginConfig.CustomHeaderPrefix)] = fmt.Sprintf("%d", duration.Milliseconds())
}
// Add plugin info header
var interfaces []string
if pluginConfig.EnableHTTPHooks {
interfaces = append(interfaces, "http")
}
if pluginConfig.EnableLLMHooks {
interfaces = append(interfaces, "llm")
}
if pluginConfig.EnableMCPHooks {
interfaces = append(interfaces, "mcp")
}
if pluginConfig.EnableObservability {
interfaces = append(interfaces, "observability")
}
resp.Headers[fmt.Sprintf("%s-Interfaces", pluginConfig.CustomHeaderPrefix)] = fmt.Sprintf("%v", interfaces)
return nil
}
// ============================================================================
// LLMPlugin Interface
// ============================================================================
// PreLLMHook is called before the LLM provider is invoked
func PreLLMHook(ctx *schemas.BifrostContext, req *schemas.BifrostRequest) (*schemas.BifrostRequest, *schemas.LLMPluginShortCircuit, error) {
if !pluginConfig.EnableLLMHooks {
return req, nil, nil
}
if pluginConfig.EnableLogging {
fmt.Println("[Multi-Interface Plugin] PreLLMHook called")
httpPath := ctx.Value(schemas.BifrostContextKey("multi-http-path"))
fmt.Printf("[Multi-Interface Plugin] Processing LLM request from path: %v\n", httpPath)
}
// Store LLM metadata
ctx.SetValue(schemas.BifrostContextKey("multi-llm-start-time"), time.Now())
// Example: Add system prompt with uptime (configurable)
if pluginConfig.InjectUptime && req.ChatRequest != nil && req.ChatRequest.Input != nil {
var content string
if pluginConfig.TrackRequests {
content = fmt.Sprintf("Processing request #%d. Server uptime: %v", requestCount, time.Since(startTime))
} else {
content = fmt.Sprintf("Server uptime: %v", time.Since(startTime))
}
systemMsg := schemas.ChatMessage{
Role: "system",
Content: &schemas.ChatMessageContent{ContentStr: &content},
}
req.ChatRequest.Input = append([]schemas.ChatMessage{systemMsg}, req.ChatRequest.Input...)
}
return req, nil, nil
}
// PostLLMHook is called after the LLM provider responds
func PostLLMHook(ctx *schemas.BifrostContext, resp *schemas.BifrostResponse, bifrostErr *schemas.BifrostError) (*schemas.BifrostResponse, *schemas.BifrostError, error) {
if !pluginConfig.EnableLLMHooks {
return resp, bifrostErr, nil
}
if pluginConfig.EnableLogging {
fmt.Println("[Multi-Interface Plugin] PostLLMHook called")
}
// Calculate LLM duration
if startTime, ok := ctx.Value(schemas.BifrostContextKey("multi-llm-start-time")).(time.Time); ok {
duration := time.Since(startTime)
if pluginConfig.EnableLogging {
fmt.Printf("[Multi-Interface Plugin] LLM call took: %v\n", duration)
}
// Store for observability
ctx.SetValue(schemas.BifrostContextKey("multi-llm-duration"), duration)
}
return resp, bifrostErr, nil
}
// ============================================================================
// MCPPlugin Interface
// ============================================================================
// PreMCPHook is called before MCP tool/resource calls are executed
func PreMCPHook(ctx *schemas.BifrostContext, req *schemas.BifrostMCPRequest) (*schemas.BifrostMCPRequest, *schemas.MCPPluginShortCircuit, error) {
if !pluginConfig.EnableMCPHooks {
return req, nil, nil
}
if pluginConfig.EnableLogging {
fmt.Println("[Multi-Interface Plugin] PreMCPHook called")
httpPath := ctx.Value(schemas.BifrostContextKey("multi-http-path"))
fmt.Printf("[Multi-Interface Plugin] Processing MCP request from path: %v\n", httpPath)
}
// Store MCP metadata
ctx.SetValue(schemas.BifrostContextKey("multi-mcp-start-time"), time.Now())
ctx.SetValue(schemas.BifrostContextKey("multi-mcp-type"), req.RequestType)
// Example: Log the MCP call
if pluginConfig.EnableLogging && req.ChatAssistantMessageToolCall != nil && req.ChatAssistantMessageToolCall.Function.Name != nil {
fmt.Printf("[Multi-Interface Plugin] MCP tool call: %s\n", *req.ChatAssistantMessageToolCall.Function.Name)
}
return req, nil, nil
}
// PostMCPHook is called after MCP tool/resource calls complete
func PostMCPHook(ctx *schemas.BifrostContext, resp *schemas.BifrostMCPResponse, bifrostErr *schemas.BifrostError) (*schemas.BifrostMCPResponse, *schemas.BifrostError, error) {
if !pluginConfig.EnableMCPHooks {
return resp, bifrostErr, nil
}
if pluginConfig.EnableLogging {
fmt.Println("[Multi-Interface Plugin] PostMCPHook called")
}
// Calculate MCP duration
if startTime, ok := ctx.Value(schemas.BifrostContextKey("multi-mcp-start-time")).(time.Time); ok {
duration := time.Since(startTime)
if pluginConfig.EnableLogging {
fmt.Printf("[Multi-Interface Plugin] MCP call took: %v\n", duration)
}
// Store for observability
ctx.SetValue(schemas.BifrostContextKey("multi-mcp-duration"), duration)
}
return resp, bifrostErr, nil
}
// ============================================================================
// ObservabilityPlugin Interface
// ============================================================================
// Inject receives completed traces for forwarding to observability backends
func Inject(ctx context.Context, trace *schemas.Trace) error {
if !pluginConfig.EnableObservability {
return nil
}
if pluginConfig.EnableLogging {
fmt.Println("[Multi-Interface Plugin] Inject called - sending trace to observability backend")
}
// Example: Format trace as JSON
traceJSON, err := json.MarshalIndent(trace, "", " ")
if err != nil {
fmt.Printf("[Multi-Interface Plugin] Failed to marshal trace: %v\n", err)
return err
}
// Example: Log the trace (in production, send to OTEL, Datadog, etc.)
if pluginConfig.EnableLogging {
fmt.Printf("[Multi-Interface Plugin] Trace data:\n%s\n", string(traceJSON))
}
// In production, you would send this to your observability backend here
// Example: sendToDatadog(traceJSON)
// Example: sendToOTEL(trace)
return nil
}
// ============================================================================
// Cleanup
// ============================================================================
// Cleanup is called when the plugin is unloaded (required)
func Cleanup() error {
uptime := time.Since(startTime)
if pluginConfig.TrackRequests {
fmt.Printf("[Multi-Interface Plugin] Cleanup called - processed %d requests over %v\n",
requestCount, uptime)
} else {
fmt.Printf("[Multi-Interface Plugin] Cleanup called - uptime: %v\n", uptime)
}
return nil
}