Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
64 changes: 64 additions & 0 deletions src/shared/providers/definitions/eden-ai-responses.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
import { ModelProviderEnum, ModelProviderType } from '../../types'
import { defineProvider } from '../registry'
import EdenAIResponses from './models/eden-ai-responses'

// Eden AI Responses API: a stateful, OpenAI-compatible alternative to chat
// completions that stores conversation history server-side.
// Docs: https://www.edenai.co/docs/api-reference/responses/create-response
export const edenAIResponsesProvider = defineProvider({
id: ModelProviderEnum.EdenAIResponses,
name: 'Eden AI (Responses)',
type: ModelProviderType.OpenAIResponses,
description: 'eden-ai-responses',
curatedModelIds: ['@edenai', 'openai/gpt-4o', 'anthropic/claude-sonnet-4-5'],
urls: {
website: 'https://www.edenai.co/',
apiKey: 'https://app.edenai.run/admin/api-settings/features-preferences',
docs: 'https://www.edenai.co/docs/api-reference/responses/create-response',
},
defaultSettings: {
apiHost: 'https://api.edenai.run/v3',
apiPath: '/responses',
models: [
{
modelId: '@edenai',
nickname: 'Eden AI (auto-router)',
capabilities: ['tool_use'],
contextWindow: 128_000,
},
{
modelId: 'openai/gpt-4o',
nickname: 'GPT-4o',
capabilities: ['vision', 'tool_use'],
contextWindow: 128_000,
maxOutput: 16_384,
},
{
modelId: 'anthropic/claude-sonnet-4-5',
nickname: 'Claude Sonnet 4.5',
capabilities: ['vision', 'tool_use', 'reasoning'],
contextWindow: 200_000,
maxOutput: 64_000,
},
],
},
createModel: (config) => {
return new EdenAIResponses(
{
apiKey: config.providerSetting.apiKey || '',
apiHost: config.formattedApiHost || 'https://api.edenai.run/v3',
apiPath: config.formattedApiPath || '/responses',
model: config.model,
temperature: config.settings.temperature,
topP: config.settings.topP,
maxOutputTokens: config.settings.maxTokens,
stream: config.settings.stream,
useProxy: config.providerSetting.useProxy,
},
config.dependencies
)
},
getDisplayName: (modelId, providerSettings) => {
return `Eden AI Responses (${providerSettings?.models?.find((m) => m.modelId === modelId)?.nickname || modelId})`
},
})
89 changes: 89 additions & 0 deletions src/shared/providers/definitions/eden-ai.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
import { ModelProviderEnum, ModelProviderType } from '../../types'
import { defineProvider } from '../registry'
import EdenAI from './models/eden-ai'

// Eden AI exposes an OpenAI-compatible Chat Completions endpoint that routes to
// 100+ models from many providers. Model ids use the `provider/model` format,
// e.g. `openai/gpt-4o`. The special `@edenai` id auto-routes to the best model.
// Docs: https://www.edenai.co/docs/api-reference/chat/chat-completions
export const edenAIProvider = defineProvider({
id: ModelProviderEnum.EdenAI,
name: 'Eden AI',
type: ModelProviderType.OpenAI,
description: 'eden-ai',
curatedModelIds: [
'@edenai',
'openai/gpt-4o',
'openai/gpt-4o-mini',
'anthropic/claude-sonnet-4-5',
'google/gemini-2.5-pro',
'mistral/mistral-large-latest',
],
urls: {
website: 'https://www.edenai.co/',
apiKey: 'https://app.edenai.run/admin/api-settings/features-preferences',
docs: 'https://www.edenai.co/docs/api-reference/chat/chat-completions',
},
defaultSettings: {
apiHost: 'https://api.edenai.run/v3',
models: [
{
modelId: '@edenai',
nickname: 'Eden AI (auto-router)',
capabilities: ['tool_use'],
contextWindow: 128_000,
},
{
modelId: 'openai/gpt-4o',
nickname: 'GPT-4o',
capabilities: ['vision', 'tool_use'],
contextWindow: 128_000,
maxOutput: 16_384,
},
{
modelId: 'openai/gpt-4o-mini',
nickname: 'GPT-4o mini',
capabilities: ['vision', 'tool_use'],
contextWindow: 128_000,
maxOutput: 16_384,
},
{
modelId: 'anthropic/claude-sonnet-4-5',
nickname: 'Claude Sonnet 4.5',
capabilities: ['vision', 'tool_use', 'reasoning'],
contextWindow: 200_000,
maxOutput: 64_000,
},
{
modelId: 'google/gemini-2.5-pro',
nickname: 'Gemini 2.5 Pro',
capabilities: ['vision', 'tool_use', 'reasoning'],
contextWindow: 1_048_576,
maxOutput: 65_536,
},
{
modelId: 'mistral/mistral-large-latest',
nickname: 'Mistral Large',
capabilities: ['tool_use'],
contextWindow: 128_000,
maxOutput: 8_192,
},
],
},
createModel: (config) => {
return new EdenAI(
{
apiKey: config.providerSetting.apiKey || '',
model: config.model,
temperature: config.settings.temperature,
topP: config.settings.topP,
maxOutputTokens: config.settings.maxTokens,
stream: config.settings.stream,
},
config.dependencies
)
},
getDisplayName: (modelId, providerSettings) => {
return `Eden AI (${providerSettings?.models?.find((m) => m.modelId === modelId)?.nickname || modelId})`
},
})
86 changes: 86 additions & 0 deletions src/shared/providers/definitions/models/eden-ai-responses.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
import { createOpenAI } from '@ai-sdk/openai'
import { extractReasoningMiddleware, wrapLanguageModel } from 'ai'
import AbstractAISDKModel from '../../../models/abstract-ai-sdk'
import { fetchRemoteModels } from '../../../models/openai-compatible'
import type { CallChatCompletionOptions } from '../../../models/types'
import { createFetchWithProxy } from '../../../models/utils/fetch-proxy'
import type { ProviderModelInfo } from '../../../types'
import type { ModelDependencies } from '../../../types/adapters'

