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,64 @@
package nebius
import (
"strings"
providerUtils "github.com/maximhq/bifrost/core/providers/utils"
"github.com/maximhq/bifrost/core/schemas"
"github.com/valyala/fasthttp"
)
// parseNebiusImageError parses Nebius error responses
func parseNebiusImageError(resp *fasthttp.Response) *schemas.BifrostError {
var nebiusErr NebiusError
bifrostErr := providerUtils.HandleProviderAPIError(resp, &nebiusErr)
if bifrostErr.Error == nil {
bifrostErr.Error = &schemas.ErrorField{}
}
// Extract error message
var message string
if nebiusErr.Detail != nil {
if nebiusErr.Detail.Message != nil {
message = *nebiusErr.Detail.Message
}
if len(nebiusErr.Detail.ValidationErrors) > 0 {
var messages []string
var locations []string
if message != "" {
messages = append(messages, message)
}
for _, detail := range nebiusErr.Detail.ValidationErrors {
if detail.Msg != "" {
messages = append(messages, detail.Msg)
}
if len(detail.Loc) > 0 {
locations = append(locations, strings.Join(detail.Loc, "."))
}
}
if len(messages) > 0 {
message = strings.Join(messages, "; ")
}
if len(locations) > 0 {
locationStr := strings.Join(locations, ", ")
if message == "" {
message = "[" + locationStr + "]"
} else {
message = message + " [" + locationStr + "]"
}
}
}
}
// Use the extracted message if available
if message != "" {
bifrostErr.Error.Message = message
}
return bifrostErr
}

View File

@@ -0,0 +1,111 @@
package nebius
import (
"fmt"
"strconv"
"strings"
schemas "github.com/maximhq/bifrost/core/schemas"
)
// ToNebiusImageGenerationRequest converts a bifrost image generation request to nebius format.
func (provider *NebiusProvider) ToNebiusImageGenerationRequest(bifrostReq *schemas.BifrostImageGenerationRequest) (*NebiusImageGenerationRequest, error) {
if bifrostReq == nil || bifrostReq.Input == nil {
return nil, fmt.Errorf("bifrost request is nil or input is nil")
}
req := &NebiusImageGenerationRequest{
Model: &bifrostReq.Model,
Prompt: &bifrostReq.Input.Prompt,
}
if bifrostReq.Params != nil {
if bifrostReq.Params.ResponseFormat != nil {
req.ResponseFormat = bifrostReq.Params.ResponseFormat
}
if bifrostReq.Params.Size != nil && strings.TrimSpace(strings.ToLower(*bifrostReq.Params.Size)) != "auto" {
size := strings.Split(strings.TrimSpace(strings.ToLower(*bifrostReq.Params.Size)), "x")
if len(size) != 2 {
return nil, fmt.Errorf("invalid size format: expected 'WIDTHxHEIGHT', got %q", *bifrostReq.Params.Size)
}
width, err := strconv.Atoi(size[0])
if err != nil {
return nil, fmt.Errorf("invalid width in size %q: %w", *bifrostReq.Params.Size, err)
}
height, err := strconv.Atoi(size[1])
if err != nil {
return nil, fmt.Errorf("invalid height in size %q: %w", *bifrostReq.Params.Size, err)
}
req.Width = &width
req.Height = &height
}
if bifrostReq.Params.OutputFormat != nil {
req.ResponseExtension = bifrostReq.Params.OutputFormat
}
if req.ResponseExtension != nil && strings.ToLower(*req.ResponseExtension) == "jpeg" {
req.ResponseExtension = schemas.Ptr("jpg")
}
if bifrostReq.Params.Seed != nil {
req.Seed = bifrostReq.Params.Seed
}
if bifrostReq.Params.NegativePrompt != nil {
req.NegativePrompt = bifrostReq.Params.NegativePrompt
}
if bifrostReq.Params.NumInferenceSteps != nil {
req.NumInferenceSteps = bifrostReq.Params.NumInferenceSteps
}
// Handle extra params
if bifrostReq.Params.ExtraParams != nil {
req.ExtraParams = bifrostReq.Params.ExtraParams
// Map guidance_scale
if v, ok := schemas.SafeExtractIntPointer(bifrostReq.Params.ExtraParams["guidance_scale"]); ok {
delete(req.ExtraParams, "guidance_scale")
req.GuidanceScale = v
}
// Map loras in array format [{"url": "...", "scale": ...}]
if lorasValue, exists := bifrostReq.Params.ExtraParams["loras"]; exists && lorasValue != nil {
delete(req.ExtraParams, "loras")
// Check if lorasValue is an array of maps
if lorasArray, ok := lorasValue.([]interface{}); ok {
for _, item := range lorasArray {
if loraMap, ok := item.(map[string]interface{}); ok {
if url, ok := schemas.SafeExtractString(loraMap["url"]); ok {
if scale, ok := schemas.SafeExtractInt(loraMap["scale"]); ok {
req.Loras = append(req.Loras, NebiusLora{URL: url, Scale: scale})
}
}
}
}
}
}
}
}
return req, nil
}
// ToBifrostImageResponse converts a nebius image generation response to bifrost format.
func ToBifrostImageResponse(nebiusResponse *NebiusImageGenerationResponse) *schemas.BifrostImageGenerationResponse {
if nebiusResponse == nil {
return nil
}
data := make([]schemas.ImageData, len(nebiusResponse.Data))
for i, img := range nebiusResponse.Data {
data[i] = schemas.ImageData{
URL: img.URL,
B64JSON: img.B64JSON,
RevisedPrompt: img.RevisedPrompt,
Index: i,
}
}
return &schemas.BifrostImageGenerationResponse{
ID: nebiusResponse.Id,
Data: data,
}
}

