| package plugins |
|
|
| import ( |
| "context" |
| "fmt" |
| "plugin" |
| "strings" |
|
|
| "github.com/maximhq/bifrost/core/schemas" |
| ) |
|
|
| |
| type SharedObjectPluginLoader struct{} |
|
|
| func openPlugin(dp *DynamicPlugin) (*plugin.Plugin, error) { |
| |
| if strings.HasPrefix(dp.Path, "http") { |
| |
| tempPath, err := DownloadPlugin(dp.Path, ".so") |
| if err != nil { |
| return nil, err |
| } |
| dp.Path = tempPath |
| } |
| pluginObj, err := plugin.Open(dp.Path) |
| if err != nil { |
| return nil, err |
| } |
| dp.plugin = pluginObj |
| return pluginObj, nil |
| } |
|
|
| |
| |
| |
| func (l *SharedObjectPluginLoader) LoadPlugin(path string, config any) (schemas.BasePlugin, error) { |
| dp := &DynamicPlugin{ |
| Path: path, |
| } |
|
|
| pluginObj, err := openPlugin(dp) |
| if err != nil { |
| return nil, err |
| } |
|
|
| |
| if initSym, err := pluginObj.Lookup("Init"); err == nil { |
| if initFunc, ok := initSym.(func(config any) error); ok { |
| if err := initFunc(config); err != nil { |
| return nil, fmt.Errorf("plugin Init failed: %w", err) |
| } |
| } else { |
| return nil, fmt.Errorf("failed to cast Init to func(config any) error") |
| } |
| } |
|
|
| |
| getNameSym, err := pluginObj.Lookup("GetName") |
| if err != nil { |
| return nil, fmt.Errorf("required symbol GetName not found: %w", err) |
| } |
| var ok bool |
| if dp.getName, ok = getNameSym.(func() string); !ok { |
| return nil, fmt.Errorf("failed to cast GetName to func() string\nSee docs for more information: https://docs.getbifrost.ai/plugins/writing-go-plugin") |
| } |
|
|
| |
| cleanupSym, err := pluginObj.Lookup("Cleanup") |
| if err != nil { |
| return nil, fmt.Errorf("required symbol Cleanup not found: %w", err) |
| } |
| if dp.cleanup, ok = cleanupSym.(func() error); !ok { |
| return nil, fmt.Errorf("failed to cast Cleanup to func() error\nSee docs for more information: https://docs.getbifrost.ai/plugins/writing-go-plugin") |
| } |
|
|
| |
| if sym, err := pluginObj.Lookup("HTTPTransportPreHook"); err == nil { |
| if dp.httpTransportPreHook, ok = sym.(func(ctx *schemas.BifrostContext, req *schemas.HTTPRequest) (*schemas.HTTPResponse, error)); !ok { |
| return nil, fmt.Errorf("failed to cast HTTPTransportPreHook to expected signature") |
| } |
| } |
|
|
| |
| if sym, err := pluginObj.Lookup("HTTPTransportPostHook"); err == nil { |
| if dp.httpTransportPostHook, ok = sym.(func(ctx *schemas.BifrostContext, req *schemas.HTTPRequest, resp *schemas.HTTPResponse) error); !ok { |
| return nil, fmt.Errorf("failed to cast HTTPTransportPostHook to expected signature") |
| } |
| } |
|
|
| |
| if sym, err := pluginObj.Lookup("HTTPTransportStreamChunkHook"); err == nil { |
| if dp.httpTransportStreamChunkHook, ok = sym.(func(ctx *schemas.BifrostContext, req *schemas.HTTPRequest, chunk *schemas.BifrostStreamChunk) (*schemas.BifrostStreamChunk, error)); !ok { |
| return nil, fmt.Errorf("failed to cast HTTPTransportStreamChunkHook to expected signature") |
| } |
| } |
|
|
| |
| if sym, err := pluginObj.Lookup("PreLLMHook"); err == nil { |
| if dp.preLLMHook, ok = sym.(func(ctx *schemas.BifrostContext, req *schemas.BifrostRequest) (*schemas.BifrostRequest, *schemas.LLMPluginShortCircuit, error)); !ok { |
| return nil, fmt.Errorf("failed to cast PreLLMHook to expected signature") |
| } |
| } else if sym, err := pluginObj.Lookup("PreHook"); err == nil { |
| |
| if dp.preLLMHook, ok = sym.(func(ctx *schemas.BifrostContext, req *schemas.BifrostRequest) (*schemas.BifrostRequest, *schemas.LLMPluginShortCircuit, error)); !ok { |
| return nil, fmt.Errorf("failed to cast PreHook to expected signature (legacy backward compatibility)") |
| } |
| } |
|
|
| |
| if sym, err := pluginObj.Lookup("PostLLMHook"); err == nil { |
| if dp.postLLMHook, ok = sym.(func(ctx *schemas.BifrostContext, resp *schemas.BifrostResponse, bifrostErr *schemas.BifrostError) (*schemas.BifrostResponse, *schemas.BifrostError, error)); !ok { |
| return nil, fmt.Errorf("failed to cast PostLLMHook to expected signature") |
| } |
| } else if sym, err := pluginObj.Lookup("PostHook"); err == nil { |
| |
| if dp.postLLMHook, ok = sym.(func(ctx *schemas.BifrostContext, resp *schemas.BifrostResponse, bifrostErr *schemas.BifrostError) (*schemas.BifrostResponse, *schemas.BifrostError, error)); !ok { |
| return nil, fmt.Errorf("failed to cast PostHook to expected signature (legacy backward compatibility)") |
| } |
| } |
|
|
| |
| if sym, err := pluginObj.Lookup("PreMCPHook"); err == nil { |
| if dp.preMCPHook, ok = sym.(func(ctx *schemas.BifrostContext, req *schemas.BifrostMCPRequest) (*schemas.BifrostMCPRequest, *schemas.MCPPluginShortCircuit, error)); !ok { |
| return nil, fmt.Errorf("failed to cast PreMCPHook to expected signature") |
| } |
| } |
|
|
| |
| if sym, err := pluginObj.Lookup("PostMCPHook"); err == nil { |
| if dp.postMCPHook, ok = sym.(func(ctx *schemas.BifrostContext, resp *schemas.BifrostMCPResponse, bifrostErr *schemas.BifrostError) (*schemas.BifrostMCPResponse, *schemas.BifrostError, error)); !ok { |
| return nil, fmt.Errorf("failed to cast PostMCPHook to expected signature") |
| } |
| } |
|
|
| |
| if sym, err := pluginObj.Lookup("Inject"); err == nil { |
| if dp.inject, ok = sym.(func(ctx context.Context, trace *schemas.Trace) error); !ok { |
| return nil, fmt.Errorf("failed to cast Inject to expected signature") |
| } |
| } |
|
|
| return dp, nil |
| } |
|
|
| |
| |
| |
| |
| func (l *SharedObjectPluginLoader) VerifyBasePlugin(path string) (string, error) { |
| dp := &DynamicPlugin{ |
| Path: path, |
| } |
| pluginObj, err := openPlugin(dp) |
| if err != nil { |
| return "", err |
| } |
| |
| getNameSym, err := pluginObj.Lookup("GetName") |
| if err != nil { |
| return "", fmt.Errorf("required symbol GetName not found: %w", err) |
| } |
| var ok bool |
| if dp.getName, ok = getNameSym.(func() string); !ok { |
| return "", fmt.Errorf("failed to cast GetName to func() string") |
| } |
| |
| cleanupSym, err := pluginObj.Lookup("Cleanup") |
| if err != nil { |
| return "", fmt.Errorf("required symbol Cleanup not found: %w", err) |
| } |
| if dp.cleanup, ok = cleanupSym.(func() error); !ok { |
| return "", fmt.Errorf("failed to cast Cleanup to func() error") |
| } |
| return dp.getName(), nil |
| } |
|
|