interface Options {
apiKey: string
apiHost: string
apiPath: string
model: ProviderModelInfo
temperature?: number
topP?: number
maxOutputTokens?: number
stream?: boolean
useProxy?: boolean
}

type FetchFunction = typeof globalThis.fetch

// Eden AI's Responses endpoint is OpenAI-compatible (stateful alternative to chat
// completions). Unlike OpenAI's `https://api.openai.com` host, Eden AI keeps the
// version segment in the path (`https://api.edenai.run/v3/responses`), so we route
// requests explicitly to `${apiHost}${apiPath}` instead of using the shared
// `normalizeOpenAIResponsesHostAndPath` helper (which would append `/v1`).
// Docs: https://www.edenai.co/docs/api-reference/responses/create-response
export default class EdenAIResponses extends AbstractAISDKModel {
public name = 'Eden AI Responses'

constructor(
public options: Options,
dependencies: ModelDependencies
) {
super(options, dependencies)
}

protected getCallSettings() {
return {
temperature: this.options.temperature,
topP: this.options.topP,
maxOutputTokens: this.options.maxOutputTokens,
stream: this.options.stream,
}
}

static isSupportTextEmbedding() {
return true
}

protected getProvider(_options: CallChatCompletionOptions, fetchFunction?: FetchFunction) {
return createOpenAI({
apiKey: this.options.apiKey,
baseURL: this.options.apiHost,
fetch: fetchFunction,
})
}

protected getChatModel(options: CallChatCompletionOptions) {
const { apiHost, apiPath } = this.options
const provider = this.getProvider(options, (_input, init) =>
createFetchWithProxy(this.options.useProxy, this.dependencies)(`${apiHost}${apiPath}`, init)
)
return wrapLanguageModel({
model: provider.responses(this.options.model.modelId),
middleware: extractReasoningMiddleware({ tagName: 'think' }),
})
}

public listModels() {
return fetchRemoteModels(
{
apiHost: this.options.apiHost,
apiKey: this.options.apiKey,
useProxy: this.options.useProxy,
},
this.dependencies
)
}

protected getImageModel() {
return null
}
}
28 changes: 28 additions & 0 deletions src/shared/providers/definitions/models/eden-ai.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import OpenAICompatible, { type OpenAICompatibleSettings } from '../../../models/openai-compatible'
import type { ModelDependencies } from '../../../types/adapters'

interface Options extends OpenAICompatibleSettings {}

export default class EdenAI extends OpenAICompatible {
public name = 'Eden AI'
public options: Options
constructor(options: Omit<Options, 'apiHost'>, dependencies: ModelDependencies) {
const apiHost = 'https://api.edenai.run/v3'
super(
{
apiKey: options.apiKey,
apiHost,
model: options.model,
temperature: options.temperature,
topP: options.topP,
maxOutputTokens: options.maxOutputTokens,
stream: options.stream,
},
dependencies
)
this.options = {
...options,
apiHost,
}
}
}
2 changes: 2 additions & 0 deletions src/shared/providers/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@ import './definitions/minimax'
import './definitions/moonshot'
import './definitions/siliconflow'
import './definitions/openrouter'
import './definitions/eden-ai'
import './definitions/eden-ai-responses'
import './definitions/ollama'
import './definitions/lmstudio'
import './definitions/azure'
Expand Down
2 changes: 2 additions & 0 deletions src/shared/types/provider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,8 @@ export enum ModelProviderEnum {
Perplexity = 'perplexity',
XAI = 'xAI',
OpenRouter = 'openrouter',
EdenAI = 'eden-ai',
EdenAIResponses = 'eden-ai-responses',
Bedrock = 'bedrock',
Custom = 'custom',
}
Expand Down