View File

@@ -0,0 +1,535 @@
// Package nebius implements the Nebius LLM provider.
package nebius
import (
"context"
"fmt"
"net/http"
"net/url"
"strings"
"time"
"github.com/maximhq/bifrost/core/providers/openai"
providerUtils "github.com/maximhq/bifrost/core/providers/utils"
schemas "github.com/maximhq/bifrost/core/schemas"
"github.com/valyala/fasthttp"
)
// NebiusProvider implements the Provider interface for Nebius's API.
type NebiusProvider struct {
logger schemas.Logger // Logger for provider operations
client *fasthttp.Client // HTTP client for unary API requests (ReadTimeout bounds overall response)
streamingClient *fasthttp.Client // HTTP client for streaming API requests (no ReadTimeout; idle governed by NewIdleTimeoutReader)
networkConfig schemas.NetworkConfig // Network configuration including extra headers
sendBackRawRequest bool // Whether to include raw request in BifrostResponse
sendBackRawResponse bool // Whether to include raw response in BifrostResponse
}
// NewNebiusProvider creates a new Nebius provider instance.
// It initializes the HTTP client with the provided configuration and sets up response pools.
// The client is configured with timeouts, concurrency limits, and optional proxy settings.
func NewNebiusProvider(config *schemas.ProviderConfig, logger schemas.Logger) (*NebiusProvider, error) {
config.CheckAndSetDefaults()
requestTimeout := time.Second * time.Duration(config.NetworkConfig.DefaultRequestTimeoutInSeconds)
client := &fasthttp.Client{
ReadTimeout: requestTimeout,
WriteTimeout: requestTimeout,
MaxConnsPerHost: config.NetworkConfig.MaxConnsPerHost,
MaxIdleConnDuration: 30 * time.Second,
MaxConnWaitTimeout: requestTimeout,
MaxConnDuration: time.Second * time.Duration(schemas.DefaultMaxConnDurationInSeconds),
ConnPoolStrategy: fasthttp.FIFO,
}
// Configure proxy and retry policy
client = providerUtils.ConfigureProxy(client, config.ProxyConfig, logger)
client = providerUtils.ConfigureDialer(client)
client = providerUtils.ConfigureTLS(client, config.NetworkConfig, logger)
streamingClient := providerUtils.BuildStreamingClient(client)
// Set default BaseURL if not provided
if config.NetworkConfig.BaseURL == "" {
config.NetworkConfig.BaseURL = "https://api.tokenfactory.nebius.com"
}
config.NetworkConfig.BaseURL = strings.TrimRight(config.NetworkConfig.BaseURL, "/")
return &NebiusProvider{
logger: logger,
client: client,
streamingClient: streamingClient,
networkConfig: config.NetworkConfig,
sendBackRawRequest: config.SendBackRawRequest,
sendBackRawResponse: config.SendBackRawResponse,
}, nil
}
// GetProviderKey returns the provider identifier for Nebius.
func (provider *NebiusProvider) GetProviderKey() schemas.ModelProvider {
return schemas.Nebius
}
// ListModels performs a list models request to Nebius's API.
func (provider *NebiusProvider) ListModels(ctx *schemas.BifrostContext, keys []schemas.Key, request *schemas.BifrostListModelsRequest) (*schemas.BifrostListModelsResponse, *schemas.BifrostError) {
return openai.HandleOpenAIListModelsRequest(
ctx,
provider.client,
request,
provider.networkConfig.BaseURL+providerUtils.GetPathFromContext(ctx, "/v1/models"),
keys,
provider.networkConfig.ExtraHeaders,
provider.GetProviderKey(),
providerUtils.ShouldSendBackRawRequest(ctx, provider.sendBackRawRequest),
providerUtils.ShouldSendBackRawResponse(ctx, provider.sendBackRawResponse),
)
}
// TextCompletion performs a text completion request to Nebius's API.
// It formats the request, sends it to Nebius, and processes the response.
// Returns a BifrostResponse containing the completion results or an error if the request fails.
func (provider *NebiusProvider) TextCompletion(ctx *schemas.BifrostContext, key schemas.Key, request *schemas.BifrostTextCompletionRequest) (*schemas.BifrostTextCompletionResponse, *schemas.BifrostError) {
return openai.HandleOpenAITextCompletionRequest(
ctx,
provider.client,
provider.networkConfig.BaseURL+providerUtils.GetPathFromContext(ctx, "/v1/completions"),
request,
key,
provider.networkConfig.ExtraHeaders,
provider.GetProviderKey(),
providerUtils.ShouldSendBackRawRequest(ctx, provider.sendBackRawRequest),
providerUtils.ShouldSendBackRawResponse(ctx, provider.sendBackRawResponse),
nil,
nil,
provider.logger,
)
}
// TextCompletionStream performs a streaming text completion request to Nebius's API.
// It formats the request, sends it to Nebius, and processes the response.
// Returns a channel of BifrostStreamChunk objects or an error if the request fails.
func (provider *NebiusProvider) TextCompletionStream(ctx *schemas.BifrostContext, postHookRunner schemas.PostHookRunner, postHookSpanFinalizer func(context.Context), key schemas.Key, request *schemas.BifrostTextCompletionRequest) (chan *schemas.BifrostStreamChunk, *schemas.BifrostError) {
var authHeader map[string]string
if key.Value.GetValue() != "" {
authHeader = map[string]string{"Authorization": "Bearer " + key.Value.GetValue()}
}
// Use shared OpenAI-compatible streaming logic
return openai.HandleOpenAITextCompletionStreaming(
ctx,
provider.streamingClient,
provider.networkConfig.BaseURL+providerUtils.GetPathFromContext(ctx, "/v1/completions"),
request,
authHeader,
provider.networkConfig.ExtraHeaders,
providerUtils.ShouldSendBackRawRequest(ctx, provider.sendBackRawRequest),
providerUtils.ShouldSendBackRawResponse(ctx, provider.sendBackRawResponse),
provider.GetProviderKey(),
nil,
postHookRunner,
nil,
nil,
provider.logger,
postHookSpanFinalizer,
)
}
// ChatCompletion performs a chat completion request to the Nebius API.
func (provider *NebiusProvider) ChatCompletion(ctx *schemas.BifrostContext, key schemas.Key, request *schemas.BifrostChatRequest) (*schemas.BifrostChatResponse, *schemas.BifrostError) {
path := providerUtils.GetPathFromContext(ctx, "/v1/chat/completions")
// Append query parameter if present
if rawID, ok := request.Params.ExtraParams["ai_project_id"]; ok && rawID != nil {
if strings.Contains(path, "?") {
path = path + "&ai_project_id=" + fmt.Sprint(rawID)
} else {
path = path + "?ai_project_id=" + fmt.Sprint(rawID)
}
}
return openai.HandleOpenAIChatCompletionRequest(
ctx,
provider.client,
provider.networkConfig.BaseURL+path,
request,
key,
provider.networkConfig.ExtraHeaders,
providerUtils.ShouldSendBackRawRequest(ctx, provider.sendBackRawRequest),
providerUtils.ShouldSendBackRawResponse(ctx, provider.sendBackRawResponse),
provider.GetProviderKey(),
nil,
nil,
provider.logger,
)
}
// ChatCompletionStream performs a streaming chat completion request to the Nebius API.
// It supports real-time streaming of responses using Server-Sent Events (SSE).
// Uses Nebius's OpenAI-compatible streaming format.
// Returns a channel containing BifrostStreamChunk objects representing the stream or an error if the request fails.
func (provider *NebiusProvider) ChatCompletionStream(ctx *schemas.BifrostContext, postHookRunner schemas.PostHookRunner, postHookSpanFinalizer func(context.Context), key schemas.Key, request *schemas.BifrostChatRequest) (chan *schemas.BifrostStreamChunk, *schemas.BifrostError) {
var authHeader map[string]string
if key.Value.GetValue() != "" {
authHeader = map[string]string{"Authorization": "Bearer " + key.Value.GetValue()}
}
// Use shared OpenAI-compatible streaming logic
return openai.HandleOpenAIChatCompletionStreaming(
ctx,
provider.streamingClient,
provider.networkConfig.BaseURL+providerUtils.GetPathFromContext(ctx, "/v1/chat/completions"),
request,
authHeader,
provider.networkConfig.ExtraHeaders,
providerUtils.ShouldSendBackRawRequest(ctx, provider.sendBackRawRequest),
providerUtils.ShouldSendBackRawResponse(ctx, provider.sendBackRawResponse),
provider.GetProviderKey(),
postHookRunner,
nil,
nil,
nil,
nil,
nil,
provider.logger,
postHookSpanFinalizer,
)
}
func (provider *NebiusProvider) Responses(ctx *schemas.BifrostContext, key schemas.Key, request *schemas.BifrostResponsesRequest) (*schemas.BifrostResponsesResponse, *schemas.BifrostError) {
chatResponse, err := provider.ChatCompletion(ctx, key, request.ToChatRequest())
if err != nil {
return nil, err
}
response := chatResponse.ToBifrostResponsesResponse()
return response, nil
}
// ResponsesStream performs a streaming responses request to the Nebius API.
func (provider *NebiusProvider) ResponsesStream(ctx *schemas.BifrostContext, postHookRunner schemas.PostHookRunner, postHookSpanFinalizer func(context.Context), key schemas.Key, request *schemas.BifrostResponsesRequest) (chan *schemas.BifrostStreamChunk, *schemas.BifrostError) {
ctx.SetValue(schemas.BifrostContextKeyIsResponsesToChatCompletionFallback, true)
return provider.ChatCompletionStream(
ctx,
postHookRunner,
postHookSpanFinalizer,
key,
request.ToChatRequest(),
)
}
// Embedding generates embeddings for the given input text(s).
// The input can be either a single string or a slice of strings for batch embedding.
// Returns a BifrostResponse containing the embedding(s) and any error that occurred.
func (provider *NebiusProvider) Embedding(ctx *schemas.BifrostContext, key schemas.Key, request *schemas.BifrostEmbeddingRequest) (*schemas.BifrostEmbeddingResponse, *schemas.BifrostError) {
return openai.HandleOpenAIEmbeddingRequest(
ctx,
provider.client,
provider.networkConfig.BaseURL+providerUtils.GetPathFromContext(ctx, "/v1/embeddings"),
request,
key,
provider.networkConfig.ExtraHeaders,
provider.GetProviderKey(),
providerUtils.ShouldSendBackRawRequest(ctx, provider.sendBackRawRequest),
providerUtils.ShouldSendBackRawResponse(ctx, provider.sendBackRawResponse),
nil,
provider.logger)
}
// Speech is not supported by the Nebius provider.
func (provider *NebiusProvider) Speech(ctx *schemas.BifrostContext, key schemas.Key, request *schemas.BifrostSpeechRequest) (*schemas.BifrostSpeechResponse, *schemas.BifrostError) {
return nil, providerUtils.NewUnsupportedOperationError(schemas.SpeechRequest, provider.GetProviderKey())
}
// Rerank is not supported by the Nebius provider.
func (provider *NebiusProvider) Rerank(ctx *schemas.BifrostContext, key schemas.Key, request *schemas.BifrostRerankRequest) (*schemas.BifrostRerankResponse, *schemas.BifrostError) {
return nil, providerUtils.NewUnsupportedOperationError(schemas.RerankRequest, provider.GetProviderKey())
}
// OCR is not supported by the Nebius provider.
func (provider *NebiusProvider) OCR(ctx *schemas.BifrostContext, key schemas.Key, request *schemas.BifrostOCRRequest) (*schemas.BifrostOCRResponse, *schemas.BifrostError) {
return nil, providerUtils.NewUnsupportedOperationError(schemas.OCRRequest, provider.GetProviderKey())
}
// SpeechStream is not supported by the Nebius provider.
func (provider *NebiusProvider) SpeechStream(ctx *schemas.BifrostContext, postHookRunner schemas.PostHookRunner, postHookSpanFinalizer func(context.Context), key schemas.Key, request *schemas.BifrostSpeechRequest) (chan *schemas.BifrostStreamChunk, *schemas.BifrostError) {
return nil, providerUtils.NewUnsupportedOperationError(schemas.SpeechStreamRequest, provider.GetProviderKey())
}
// Transcription is not supported by the Nebius provider.
func (provider *NebiusProvider) Transcription(ctx *schemas.BifrostContext, key schemas.Key, request *schemas.BifrostTranscriptionRequest) (*schemas.BifrostTranscriptionResponse, *schemas.BifrostError) {
return nil, providerUtils.NewUnsupportedOperationError(schemas.TranscriptionRequest, provider.GetProviderKey())
}
// TranscriptionStream is not supported by the Nebius provider.
func (provider *NebiusProvider) TranscriptionStream(ctx *schemas.BifrostContext, postHookRunner schemas.PostHookRunner, postHookSpanFinalizer func(context.Context), key schemas.Key, request *schemas.BifrostTranscriptionRequest) (chan *schemas.BifrostStreamChunk, *schemas.BifrostError) {
return nil, providerUtils.NewUnsupportedOperationError(schemas.TranscriptionStreamRequest, provider.GetProviderKey())
}
// ImageGeneration performs an Image Generation request to Nebius's API.
// It formats the request, sends it to Nebius Token Factory, and processes the response.
// Returns a BifrostResponse containing the bifrost response or an error if the request fails.
func (provider *NebiusProvider) ImageGeneration(ctx *schemas.BifrostContext, key schemas.Key, request *schemas.BifrostImageGenerationRequest) (*schemas.BifrostImageGenerationResponse, *schemas.BifrostError) {
// Validate request is not nil
if request == nil {
return nil, providerUtils.NewBifrostOperationError("image generation request is nil", nil)
}
// Validate input and prompt are not nil/empty
if request.Input == nil || strings.TrimSpace(request.Input.Prompt) == "" {
return nil, providerUtils.NewBifrostOperationError("prompt cannot be empty", nil)
}
path := providerUtils.GetPathFromContext(ctx, "/v1/images/generations")
// Create request
req := fasthttp.AcquireRequest()
resp := fasthttp.AcquireResponse()
defer fasthttp.ReleaseRequest(req)
defer fasthttp.ReleaseResponse(resp)
// Append query parameter if present
if request.Params != nil && request.Params.ExtraParams != nil {
if rawID, ok := request.Params.ExtraParams["ai_project_id"]; ok && rawID != nil {
escapedID := url.QueryEscape(fmt.Sprint(rawID))
if strings.Contains(path, "?") {
path = path + "&ai_project_id=" + escapedID
} else {
path = path + "?ai_project_id=" + escapedID
}
}
}
// Set any extra headers from network config
providerUtils.SetExtraHeaders(ctx, req, provider.networkConfig.ExtraHeaders, nil)
req.SetRequestURI(provider.networkConfig.BaseURL + path)
req.Header.SetMethod(http.MethodPost)
req.Header.SetContentType("application/json")
if value := key.Value.GetValue(); value != "" {
req.Header.Set("Authorization", "Bearer "+value)
}
// Use centralized converter
jsonData, bifrostErr := providerUtils.CheckContextAndGetRequestBody(
ctx,
request,
func() (providerUtils.RequestBodyWithExtraParams, error) {
return provider.ToNebiusImageGenerationRequest(request)
})
if bifrostErr != nil {
return nil, bifrostErr
}
if !providerUtils.ApplyLargePayloadRequestBodyWithModelNormalization(ctx, req, schemas.Nebius) {
req.SetBody(jsonData)
}
// Make request
latency, bifrostErr, wait := providerUtils.MakeRequestWithContext(ctx, provider.client, req, resp)
defer wait()
if bifrostErr != nil {
return nil, bifrostErr
}
// Handle error response
if resp.StatusCode() != fasthttp.StatusOK {
return nil, providerUtils.EnrichError(ctx, parseNebiusImageError(resp), jsonData, nil, providerUtils.ShouldSendBackRawRequest(ctx, provider.sendBackRawRequest), providerUtils.ShouldSendBackRawResponse(ctx, provider.sendBackRawResponse))
}
body, err := providerUtils.CheckAndDecodeBody(resp)
if err != nil {
return nil, providerUtils.EnrichError(ctx, providerUtils.NewBifrostOperationError(schemas.ErrProviderResponseDecode, err), jsonData, nil, provider.sendBackRawRequest, provider.sendBackRawResponse)
}
response := &schemas.BifrostImageGenerationResponse{}
sendBackRawRequest, sendBackRawResponse := providerUtils.ShouldSendBackRawRequest(ctx, provider.sendBackRawRequest), providerUtils.ShouldSendBackRawResponse(ctx, provider.sendBackRawResponse)
// Use enhanced response handler with pre-allocated response
rawRequest, rawResponse, bifrostErr := providerUtils.HandleProviderResponse(
body,
response,
jsonData,
sendBackRawRequest,
sendBackRawResponse,
)
if bifrostErr != nil {
return nil, bifrostErr
}
response.ExtraFields.Latency = latency.Milliseconds()
// Set raw request if enabled
if sendBackRawRequest {
response.ExtraFields.RawRequest = rawRequest
}
// Set raw response if enabled
if sendBackRawResponse {
response.ExtraFields.RawResponse = rawResponse
}
return response, nil
}
// ImageGenerationStream is not supported by Nebius provider.
func (provider *NebiusProvider) ImageGenerationStream(ctx *schemas.BifrostContext, postHookRunner schemas.PostHookRunner, postHookSpanFinalizer func(context.Context), key schemas.Key, request *schemas.BifrostImageGenerationRequest) (chan *schemas.BifrostStreamChunk, *schemas.BifrostError) {
return nil, providerUtils.NewUnsupportedOperationError(schemas.ImageGenerationStreamRequest, provider.GetProviderKey())
}
// ImageEdit is not supported by the Nebius provider.
func (provider *NebiusProvider) ImageEdit(ctx *schemas.BifrostContext, key schemas.Key, request *schemas.BifrostImageEditRequest) (*schemas.BifrostImageGenerationResponse, *schemas.BifrostError) {
return nil, providerUtils.NewUnsupportedOperationError(schemas.ImageEditRequest, provider.GetProviderKey())
}
// ImageEditStream is not supported by the Nebius provider.
func (provider *NebiusProvider) ImageEditStream(ctx *schemas.BifrostContext, postHookRunner schemas.PostHookRunner, postHookSpanFinalizer func(context.Context), key schemas.Key, request *schemas.BifrostImageEditRequest) (chan *schemas.BifrostStreamChunk, *schemas.BifrostError) {
return nil, providerUtils.NewUnsupportedOperationError(schemas.ImageEditStreamRequest, provider.GetProviderKey())
}
// ImageVariation is not supported by the Nebius provider.
func (provider *NebiusProvider) ImageVariation(ctx *schemas.BifrostContext, key schemas.Key, request *schemas.BifrostImageVariationRequest) (*schemas.BifrostImageGenerationResponse, *schemas.BifrostError) {
return nil, providerUtils.NewUnsupportedOperationError(schemas.ImageVariationRequest, provider.GetProviderKey())
}
// VideoGeneration is not supported by the Nebius provider.
func (provider *NebiusProvider) VideoGeneration(_ *schemas.BifrostContext, _ schemas.Key, _ *schemas.BifrostVideoGenerationRequest) (*schemas.BifrostVideoGenerationResponse, *schemas.BifrostError) {
return nil, providerUtils.NewUnsupportedOperationError(schemas.VideoGenerationRequest, provider.GetProviderKey())
}
// VideoRetrieve is not supported by the Nebius provider.
func (provider *NebiusProvider) VideoRetrieve(_ *schemas.BifrostContext, _ schemas.Key, _ *schemas.BifrostVideoRetrieveRequest) (*schemas.BifrostVideoGenerationResponse, *schemas.BifrostError) {
return nil, providerUtils.NewUnsupportedOperationError(schemas.VideoRetrieveRequest, provider.GetProviderKey())
}
// VideoDownload is not supported by the Nebius provider.
func (provider *NebiusProvider) VideoDownload(_ *schemas.BifrostContext, _ schemas.Key, _ *schemas.BifrostVideoDownloadRequest) (*schemas.BifrostVideoDownloadResponse, *schemas.BifrostError) {
return nil, providerUtils.NewUnsupportedOperationError(schemas.VideoDownloadRequest, provider.GetProviderKey())
}
// VideoDelete is not supported by Nebius provider.
func (provider *NebiusProvider) VideoDelete(_ *schemas.BifrostContext, _ schemas.Key, _ *schemas.BifrostVideoDeleteRequest) (*schemas.BifrostVideoDeleteResponse, *schemas.BifrostError) {
return nil, providerUtils.NewUnsupportedOperationError(schemas.VideoDeleteRequest, provider.GetProviderKey())
}
// VideoList is not supported by Nebius provider.
func (provider *NebiusProvider) VideoList(_ *schemas.BifrostContext, _ schemas.Key, _ *schemas.BifrostVideoListRequest) (*schemas.BifrostVideoListResponse, *schemas.BifrostError) {
return nil, providerUtils.NewUnsupportedOperationError(schemas.VideoListRequest, provider.GetProviderKey())
}
// VideoRemix is not supported by Nebius provider.
func (provider *NebiusProvider) VideoRemix(_ *schemas.BifrostContext, _ schemas.Key, _ *schemas.BifrostVideoRemixRequest) (*schemas.BifrostVideoGenerationResponse, *schemas.BifrostError) {
return nil, providerUtils.NewUnsupportedOperationError(schemas.VideoRemixRequest, provider.GetProviderKey())
}
// BatchCreate is not supported by Nebius provider.
func (provider *NebiusProvider) BatchCreate(_ *schemas.BifrostContext, _ schemas.Key, _ *schemas.BifrostBatchCreateRequest) (*schemas.BifrostBatchCreateResponse, *schemas.BifrostError) {
return nil, providerUtils.NewUnsupportedOperationError(schemas.BatchCreateRequest, provider.GetProviderKey())
}
// BatchList is not supported by Nebius provider.
func (provider *NebiusProvider) BatchList(_ *schemas.BifrostContext, _ []schemas.Key, _ *schemas.BifrostBatchListRequest) (*schemas.BifrostBatchListResponse, *schemas.BifrostError) {
return nil, providerUtils.NewUnsupportedOperationError(schemas.BatchListRequest, provider.GetProviderKey())
}
// BatchRetrieve is not supported by Nebius provider.
func (provider *NebiusProvider) BatchRetrieve(_ *schemas.BifrostContext, _ []schemas.Key, _ *schemas.BifrostBatchRetrieveRequest) (*schemas.BifrostBatchRetrieveResponse, *schemas.BifrostError) {
return nil, providerUtils.NewUnsupportedOperationError(schemas.BatchRetrieveRequest, provider.GetProviderKey())
}
// BatchCancel is not supported by Nebius provider.
func (provider *NebiusProvider) BatchCancel(_ *schemas.BifrostContext, _ []schemas.Key, _ *schemas.BifrostBatchCancelRequest) (*schemas.BifrostBatchCancelResponse, *schemas.BifrostError) {
return nil, providerUtils.NewUnsupportedOperationError(schemas.BatchCancelRequest, provider.GetProviderKey())
}
// BatchDelete is not supported by Nebius provider.
func (provider *NebiusProvider) BatchDelete(_ *schemas.BifrostContext, _ []schemas.Key, _ *schemas.BifrostBatchDeleteRequest) (*schemas.BifrostBatchDeleteResponse, *schemas.BifrostError) {
return nil, providerUtils.NewUnsupportedOperationError(schemas.BatchDeleteRequest, provider.GetProviderKey())
}
// BatchResults is not supported by Nebius provider.
func (provider *NebiusProvider) BatchResults(_ *schemas.BifrostContext, _ []schemas.Key, _ *schemas.BifrostBatchResultsRequest) (*schemas.BifrostBatchResultsResponse, *schemas.BifrostError) {
return nil, providerUtils.NewUnsupportedOperationError(schemas.BatchResultsRequest, provider.GetProviderKey())
}
// FileUpload is not supported by Nebius provider.
func (provider *NebiusProvider) FileUpload(_ *schemas.BifrostContext, _ schemas.Key, _ *schemas.BifrostFileUploadRequest) (*schemas.BifrostFileUploadResponse, *schemas.BifrostError) {
return nil, providerUtils.NewUnsupportedOperationError(schemas.FileUploadRequest, provider.GetProviderKey())
}
// FileList is not supported by Nebius provider.
func (provider *NebiusProvider) FileList(_ *schemas.BifrostContext, _ []schemas.Key, _ *schemas.BifrostFileListRequest) (*schemas.BifrostFileListResponse, *schemas.BifrostError) {
return nil, providerUtils.NewUnsupportedOperationError(schemas.FileListRequest, provider.GetProviderKey())
}
// FileRetrieve is not supported by Nebius provider.
func (provider *NebiusProvider) FileRetrieve(_ *schemas.BifrostContext, _ []schemas.Key, _ *schemas.BifrostFileRetrieveRequest) (*schemas.BifrostFileRetrieveResponse, *schemas.BifrostError) {
return nil, providerUtils.NewUnsupportedOperationError(schemas.FileRetrieveRequest, provider.GetProviderKey())
}
// FileDelete is not supported by Nebius provider.
func (provider *NebiusProvider) FileDelete(_ *schemas.BifrostContext, _ []schemas.Key, _ *schemas.BifrostFileDeleteRequest) (*schemas.BifrostFileDeleteResponse, *schemas.BifrostError) {
return nil, providerUtils.NewUnsupportedOperationError(schemas.FileDeleteRequest, provider.GetProviderKey())
}
// FileContent is not supported by Nebius provider.
func (provider *NebiusProvider) FileContent(_ *schemas.BifrostContext, _ []schemas.Key, _ *schemas.BifrostFileContentRequest) (*schemas.BifrostFileContentResponse, *schemas.BifrostError) {
return nil, providerUtils.NewUnsupportedOperationError(schemas.FileContentRequest, provider.GetProviderKey())
}
// CountTokens is not supported by Nebius provider.
func (provider *NebiusProvider) CountTokens(_ *schemas.BifrostContext, _ schemas.Key, _ *schemas.BifrostResponsesRequest) (*schemas.BifrostCountTokensResponse, *schemas.BifrostError) {
return nil, providerUtils.NewUnsupportedOperationError(schemas.CountTokensRequest, provider.GetProviderKey())
}
// ContainerCreate is not supported by the Nebius provider.
func (provider *NebiusProvider) ContainerCreate(_ *schemas.BifrostContext, _ schemas.Key, _ *schemas.BifrostContainerCreateRequest) (*schemas.BifrostContainerCreateResponse, *schemas.BifrostError) {
return nil, providerUtils.NewUnsupportedOperationError(schemas.ContainerCreateRequest, provider.GetProviderKey())
}
// ContainerList is not supported by the Nebius provider.
func (provider *NebiusProvider) ContainerList(_ *schemas.BifrostContext, _ []schemas.Key, _ *schemas.BifrostContainerListRequest) (*schemas.BifrostContainerListResponse, *schemas.BifrostError) {
return nil, providerUtils.NewUnsupportedOperationError(schemas.ContainerListRequest, provider.GetProviderKey())
}
// ContainerRetrieve is not supported by the Nebius provider.
func (provider *NebiusProvider) ContainerRetrieve(_ *schemas.BifrostContext, _ []schemas.Key, _ *schemas.BifrostContainerRetrieveRequest) (*schemas.BifrostContainerRetrieveResponse, *schemas.BifrostError) {
return nil, providerUtils.NewUnsupportedOperationError(schemas.ContainerRetrieveRequest, provider.GetProviderKey())
}
// ContainerDelete is not supported by the Nebius provider.
func (provider *NebiusProvider) ContainerDelete(_ *schemas.BifrostContext, _ []schemas.Key, _ *schemas.BifrostContainerDeleteRequest) (*schemas.BifrostContainerDeleteResponse, *schemas.BifrostError) {
return nil, providerUtils.NewUnsupportedOperationError(schemas.ContainerDeleteRequest, provider.GetProviderKey())
}
// ContainerFileCreate is not supported by the Nebius provider.
func (provider *NebiusProvider) ContainerFileCreate(_ *schemas.BifrostContext, _ schemas.Key, _ *schemas.BifrostContainerFileCreateRequest) (*schemas.BifrostContainerFileCreateResponse, *schemas.BifrostError) {
return nil, providerUtils.NewUnsupportedOperationError(schemas.ContainerFileCreateRequest, provider.GetProviderKey())
}
// ContainerFileList is not supported by the Nebius provider.
func (provider *NebiusProvider) ContainerFileList(_ *schemas.BifrostContext, _ []schemas.Key, _ *schemas.BifrostContainerFileListRequest) (*schemas.BifrostContainerFileListResponse, *schemas.BifrostError) {
return nil, providerUtils.NewUnsupportedOperationError(schemas.ContainerFileListRequest, provider.GetProviderKey())
}
// ContainerFileRetrieve is not supported by the Nebius provider.
func (provider *NebiusProvider) ContainerFileRetrieve(_ *schemas.BifrostContext, _ []schemas.Key, _ *schemas.BifrostContainerFileRetrieveRequest) (*schemas.BifrostContainerFileRetrieveResponse, *schemas.BifrostError) {
return nil, providerUtils.NewUnsupportedOperationError(schemas.ContainerFileRetrieveRequest, provider.GetProviderKey())
}
// ContainerFileContent is not supported by the Nebius provider.
func (provider *NebiusProvider) ContainerFileContent(_ *schemas.BifrostContext, _ []schemas.Key, _ *schemas.BifrostContainerFileContentRequest) (*schemas.BifrostContainerFileContentResponse, *schemas.BifrostError) {
return nil, providerUtils.NewUnsupportedOperationError(schemas.ContainerFileContentRequest, provider.GetProviderKey())
}
// ContainerFileDelete is not supported by the Nebius provider.
func (provider *NebiusProvider) ContainerFileDelete(_ *schemas.BifrostContext, _ []schemas.Key, _ *schemas.BifrostContainerFileDeleteRequest) (*schemas.BifrostContainerFileDeleteResponse, *schemas.BifrostError) {
return nil, providerUtils.NewUnsupportedOperationError(schemas.ContainerFileDeleteRequest, provider.GetProviderKey())
}
// Passthrough is not supported by the Nebius provider.
func (provider *NebiusProvider) Passthrough(_ *schemas.BifrostContext, _ schemas.Key, _ *schemas.BifrostPassthroughRequest) (*schemas.BifrostPassthroughResponse, *schemas.BifrostError) {
return nil, providerUtils.NewUnsupportedOperationError(schemas.PassthroughRequest, provider.GetProviderKey())
}
func (provider *NebiusProvider) PassthroughStream(_ *schemas.BifrostContext, _ schemas.PostHookRunner, _ func(context.Context), _ schemas.Key, _ *schemas.BifrostPassthroughRequest) (chan *schemas.BifrostStreamChunk, *schemas.BifrostError) {
return nil, providerUtils.NewUnsupportedOperationError(schemas.PassthroughStreamRequest, provider.GetProviderKey())
}

