diff --git a/.github/workflows/pr-tests.yml b/.github/workflows/pr-tests.yml index 70bb7e3605..4d044938e3 100644 --- a/.github/workflows/pr-tests.yml +++ b/.github/workflows/pr-tests.yml @@ -122,6 +122,7 @@ jobs: AZURE_API_KEY: ${{ secrets.AZURE_API_KEY }} ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }} GROQ_API_KEY: ${{ secrets.GROQ_API_KEY }} + DEEPSEEK_API_KEY: ${{ secrets.DEEPSEEK_API_KEY }} MISTRAL_API_KEY: ${{ secrets.MISTRAL_API_KEY }} OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }} GEMINI_API_KEY: ${{ secrets.GEMINI_API_KEY }} diff --git a/.github/workflows/release-pipeline.yml b/.github/workflows/release-pipeline.yml index 9b2e625c92..087926c23a 100644 --- a/.github/workflows/release-pipeline.yml +++ b/.github/workflows/release-pipeline.yml @@ -153,6 +153,7 @@ jobs: api.anthropic.com:443 api.cerebras.ai:443 api.cohere.ai:443 + api.deepseek.com:443 api.elevenlabs.io:443 api.fireworks.ai:443 api.github.com:443 @@ -212,6 +213,7 @@ jobs: AZURE_TENANT_ID: ${{ secrets.AZURE_TENANT_ID }} ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }} GROQ_API_KEY: ${{ secrets.GROQ_API_KEY }} + DEEPSEEK_API_KEY: ${{ secrets.DEEPSEEK_API_KEY }} MISTRAL_API_KEY: ${{ secrets.MISTRAL_API_KEY }} OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }} GEMINI_API_KEY: ${{ secrets.GEMINI_API_KEY }} @@ -280,6 +282,7 @@ jobs: _grpc_config.cluster.qdrant.io:443 _grpc_config.localhost:443 api.github.com:443 + api.deepseek.com:443 auth.docker.io:443 cluster.qdrant.io:443 cluster.weaviate.network:443 @@ -338,6 +341,7 @@ jobs: AZURE_TENANT_ID: ${{ secrets.AZURE_TENANT_ID }} ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }} GROQ_API_KEY: ${{ secrets.GROQ_API_KEY }} + DEEPSEEK_API_KEY: ${{ secrets.DEEPSEEK_API_KEY }} MISTRAL_API_KEY: ${{ secrets.MISTRAL_API_KEY }} OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }} GEMINI_API_KEY: ${{ secrets.GEMINI_API_KEY }} @@ -374,6 +378,7 @@ jobs: _https._tcp.motd.ubuntu.com:443 _https._tcp.packages.microsoft.com:443 api.github.com:443 + api.deepseek.com:443 api.openai.com:443 auth.docker.io:443 azure.archive.ubuntu.com:80 @@ -440,6 +445,7 @@ jobs: AZURE_TENANT_ID: ${{ secrets.AZURE_TENANT_ID }} ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }} GROQ_API_KEY: ${{ secrets.GROQ_API_KEY }} + DEEPSEEK_API_KEY: ${{ secrets.DEEPSEEK_API_KEY }} MISTRAL_API_KEY: ${{ secrets.MISTRAL_API_KEY }} OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }} GEMINI_API_KEY: ${{ secrets.GEMINI_API_KEY }} @@ -734,6 +740,7 @@ jobs: api.anthropic.com:443 api.cerebras.ai:443 api.cohere.ai:443 + api.deepseek.com:443 api.elevenlabs.io:443 api.github.com:443 api.groq.com:443 @@ -799,6 +806,7 @@ jobs: MISTRAL_API_KEY: ${{ secrets.MISTRAL_API_KEY }} COHERE_API_KEY: ${{ secrets.COHERE_API_KEY }} GROQ_API_KEY: ${{ secrets.GROQ_API_KEY }} + DEEPSEEK_API_KEY: ${{ secrets.DEEPSEEK_API_KEY }} PERPLEXITY_API_KEY: ${{ secrets.PERPLEXITY_API_KEY }} CEREBRAS_API_KEY: ${{ secrets.CEREBRAS_API_KEY }} OPENROUTER_API_KEY: ${{ secrets.OPENROUTER_API_KEY }} @@ -843,6 +851,7 @@ jobs: api.anthropic.com:443 api.cerebras.ai:443 api.cohere.ai:443 + api.deepseek.com:443 api.elevenlabs.io:443 api.github.com:443 api.groq.com:443 @@ -908,6 +917,7 @@ jobs: MISTRAL_API_KEY: ${{ secrets.MISTRAL_API_KEY }} COHERE_API_KEY: ${{ secrets.COHERE_API_KEY }} GROQ_API_KEY: ${{ secrets.GROQ_API_KEY }} + DEEPSEEK_API_KEY: ${{ secrets.DEEPSEEK_API_KEY }} PERPLEXITY_API_KEY: ${{ secrets.PERPLEXITY_API_KEY }} CEREBRAS_API_KEY: ${{ secrets.CEREBRAS_API_KEY }} OPENROUTER_API_KEY: ${{ secrets.OPENROUTER_API_KEY }} @@ -965,6 +975,7 @@ jobs: egress-policy: block allowed-endpoints: > api.github.com:443 + api.deepseek.com:443 github.com:443 nodejs.org:443 release-assets.githubusercontent.com:443 @@ -1010,6 +1021,7 @@ jobs: AZURE_TENANT_ID: ${{ secrets.AZURE_TENANT_ID }} ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }} GROQ_API_KEY: ${{ secrets.GROQ_API_KEY }} + DEEPSEEK_API_KEY: ${{ secrets.DEEPSEEK_API_KEY }} MISTRAL_API_KEY: ${{ secrets.MISTRAL_API_KEY }} OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }} GEMINI_API_KEY: ${{ secrets.GEMINI_API_KEY }} @@ -1062,6 +1074,7 @@ jobs: egress-policy: block allowed-endpoints: > api.github.com:443 + api.deepseek.com:443 github.com:443 proxy.golang.org:443 release-assets.githubusercontent.com:443 @@ -1119,6 +1132,7 @@ jobs: AZURE_TENANT_ID: ${{ secrets.AZURE_TENANT_ID }} ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }} GROQ_API_KEY: ${{ secrets.GROQ_API_KEY }} + DEEPSEEK_API_KEY: ${{ secrets.DEEPSEEK_API_KEY }} MISTRAL_API_KEY: ${{ secrets.MISTRAL_API_KEY }} OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }} GEMINI_API_KEY: ${{ secrets.GEMINI_API_KEY }} @@ -1173,6 +1187,7 @@ jobs: _https._tcp.motd.ubuntu.com:443 _https._tcp.packages.microsoft.com:443 api.github.com:443 + api.deepseek.com:443 azure.archive.ubuntu.com:80 esm.ubuntu.com:443 github.com:443 @@ -1244,6 +1259,7 @@ jobs: AZURE_TENANT_ID: ${{ secrets.AZURE_TENANT_ID }} ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }} GROQ_API_KEY: ${{ secrets.GROQ_API_KEY }} + DEEPSEEK_API_KEY: ${{ secrets.DEEPSEEK_API_KEY }} MISTRAL_API_KEY: ${{ secrets.MISTRAL_API_KEY }} OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }} GEMINI_API_KEY: ${{ secrets.GEMINI_API_KEY }} @@ -2068,4 +2084,4 @@ jobs: MESSAGE=$(printf "šŸš€ **Release Pipeline Complete**\n\n**Components:**\n• Core: %s\n• Framework: %s\n• Plugins: %s\n• Bifrost HTTP: %s\n\n**Details:**\n• Branch: \`${{ github.ref_name }}\`\n• Commit: \`%.8s\`\n• Author: %s\n\n[View Workflow Run](${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }})" "$CORE_STATUS" "$FRAMEWORK_STATUS" "$PLUGINS_STATUS" "$BIFROST_STATUS" "${{ github.sha }}" "${{ github.actor }}") payload="$(jq -n --arg content "$MESSAGE" '{content:$content}')" - curl -sS -H "Content-Type: application/json" -d "$payload" "$DISCORD_WEBHOOK" \ No newline at end of file + curl -sS -H "Content-Type: application/json" -d "$payload" "$DISCORD_WEBHOOK" diff --git a/core/bifrost.go b/core/bifrost.go index 5fc49bef91..10c5005927 100644 --- a/core/bifrost.go +++ b/core/bifrost.go @@ -24,6 +24,7 @@ import ( "github.com/maximhq/bifrost/core/providers/bedrock" "github.com/maximhq/bifrost/core/providers/cerebras" "github.com/maximhq/bifrost/core/providers/cohere" + "github.com/maximhq/bifrost/core/providers/deepseek" "github.com/maximhq/bifrost/core/providers/elevenlabs" "github.com/maximhq/bifrost/core/providers/fireworks" "github.com/maximhq/bifrost/core/providers/gemini" @@ -3900,6 +3901,8 @@ func (bifrost *Bifrost) createBaseProvider(providerKey schemas.ModelProvider, co return perplexity.NewPerplexityProvider(config, bifrost.logger) case schemas.Cerebras: return cerebras.NewCerebrasProvider(config, bifrost.logger) + case schemas.DeepSeek: + return deepseek.NewDeepSeekProvider(config, bifrost.logger) case schemas.Gemini: return gemini.NewGeminiProvider(config, bifrost.logger), nil case schemas.OpenRouter: diff --git a/core/providers/deepseek/cachedcontents.go b/core/providers/deepseek/cachedcontents.go new file mode 100644 index 0000000000..358e869498 --- /dev/null +++ b/core/providers/deepseek/cachedcontents.go @@ -0,0 +1,33 @@ +package deepseek + +import ( + providerUtils "github.com/maximhq/bifrost/core/providers/utils" + "github.com/maximhq/bifrost/core/schemas" +) + +// CachedContentCreate is unsupported on DeepSeekProvider. DeepSeek handles +// context caching implicitly on standard chat/completions requests rather than +// exposing a named cached-content lifecycle API. +func (provider *DeepSeekProvider) CachedContentCreate(ctx *schemas.BifrostContext, key schemas.Key, request *schemas.BifrostCachedContentCreateRequest) (*schemas.BifrostCachedContentCreateResponse, *schemas.BifrostError) { + return nil, providerUtils.NewUnsupportedOperationError(schemas.CachedContentCreateRequest, provider.GetProviderKey()) +} + +// CachedContentList is unsupported on DeepSeekProvider (see CachedContentCreate). +func (provider *DeepSeekProvider) CachedContentList(ctx *schemas.BifrostContext, keys []schemas.Key, request *schemas.BifrostCachedContentListRequest) (*schemas.BifrostCachedContentListResponse, *schemas.BifrostError) { + return nil, providerUtils.NewUnsupportedOperationError(schemas.CachedContentListRequest, provider.GetProviderKey()) +} + +// CachedContentRetrieve is unsupported on DeepSeekProvider (see CachedContentCreate). +func (provider *DeepSeekProvider) CachedContentRetrieve(ctx *schemas.BifrostContext, keys []schemas.Key, request *schemas.BifrostCachedContentRetrieveRequest) (*schemas.BifrostCachedContentRetrieveResponse, *schemas.BifrostError) { + return nil, providerUtils.NewUnsupportedOperationError(schemas.CachedContentRetrieveRequest, provider.GetProviderKey()) +} + +// CachedContentUpdate is unsupported on DeepSeekProvider (see CachedContentCreate). +func (provider *DeepSeekProvider) CachedContentUpdate(ctx *schemas.BifrostContext, keys []schemas.Key, request *schemas.BifrostCachedContentUpdateRequest) (*schemas.BifrostCachedContentUpdateResponse, *schemas.BifrostError) { + return nil, providerUtils.NewUnsupportedOperationError(schemas.CachedContentUpdateRequest, provider.GetProviderKey()) +} + +// CachedContentDelete is unsupported on DeepSeekProvider (see CachedContentCreate). +func (provider *DeepSeekProvider) CachedContentDelete(ctx *schemas.BifrostContext, keys []schemas.Key, request *schemas.BifrostCachedContentDeleteRequest) (*schemas.BifrostCachedContentDeleteResponse, *schemas.BifrostError) { + return nil, providerUtils.NewUnsupportedOperationError(schemas.CachedContentDeleteRequest, provider.GetProviderKey()) +} diff --git a/core/providers/deepseek/deepseek.go b/core/providers/deepseek/deepseek.go new file mode 100644 index 0000000000..2fd417bf3d --- /dev/null +++ b/core/providers/deepseek/deepseek.go @@ -0,0 +1,355 @@ +// Package deepseek implements the DeepSeek provider. +package deepseek + +import ( + "context" + "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" +) + +// DeepSeekProvider implements the Provider interface for DeepSeek's API. +type DeepSeekProvider 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 +} + +// NewDeepSeekProvider creates a new DeepSeek provider instance. +func NewDeepSeekProvider(config *schemas.ProviderConfig, logger schemas.Logger) (*DeepSeekProvider, 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, + } + + client = providerUtils.ConfigureProxy(client, config.ProxyConfig, logger) + client = providerUtils.ConfigureDialer(client) + client = providerUtils.ConfigureTLS(client, config.NetworkConfig, logger) + streamingClient := providerUtils.BuildStreamingClient(client) + + if config.NetworkConfig.BaseURL == "" { + config.NetworkConfig.BaseURL = "https://api.deepseek.com" + } + config.NetworkConfig.BaseURL = strings.TrimRight(config.NetworkConfig.BaseURL, "/") + + return &DeepSeekProvider{ + logger: logger, + client: client, + streamingClient: streamingClient, + networkConfig: config.NetworkConfig, + sendBackRawRequest: config.SendBackRawRequest, + sendBackRawResponse: config.SendBackRawResponse, + }, nil +} + +// GetProviderKey returns the provider identifier for DeepSeek. +func (provider *DeepSeekProvider) GetProviderKey() schemas.ModelProvider { + return schemas.DeepSeek +} + +// ListModels performs a list models request to DeepSeek's API. +func (provider *DeepSeekProvider) 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, "/models"), + keys, + provider.networkConfig.ExtraHeaders, + provider.GetProviderKey(), + providerUtils.ShouldSendBackRawRequest(ctx, provider.sendBackRawRequest), + providerUtils.ShouldSendBackRawResponse(ctx, provider.sendBackRawResponse), + ) +} + +// TextCompletion is intentionally unsupported in the conservative first pass. +func (provider *DeepSeekProvider) TextCompletion(ctx *schemas.BifrostContext, key schemas.Key, request *schemas.BifrostTextCompletionRequest) (*schemas.BifrostTextCompletionResponse, *schemas.BifrostError) { + return nil, providerUtils.NewUnsupportedOperationError(schemas.TextCompletionRequest, provider.GetProviderKey()) +} + +// TextCompletionStream is intentionally unsupported in the conservative first pass. +func (provider *DeepSeekProvider) TextCompletionStream(ctx *schemas.BifrostContext, postHookRunner schemas.PostHookRunner, postHookSpanFinalizer func(context.Context), key schemas.Key, request *schemas.BifrostTextCompletionRequest) (chan *schemas.BifrostStreamChunk, *schemas.BifrostError) { + return nil, providerUtils.NewUnsupportedOperationError(schemas.TextCompletionStreamRequest, provider.GetProviderKey()) +} + +// ChatCompletion performs a chat completion request to the DeepSeek API. +func (provider *DeepSeekProvider) ChatCompletion(ctx *schemas.BifrostContext, key schemas.Key, request *schemas.BifrostChatRequest) (*schemas.BifrostChatResponse, *schemas.BifrostError) { + return openai.HandleOpenAIChatCompletionRequest( + ctx, + provider.client, + provider.networkConfig.BaseURL+providerUtils.GetPathFromContext(ctx, "/chat/completions"), + 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 DeepSeek API. +func (provider *DeepSeekProvider) 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 v := key.Value.GetValue(); v != "" { + authHeader = map[string]string{"Authorization": "Bearer " + v} + } + + return openai.HandleOpenAIChatCompletionStreaming( + ctx, + provider.streamingClient, + provider.networkConfig.BaseURL+providerUtils.GetPathFromContext(ctx, "/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, + ) +} + +// Responses performs a responses request via DeepSeek chat fallback. +func (provider *DeepSeekProvider) 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 + } + + return chatResponse.ToBifrostResponsesResponse(), nil +} + +// ResponsesStream performs a streaming responses request via DeepSeek chat fallback. +func (provider *DeepSeekProvider) 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 is not supported by the DeepSeek provider. +func (provider *DeepSeekProvider) Embedding(ctx *schemas.BifrostContext, key schemas.Key, request *schemas.BifrostEmbeddingRequest) (*schemas.BifrostEmbeddingResponse, *schemas.BifrostError) { + return nil, providerUtils.NewUnsupportedOperationError(schemas.EmbeddingRequest, provider.GetProviderKey()) +} + +// Speech is not supported by the DeepSeek provider. +func (provider *DeepSeekProvider) 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 DeepSeek provider. +func (provider *DeepSeekProvider) 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 DeepSeek provider. +func (provider *DeepSeekProvider) 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 DeepSeek provider. +func (provider *DeepSeekProvider) 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 DeepSeek provider. +func (provider *DeepSeekProvider) 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 DeepSeek provider. +func (provider *DeepSeekProvider) 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 is not supported by the DeepSeek provider. +func (provider *DeepSeekProvider) ImageGeneration(ctx *schemas.BifrostContext, key schemas.Key, request *schemas.BifrostImageGenerationRequest) (*schemas.BifrostImageGenerationResponse, *schemas.BifrostError) { + return nil, providerUtils.NewUnsupportedOperationError(schemas.ImageGenerationRequest, provider.GetProviderKey()) +} + +// ImageGenerationStream is not supported by the DeepSeek provider. +func (provider *DeepSeekProvider) 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 DeepSeek provider. +func (provider *DeepSeekProvider) 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 DeepSeek provider. +func (provider *DeepSeekProvider) 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 DeepSeek provider. +func (provider *DeepSeekProvider) 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 DeepSeek provider. +func (provider *DeepSeekProvider) 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 DeepSeek provider. +func (provider *DeepSeekProvider) 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 DeepSeek provider. +func (provider *DeepSeekProvider) VideoDownload(_ *schemas.BifrostContext, _ schemas.Key, _ *schemas.BifrostVideoDownloadRequest) (*schemas.BifrostVideoDownloadResponse, *schemas.BifrostError) { + return nil, providerUtils.NewUnsupportedOperationError(schemas.VideoDownloadRequest, provider.GetProviderKey()) +} + +// VideoDelete is not supported by the DeepSeek provider. +func (provider *DeepSeekProvider) VideoDelete(_ *schemas.BifrostContext, _ schemas.Key, _ *schemas.BifrostVideoDeleteRequest) (*schemas.BifrostVideoDeleteResponse, *schemas.BifrostError) { + return nil, providerUtils.NewUnsupportedOperationError(schemas.VideoDeleteRequest, provider.GetProviderKey()) +} + +// VideoList is not supported by the DeepSeek provider. +func (provider *DeepSeekProvider) VideoList(_ *schemas.BifrostContext, _ schemas.Key, _ *schemas.BifrostVideoListRequest) (*schemas.BifrostVideoListResponse, *schemas.BifrostError) { + return nil, providerUtils.NewUnsupportedOperationError(schemas.VideoListRequest, provider.GetProviderKey()) +} + +// VideoRemix is not supported by the DeepSeek provider. +func (provider *DeepSeekProvider) VideoRemix(_ *schemas.BifrostContext, _ schemas.Key, _ *schemas.BifrostVideoRemixRequest) (*schemas.BifrostVideoGenerationResponse, *schemas.BifrostError) { + return nil, providerUtils.NewUnsupportedOperationError(schemas.VideoRemixRequest, provider.GetProviderKey()) +} + +// BatchCreate is not supported by the DeepSeek provider. +func (provider *DeepSeekProvider) BatchCreate(_ *schemas.BifrostContext, _ schemas.Key, _ *schemas.BifrostBatchCreateRequest) (*schemas.BifrostBatchCreateResponse, *schemas.BifrostError) { + return nil, providerUtils.NewUnsupportedOperationError(schemas.BatchCreateRequest, provider.GetProviderKey()) +} + +// BatchList is not supported by the DeepSeek provider. +func (provider *DeepSeekProvider) BatchList(_ *schemas.BifrostContext, _ []schemas.Key, _ *schemas.BifrostBatchListRequest) (*schemas.BifrostBatchListResponse, *schemas.BifrostError) { + return nil, providerUtils.NewUnsupportedOperationError(schemas.BatchListRequest, provider.GetProviderKey()) +} + +// BatchRetrieve is not supported by the DeepSeek provider. +func (provider *DeepSeekProvider) BatchRetrieve(_ *schemas.BifrostContext, _ []schemas.Key, _ *schemas.BifrostBatchRetrieveRequest) (*schemas.BifrostBatchRetrieveResponse, *schemas.BifrostError) { + return nil, providerUtils.NewUnsupportedOperationError(schemas.BatchRetrieveRequest, provider.GetProviderKey()) +} + +// BatchCancel is not supported by the DeepSeek provider. +func (provider *DeepSeekProvider) BatchCancel(_ *schemas.BifrostContext, _ []schemas.Key, _ *schemas.BifrostBatchCancelRequest) (*schemas.BifrostBatchCancelResponse, *schemas.BifrostError) { + return nil, providerUtils.NewUnsupportedOperationError(schemas.BatchCancelRequest, provider.GetProviderKey()) +} + +// BatchDelete is not supported by the DeepSeek provider. +func (provider *DeepSeekProvider) BatchDelete(_ *schemas.BifrostContext, _ []schemas.Key, _ *schemas.BifrostBatchDeleteRequest) (*schemas.BifrostBatchDeleteResponse, *schemas.BifrostError) { + return nil, providerUtils.NewUnsupportedOperationError(schemas.BatchDeleteRequest, provider.GetProviderKey()) +} + +// BatchResults is not supported by the DeepSeek provider. +func (provider *DeepSeekProvider) BatchResults(_ *schemas.BifrostContext, _ []schemas.Key, _ *schemas.BifrostBatchResultsRequest) (*schemas.BifrostBatchResultsResponse, *schemas.BifrostError) { + return nil, providerUtils.NewUnsupportedOperationError(schemas.BatchResultsRequest, provider.GetProviderKey()) +} + +// FileUpload is not supported by the DeepSeek provider. +func (provider *DeepSeekProvider) FileUpload(_ *schemas.BifrostContext, _ schemas.Key, _ *schemas.BifrostFileUploadRequest) (*schemas.BifrostFileUploadResponse, *schemas.BifrostError) { + return nil, providerUtils.NewUnsupportedOperationError(schemas.FileUploadRequest, provider.GetProviderKey()) +} + +// FileList is not supported by the DeepSeek provider. +func (provider *DeepSeekProvider) FileList(_ *schemas.BifrostContext, _ []schemas.Key, _ *schemas.BifrostFileListRequest) (*schemas.BifrostFileListResponse, *schemas.BifrostError) { + return nil, providerUtils.NewUnsupportedOperationError(schemas.FileListRequest, provider.GetProviderKey()) +} + +// FileRetrieve is not supported by the DeepSeek provider. +func (provider *DeepSeekProvider) FileRetrieve(_ *schemas.BifrostContext, _ []schemas.Key, _ *schemas.BifrostFileRetrieveRequest) (*schemas.BifrostFileRetrieveResponse, *schemas.BifrostError) { + return nil, providerUtils.NewUnsupportedOperationError(schemas.FileRetrieveRequest, provider.GetProviderKey()) +} + +// FileDelete is not supported by the DeepSeek provider. +func (provider *DeepSeekProvider) FileDelete(_ *schemas.BifrostContext, _ []schemas.Key, _ *schemas.BifrostFileDeleteRequest) (*schemas.BifrostFileDeleteResponse, *schemas.BifrostError) { + return nil, providerUtils.NewUnsupportedOperationError(schemas.FileDeleteRequest, provider.GetProviderKey()) +} + +// FileContent is not supported by the DeepSeek provider. +func (provider *DeepSeekProvider) FileContent(_ *schemas.BifrostContext, _ []schemas.Key, _ *schemas.BifrostFileContentRequest) (*schemas.BifrostFileContentResponse, *schemas.BifrostError) { + return nil, providerUtils.NewUnsupportedOperationError(schemas.FileContentRequest, provider.GetProviderKey()) +} + +// CountTokens is not supported by the DeepSeek provider. +func (provider *DeepSeekProvider) 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 DeepSeek provider. +func (provider *DeepSeekProvider) 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 DeepSeek provider. +func (provider *DeepSeekProvider) 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 DeepSeek provider. +func (provider *DeepSeekProvider) 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 DeepSeek provider. +func (provider *DeepSeekProvider) 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 DeepSeek provider. +func (provider *DeepSeekProvider) 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 DeepSeek provider. +func (provider *DeepSeekProvider) 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 DeepSeek provider. +func (provider *DeepSeekProvider) 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 DeepSeek provider. +func (provider *DeepSeekProvider) 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 DeepSeek provider. +func (provider *DeepSeekProvider) 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 DeepSeek provider. +func (provider *DeepSeekProvider) Passthrough(_ *schemas.BifrostContext, _ schemas.Key, _ *schemas.BifrostPassthroughRequest) (*schemas.BifrostPassthroughResponse, *schemas.BifrostError) { + return nil, providerUtils.NewUnsupportedOperationError(schemas.PassthroughRequest, provider.GetProviderKey()) +} + +// PassthroughStream is not supported by the DeepSeek provider. +func (provider *DeepSeekProvider) 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()) +} diff --git a/core/providers/deepseek/deepseek_test.go b/core/providers/deepseek/deepseek_test.go new file mode 100644 index 0000000000..d24d27eb62 --- /dev/null +++ b/core/providers/deepseek/deepseek_test.go @@ -0,0 +1,142 @@ +package deepseek_test + +import ( + "context" + "fmt" + "net/http" + "net/http/httptest" + "os" + "strings" + "testing" + + bifrost "github.com/maximhq/bifrost/core" + "github.com/maximhq/bifrost/core/internal/llmtests" + deepseekprovider "github.com/maximhq/bifrost/core/providers/deepseek" + schemas "github.com/maximhq/bifrost/core/schemas" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func TestDeepSeek(t *testing.T) { + t.Parallel() + if strings.TrimSpace(os.Getenv("DEEPSEEK_API_KEY")) == "" { + t.Skip("Skipping DeepSeek tests because DEEPSEEK_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.DeepSeek, + ChatModel: "deepseek-v4-flash", + Fallbacks: []schemas.Fallback{ + {Provider: schemas.DeepSeek, Model: "deepseek-v4-pro"}, + }, + Scenarios: llmtests.TestScenarios{ + TextCompletion: false, + TextCompletionStream: false, + SimpleChat: true, + CompletionStream: true, + MultiTurnConversation: true, + ToolCalls: true, + ToolCallsStreaming: true, + MultipleToolCalls: false, + MultipleToolCallsStreaming: false, + End2EndToolCalling: true, + AutomaticFunctionCall: true, + ImageURL: false, + ImageBase64: false, + MultipleImages: false, + CompleteEnd2End: true, + Embedding: false, + Reasoning: false, + ListModels: true, + }, + } + + t.Run("DeepSeekTests", func(t *testing.T) { + llmtests.RunAllComprehensiveTests(t, client, ctx, testConfig) + }) +} + +func newTestDeepSeekProvider(t *testing.T, baseURL string) *deepseekprovider.DeepSeekProvider { + t.Helper() + + provider, err := deepseekprovider.NewDeepSeekProvider(&schemas.ProviderConfig{ + NetworkConfig: schemas.NetworkConfig{ + BaseURL: baseURL, + DefaultRequestTimeoutInSeconds: 30, + }, + }, bifrost.NewNoOpLogger()) + require.NoError(t, err) + return provider +} + +func TestDeepSeekChatCompletion_UsesRootChatPath(t *testing.T) { + t.Parallel() + + server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + assert.Equal(t, "/chat/completions", r.URL.Path) + assert.Equal(t, "Bearer test-api-key", r.Header.Get("Authorization")) + w.Header().Set("Content-Type", "application/json") + fmt.Fprint(w, `{"id":"chatcmpl-test","object":"chat.completion","created":1234567890,"model":"deepseek-v4-pro","choices":[{"index":0,"message":{"role":"assistant","content":"Hello from DeepSeek"},"finish_reason":"stop"}],"usage":{"prompt_tokens":5,"completion_tokens":4,"total_tokens":9}}`) + })) + defer server.Close() + + provider := newTestDeepSeekProvider(t, server.URL) + ctx := schemas.NewBifrostContext(context.Background(), schemas.NoDeadline) + content := "Hello" + + resp, bifrostErr := provider.ChatCompletion(ctx, schemas.Key{Value: schemas.EnvVar{Val: "test-api-key"}}, &schemas.BifrostChatRequest{ + Provider: schemas.DeepSeek, + Model: "deepseek-v4-pro", + Input: []schemas.ChatMessage{{ + Role: schemas.ChatMessageRoleUser, + Content: &schemas.ChatMessageContent{ContentStr: &content}, + }}, + }) + + require.Nil(t, bifrostErr) + require.NotNil(t, resp) + require.Len(t, resp.Choices, 1) + require.Equal(t, "Hello from DeepSeek", *resp.Choices[0].ChatNonStreamResponseChoice.Message.Content.ContentStr) +} + +func TestDeepSeekListModels_UsesRootModelsPath(t *testing.T) { + t.Parallel() + + server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + assert.Equal(t, "/models", r.URL.Path) + w.Header().Set("Content-Type", "application/json") + fmt.Fprint(w, `{"object":"list","data":[{"id":"deepseek-v4-pro","object":"model","owned_by":"deepseek"}]}`) + })) + defer server.Close() + + provider := newTestDeepSeekProvider(t, server.URL) + ctx := schemas.NewBifrostContext(context.Background(), schemas.NoDeadline) + + resp, bifrostErr := provider.ListModels(ctx, []schemas.Key{{Value: schemas.EnvVar{Val: "test-api-key"}}}, &schemas.BifrostListModelsRequest{Unfiltered: true}) + + require.Nil(t, bifrostErr) + require.NotNil(t, resp) + require.Len(t, resp.Data, 1) + require.Equal(t, "deepseek/deepseek-v4-pro", resp.Data[0].ID) +} + +func TestDeepSeekTextCompletion_IsUnsupportedInFirstPass(t *testing.T) { + t.Parallel() + + provider := newTestDeepSeekProvider(t, "https://api.deepseek.com") + ctx := schemas.NewBifrostContext(context.Background(), schemas.NoDeadline) + + resp, bifrostErr := provider.TextCompletion(ctx, schemas.Key{Value: schemas.EnvVar{Val: "test-api-key"}}, &schemas.BifrostTextCompletionRequest{}) + + require.Nil(t, resp) + require.NotNil(t, bifrostErr) + require.NotNil(t, bifrostErr.Error) + require.Equal(t, "unsupported_operation", *bifrostErr.Error.Code) +} diff --git a/core/schemas/bifrost.go b/core/schemas/bifrost.go index 0c2352e53e..650a71b971 100644 --- a/core/schemas/bifrost.go +++ b/core/schemas/bifrost.go @@ -48,6 +48,7 @@ const ( Parasail ModelProvider = "parasail" Perplexity ModelProvider = "perplexity" Cerebras ModelProvider = "cerebras" + DeepSeek ModelProvider = "deepseek" Gemini ModelProvider = "gemini" OpenRouter ModelProvider = "openrouter" Elevenlabs ModelProvider = "elevenlabs" @@ -77,6 +78,7 @@ var StandardProviders = []ModelProvider{ Azure, Bedrock, Cerebras, + DeepSeek, Cohere, Gemini, Groq, diff --git a/docs/deployment-guides/config-json/schema-reference.mdx b/docs/deployment-guides/config-json/schema-reference.mdx index 672ff893bb..23f8af19cc 100644 --- a/docs/deployment-guides/config-json/schema-reference.mdx +++ b/docs/deployment-guides/config-json/schema-reference.mdx @@ -87,7 +87,7 @@ Full documentation: [Client Configuration](/deployment-guides/config-json/client Keyed by provider name. Each entry contains a `keys` array and optional `network_config`, `concurrency_and_buffer_size`, `proxy_config`. -Supported provider keys: `openai`, `anthropic`, `azure`, `bedrock`, `vertex`, `gemini`, `mistral`, `groq`, `cohere`, `perplexity`, `xai`, `cerebras`, `openrouter`, `nebius`, `fireworks`, `parasail`, `huggingface`, `replicate`, `ollama`, `vllm`, `sgl`, `elevenlabs`, `runway`. +Supported provider keys: `openai`, `anthropic`, `azure`, `bedrock`, `vertex`, `gemini`, `mistral`, `groq`, `cohere`, `deepseek`, `perplexity`, `xai`, `cerebras`, `openrouter`, `nebius`, `fireworks`, `parasail`, `huggingface`, `replicate`, `ollama`, `vllm`, `sgl`, `elevenlabs`, `runway`. Full documentation: [Provider Setup](/deployment-guides/config-json/providers). diff --git a/docs/docs.json b/docs/docs.json index 0d7aec147a..1a55bf3262 100644 --- a/docs/docs.json +++ b/docs/docs.json @@ -134,6 +134,7 @@ "providers/supported-providers/bedrock", "providers/supported-providers/cerebras", "providers/supported-providers/cohere", + "providers/supported-providers/deepseek", "providers/supported-providers/databricks", "providers/supported-providers/elevenlabs", "providers/supported-providers/fireworks", diff --git a/docs/openapi/openapi.json b/docs/openapi/openapi.json index afcd4003dc..d172b7d670 100644 --- a/docs/openapi/openapi.json +++ b/docs/openapi/openapi.json @@ -42293,6 +42293,7 @@ "perplexity", "replicate", "cerebras", + "deepseek", "gemini", "openrouter", "elevenlabs", @@ -57764,4 +57765,4 @@ } } } -} \ No newline at end of file +} diff --git a/docs/openapi/schemas/inference/common.yaml b/docs/openapi/schemas/inference/common.yaml index 7b0ab29da9..03d8659b0f 100644 --- a/docs/openapi/schemas/inference/common.yaml +++ b/docs/openapi/schemas/inference/common.yaml @@ -19,6 +19,7 @@ ModelProvider: - perplexity - replicate - cerebras + - deepseek - gemini - openrouter - elevenlabs diff --git a/docs/overview.mdx b/docs/overview.mdx index ad5b5b2c90..e5d9c993e2 100644 --- a/docs/overview.mdx +++ b/docs/overview.mdx @@ -224,6 +224,9 @@ Bifrost supports 20+ AI providers through a single unified API. Configure multip OpenAI-compatible with streaming and embeddings. + + OpenAI-compatible chat models with streaming and tool calling. + Grok models with vision and reasoning support. diff --git a/docs/providers/supported-providers/deepseek.mdx b/docs/providers/supported-providers/deepseek.mdx new file mode 100644 index 0000000000..79d21bbcc4 --- /dev/null +++ b/docs/providers/supported-providers/deepseek.mdx @@ -0,0 +1,144 @@ +--- +title: "DeepSeek" +description: "DeepSeek API conversion guide - OpenAI-compatible chat models, streaming, tool calling, and root-based endpoint paths" +icon: "D" +--- + +## Overview + +DeepSeek is an **OpenAI-compatible provider** for chat-oriented models. In Bifrost, the DeepSeek adapter is intentionally conservative in its first pass: + +- **OpenAI-compatible chat support** - Non-streaming and streaming chat completions +- **Responses compatibility** - Implemented through Bifrost's chat-to-responses fallback +- **Model discovery** - Lists models from DeepSeek's `/models` endpoint +- **Tool calling** - Supported through the shared OpenAI-compatible chat handlers +- **Root-based endpoints** - DeepSeek uses `/chat/completions` and `/models` instead of the more common `/v1/*` paths + +### Supported Operations + +| Operation | Non-Streaming | Streaming | Endpoint | +|-----------|---------------|-----------|----------| +| Chat Completions | āœ… | āœ… | `/chat/completions` | +| Responses API | āœ… | āœ… | `/chat/completions` (fallback) | +| List Models | āœ… | - | `/models` | +| Text Completions | āŒ | āŒ | - | +| Embeddings | āŒ | āŒ | - | +| Image Generation | āŒ | āŒ | - | +| Speech (TTS) | āŒ | āŒ | - | +| Transcriptions (STT) | āŒ | āŒ | - | +| Files | āŒ | āŒ | - | +| Batch | āŒ | āŒ | - | + + +**Unsupported Operations** (āŒ): The first DeepSeek adapter intentionally supports only the stable, documented chat/model-listing surface. Legacy text completions in DeepSeek are currently documented as beta/FIM-oriented, so Bifrost does not expose them in this first pass. + + +--- + +# 1. Chat Completions + +DeepSeek chat completions are forwarded through Bifrost's shared OpenAI-compatible chat handlers. + +### Endpoint behavior + +Unlike many other OpenAI-compatible providers, DeepSeek documents **root-based** paths: + +- `/chat/completions` +- `/models` + +So the DeepSeek provider does **not** use `/v1/chat/completions` by default. + +### Parameter handling + +DeepSeek reuses the shared OpenAI-compatible conversion path, so standard chat parameters, streaming, tools, and JSON output follow the same behavior described in [OpenAI Chat Completions](/providers/supported-providers/openai#1-chat-completions). + +DeepSeek-specific beta capabilities that require `https://api.deepseek.com/beta` are not hardcoded into the provider. If you need them, override `network_config.base_url` in your provider config. + +--- + +# 2. Responses API + +Bifrost provides Responses compatibility for DeepSeek by converting a Responses request into a Chat Completions request internally, then converting the result back into a Responses-shaped output. + +``` +BifrostResponsesRequest + → ToChatRequest() + → ChatCompletion + → ToBifrostResponsesResponse() +``` + +Streaming responses use the same fallback strategy on top of DeepSeek chat streaming. + +--- + +# 3. List Models + +DeepSeek model discovery is forwarded to the documented `/models` endpoint and returned through Bifrost's standard model-listing format. + +The currently documented public models include names such as: + +- `deepseek-v4-flash` +- `deepseek-v4-pro` + +`deepseek-chat` and `deepseek-reasoner` are deprecated aliases — they will be retired and inaccessible after July 24, 2026. Migrate to `deepseek-v4-flash` or `deepseek-v4-pro`. + +--- + + + + + +1. Open **Model Providers** → **Configurations** → **DeepSeek**. +2. Add a key using `env.DEEPSEEK_API_KEY`. +3. Pick a model like `deepseek/deepseek-v4-flash` or `deepseek/deepseek-v4-pro`. + + + + + +```bash +curl -X POST http://localhost:8080/v1/chat/completions \ + -H "Content-Type: application/json" \ + -d '{ + "model": "deepseek/deepseek-v4-pro", + "messages": [ + {"role": "user", "content": "Say hello from DeepSeek"} + ] + }' +``` + + + + + +```json +{ + "providers": { + "deepseek": { + "keys": [ + { + "value": "env.DEEPSEEK_API_KEY" + } + ] + } + } +} +``` + + + + + +--- + +## Unsupported Features + +| Feature | Reason | +|---------|--------| +| Text Completion | Kept out of the first pass because the documented DeepSeek `/completions` surface is beta/FIM-oriented | +| Embedding | Not implemented in the first DeepSeek adapter | +| Image Generation | Not implemented in the first DeepSeek adapter | +| Speech/TTS | Not implemented in the first DeepSeek adapter | +| Transcription/STT | Not implemented in the first DeepSeek adapter | +| Batch Operations | Not implemented in the first DeepSeek adapter | +| File Management | Not implemented in the first DeepSeek adapter | diff --git a/docs/providers/supported-providers/overview.mdx b/docs/providers/supported-providers/overview.mdx index 9c4b759d32..05c39a480c 100644 --- a/docs/providers/supported-providers/overview.mdx +++ b/docs/providers/supported-providers/overview.mdx @@ -20,6 +20,7 @@ The following table summarizes which operations are supported by each provider v | Azure (`azure/`) | āœ… | āœ… | āœ… | āœ… | āœ… | āœ… | āœ… | āœ… | āœ… | āœ… | āœ… | āŒ | āœ… | āœ… | āœ… | āœ… | āŒ | āœ… | āœ… | āŒ | āŒ | āŒ | āœ… | āŒ | āŒ | āœ… | āœ… | | Bedrock (`bedrock/`) | āœ… | āœ… | āœ… | āœ… | āœ… | āœ… | āœ… | āœ… | āŒ | āœ… | āŒ | āœ… | āœ… | āŒ | āŒ | āŒ | āŒ | āœ… | āœ… | āœ… | āœ… | āŒ | āŒ | āŒ | āŒ | āŒ | āŒ | | Cerebras (`cerebras/`) | āœ… | āœ… | āœ… | āœ… | āœ… | āœ… | āœ… | āŒ | āŒ | āŒ | āŒ | āŒ | āŒ | āŒ | āŒ | āŒ | āŒ | āŒ | āŒ | āŒ | āŒ | āŒ | āŒ | āŒ | āŒ | āŒ | āŒ | +| DeepSeek (`deepseek/`) | āœ… | āŒ | āŒ | āœ… | āœ… | āœ… | āœ… | āŒ | āŒ | āŒ | āŒ | āŒ | āŒ | āŒ | āŒ | āŒ | āŒ | āŒ | āŒ | āŒ | āŒ | āŒ | āŒ | āŒ | āŒ | āŒ | āŒ | | Cohere (`cohere/`) | āœ… | āŒ | āŒ | āœ… | āœ… | āœ… | āœ… | āŒ | āŒ | āŒ | āŒ | āŒ | āœ… | āŒ | āŒ | āŒ | āŒ | āŒ | āŒ | āœ… | āœ… | āŒ | āŒ | āŒ | āŒ | āŒ | āŒ | | Elevenlabs (`elevenlabs/`) | āœ… | āŒ | āŒ | āŒ | āŒ | āŒ | āŒ | āŒ | āŒ | āŒ | āŒ | āŒ | āŒ | āœ… | āœ… | āœ… | āŒ | āŒ | āŒ | āŒ | āŒ | āŒ | āŒ | āŒ | āŒ | āŒ | āŒ | | Fireworks (`fireworks/`) | āœ… | āœ… | āœ… | āœ… | āœ… | āœ… | āœ… | āŒ | āŒ | āŒ | āŒ | āŒ | āœ… | āŒ | āŒ | āŒ | āŒ | āŒ | āŒ | āŒ | āŒ | āŒ | āŒ | āŒ | āŒ | āŒ | āŒ | diff --git a/transports/config.schema.json b/transports/config.schema.json index 2728b039f9..47616a7bb0 100644 --- a/transports/config.schema.json +++ b/transports/config.schema.json @@ -325,6 +325,9 @@ "cerebras": { "$ref": "#/$defs/provider" }, + "deepseek": { + "$ref": "#/$defs/provider" + }, "vllm": { "$ref": "#/$defs/provider_with_vllm_config" }, diff --git a/ui/lib/constants/config.ts b/ui/lib/constants/config.ts index a3778f093a..479aedcb55 100644 --- a/ui/lib/constants/config.ts +++ b/ui/lib/constants/config.ts @@ -30,6 +30,7 @@ export const ModelPlaceholders = { azure: "e.g. gpt-4, gpt-35-turbo (must match alias keys)", bedrock: "e.g. claude-v2, titan-text-express-v1, ai21-j2-mid", cerebras: "e.g. cerebras-2, cerebras-2-vision", + deepseek: "e.g. deepseek-v4-flash, deepseek-v4-pro", cohere: "e.g. command-r, command-r-plus", gemini: "e.g. gemini-1.5-pro, gemini-1.5-flash", groq: "e.g. llama3-70b-8192, mixtral-8x7b-32768", @@ -56,6 +57,7 @@ export const isKeyRequiredByProvider: Record = { azure: true, bedrock: true, cerebras: true, + deepseek: true, cohere: true, gemini: true, groq: true, @@ -197,4 +199,4 @@ export const PROVIDER_SUPPORTED_REQUESTS: Record = { }; export const IS_ENTERPRISE = process.env.BIFROST_IS_ENTERPRISE === "true"; -export const TRIAL_EXPIRY = parseTrialExpiry(process.env.BIFROST_ENTERPRISE_TRIAL_EXPIRY); \ No newline at end of file +export const TRIAL_EXPIRY = parseTrialExpiry(process.env.BIFROST_ENTERPRISE_TRIAL_EXPIRY); diff --git a/ui/lib/constants/icons.tsx b/ui/lib/constants/icons.tsx index 0ada28866e..08acd05f22 100644 --- a/ui/lib/constants/icons.tsx +++ b/ui/lib/constants/icons.tsx @@ -153,6 +153,26 @@ export const ProviderIcons = { ); }, + deepseek: ({ size = "md", className = "" }: IconProps) => { + const resolvedSize = resolveSize(size); + + return ( + + DeepSeek + + + ); + }, + cohere: ({ size = "md", className = "" }: IconProps) => { const resolvedSize = resolveSize(size); return ( diff --git a/ui/lib/constants/logs.ts b/ui/lib/constants/logs.ts index a4259b4d0b..a833859e28 100644 --- a/ui/lib/constants/logs.ts +++ b/ui/lib/constants/logs.ts @@ -4,6 +4,7 @@ export const KnownProvidersNames = [ "azure", "bedrock", "cerebras", + "deepseek", "cohere", "gemini", "groq", @@ -94,6 +95,7 @@ export const ProviderLabels: Record = { perplexity: "Perplexity", sgl: "SGLang", cerebras: "Cerebras", + deepseek: "DeepSeek", gemini: "Gemini", openrouter: "OpenRouter", huggingface: "HuggingFace", @@ -307,4 +309,4 @@ export const RoutingEngineUsedColors = { "model-catalog": "bg-purple-100 text-purple-800 dark:bg-purple-900 dark:text-purple-300", } as const; -export type Status = (typeof Statuses)[number]; \ No newline at end of file +export type Status = (typeof Statuses)[number];