View File

@@ -0,0 +1,60 @@
package nebius_test
import (
"os"
"testing"
"github.com/maximhq/bifrost/core/internal/llmtests"
"github.com/maximhq/bifrost/core/schemas"
)
func TestNebius(t *testing.T) {
t.Parallel()
if os.Getenv("NEBIUS_API_KEY") == "" {
t.Skip("Skipping Nebius tests because NEBIUS_API_KEY is not set")
}
client, ctx, cancel, err := llmtests.SetupTest()
if err != nil {
t.Fatalf("Error initializing test setup: %v", err)
}
defer cancel()
defer client.Shutdown()
testConfig := llmtests.ComprehensiveTestConfig{
Provider: schemas.Nebius,
ChatModel: "openai/gpt-oss-120b",
TextModel: "openai/gpt-oss-120b",
Fallbacks: []schemas.Fallback{
{Provider: schemas.Nebius, Model: "meta-llama/Meta-Llama-3.1-8B-Instruct-fast"},
},
EmbeddingModel: "BAAI/bge-en-icl",
ImageGenerationModel: "black-forest-labs/flux-schnell",
Scenarios: llmtests.TestScenarios{
TextCompletion: true,
TextCompletionStream: true,
SimpleChat: true,
CompletionStream: true,
MultiTurnConversation: true,
ToolCalls: true,
ToolCallsStreaming: true,
MultipleToolCalls: true,
MultipleToolCallsStreaming: true,
End2EndToolCalling: true,
AutomaticFunctionCall: true,
ImageURL: true,
ImageBase64: true,
MultipleImages: true,
ImageGeneration: true,
CompleteEnd2End: true,
ImageGenerationStream: false,
Embedding: true, // Nebius supports embeddings
ListModels: true,
},
}
t.Run("NebiusTests", func(t *testing.T) {
llmtests.RunAllComprehensiveTests(t, client, ctx, testConfig)
})
}

View File

@@ -0,0 +1,84 @@
package nebius
import (
"strings"
"github.com/bytedance/sonic"
"github.com/maximhq/bifrost/core/schemas"
)
// NebiusImageGenerationRequest represents a Nebius image generation request
type NebiusImageGenerationRequest struct {
Model *string `json:"model"`
Prompt *string `json:"prompt"`
Loras []NebiusLora `json:"loras,omitempty"` // List of compatible LoRAs
Width *int `json:"width,omitempty"` // Width of output image (64-2048)
Height *int `json:"height,omitempty"` // Height of output image (64-2048)
NumInferenceSteps *int `json:"num_inference_steps,omitempty"` // number of denoising steps
Seed *int `json:"seed,omitempty"` // seed for image generation
GuidanceScale *int `json:"guidance_scale,omitempty"` // 0-100
NegativePrompt *string `json:"negative_prompt,omitempty"`
ResponseExtension *string `json:"response_extension,omitempty"` // webp, jpg, png
ResponseFormat *string `json:"response_format,omitempty"` // b64_json, url
Fallbacks []string `json:"fallbacks,omitempty"`
ExtraParams map[string]interface{} `json:"-"`
}
// GetExtraParams implements the RequestBodyWithExtraParams interface
func (r *NebiusImageGenerationRequest) GetExtraParams() map[string]interface{} {
return r.ExtraParams
}
type NebiusLora struct {
URL string `json:"url"`
Scale int `json:"scale"`
}
// NebiusImageGenerationResponse represents a Nebius image generation response
type NebiusImageGenerationResponse struct {
Data []schemas.ImageData `json:"data"`
Id string `json:"id"`
}
// NebiusError represents the error response format from Nebius API
type NebiusError struct {
Detail *NebiusErrorDetail `json:"detail,omitempty"`
}
// NebiusErrorDetail handles both string (simple errors) and array (validation errors) formats
type NebiusErrorDetail struct {
Message *string `json:"-"`
ValidationErrors []NebiusErrorDetailItem `json:"-"`
}
// NebiusErrorDetailItem represents a single validation error entry
type NebiusErrorDetailItem struct {
Loc []string `json:"loc"`
Msg string `json:"msg"`
Type string `json:"type"`
}
// UnmarshalJSON implements custom JSON unmarshaling to handle both string and array formats from Nebius API.
func (d *NebiusErrorDetail) UnmarshalJSON(data []byte) error {
// First, try to unmarshal as an array (validation errors)
trimmed := strings.TrimSpace(string(data))
if trimmed == "" || trimmed == "null" {
return nil
}
if len(trimmed) > 0 && trimmed[0] == '[' {
var validationErrors []NebiusErrorDetailItem
if err := sonic.Unmarshal(data, &validationErrors); err != nil {
return err
}
d.ValidationErrors = validationErrors
return nil
}
// If not an array, try to unmarshal as a string
var message string
if err := sonic.Unmarshal(data, &message); err != nil {
return err
}
d.Message = &message
return nil
}