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
6 changes: 6 additions & 0 deletions .agents/types/agent-definition.ts
Original file line number Diff line number Diff line change
Expand Up @@ -418,6 +418,12 @@ export type ModelName =
| 'deepseek/deepseek-r1-0528'
| 'deepseek/deepseek-r1-0528:nitro'

// Avian (OpenAI-compatible inference API at api.avian.io)
| 'avian/deepseek-v3.2'
| 'avian/kimi-k2.5'
| 'avian/glm-5'
| 'avian/minimax-m2.5'

// Other open source models
| 'moonshotai/kimi-k2'
| 'moonshotai/kimi-k2:nitro'
Expand Down
6 changes: 6 additions & 0 deletions agents/types/agent-definition.ts
Original file line number Diff line number Diff line change
Expand Up @@ -418,6 +418,12 @@ export type ModelName =
| 'deepseek/deepseek-r1-0528'
| 'deepseek/deepseek-r1-0528:nitro'

// Avian (OpenAI-compatible inference API at api.avian.io)
| 'avian/deepseek-v3.2'
| 'avian/kimi-k2.5'
| 'avian/glm-5'
| 'avian/minimax-m2.5'

// Other open source models
| 'moonshotai/kimi-k2'
| 'moonshotai/kimi-k2:nitro'
Expand Down
19 changes: 19 additions & 0 deletions common/src/constants/model-config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { isExplicitlyDefinedModel } from '../util/model-utils'
// Allowed model prefixes for validation
export const ALLOWED_MODEL_PREFIXES = [
'anthropic',
'avian',
'openai',
'google',
'x-ai',
Expand Down Expand Up @@ -51,6 +52,14 @@ export const openrouterModels = {
export type openrouterModel =
(typeof openrouterModels)[keyof typeof openrouterModels]

export const avianModels = {
avian_deepseek_v3_2: 'avian/deepseek-v3.2',
avian_kimi_k2_5: 'avian/kimi-k2.5',
avian_glm_5: 'avian/glm-5',
avian_minimax_m2_5: 'avian/minimax-m2.5',
} as const
export type AvianModel = (typeof avianModels)[keyof typeof avianModels]

export const deepseekModels = {
deepseekChat: 'deepseek-chat',
deepseekReasoner: 'deepseek-reasoner',
Expand Down Expand Up @@ -86,6 +95,7 @@ export type FinetunedVertexModel =

export const models = {
...openaiModels,
...avianModels,
...deepseekModels,
...openrouterModels,
...finetunedVertexModels,
Expand Down Expand Up @@ -114,6 +124,12 @@ export const providerModelNames = {
'openai' as const,
]),
),
...Object.fromEntries(
Object.entries(avianModels).map(([name, model]) => [
model,
'avian' as const,
]),
),
...Object.fromEntries(
Object.entries(openrouterModels).map(([name, model]) => [
model,
Expand Down Expand Up @@ -166,6 +182,7 @@ export function getModelFromShortName(
}

export const providerDomains = {
avian: 'avian.io',
google: 'google.com',
anthropic: 'anthropic.com',
openai: 'chatgpt.com',
Expand All @@ -178,6 +195,8 @@ export function getLogoForModel(modelName: string): string | undefined {

if (Object.values(openaiModels).includes(modelName as OpenAIModel))
domain = providerDomains.openai
else if (Object.values(avianModels).includes(modelName as AvianModel))
domain = providerDomains.avian
else if (Object.values(deepseekModels).includes(modelName as DeepseekModel))
domain = providerDomains.deepseek
else if (modelName.includes('claude')) domain = providerDomains.anthropic
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -418,6 +418,12 @@ export type ModelName =
| 'deepseek/deepseek-r1-0528'
| 'deepseek/deepseek-r1-0528:nitro'

// Avian (OpenAI-compatible inference API at api.avian.io)
| 'avian/deepseek-v3.2'
| 'avian/kimi-k2.5'
| 'avian/glm-5'
| 'avian/minimax-m2.5'

// Other open source models
| 'moonshotai/kimi-k2'
| 'moonshotai/kimi-k2:nitro'
Expand Down
2 changes: 2 additions & 0 deletions packages/internal/src/env-schema.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ export const serverEnvSchema = clientEnvSchema.extend({
OPEN_ROUTER_API_KEY: z.string().min(1),
OPENAI_API_KEY: z.string().min(1),
ANTHROPIC_API_KEY: z.string().min(1),
AVIAN_API_KEY: z.string().min(1).optional(),
FIREWORKS_API_KEY: z.string().min(1),
CANOPYWAVE_API_KEY: z.string().min(1).optional(),
SILICONFLOW_API_KEY: z.string().min(1).optional(),
Expand Down Expand Up @@ -53,6 +54,7 @@ export const serverProcessEnv: ServerInput = {
OPEN_ROUTER_API_KEY: process.env.OPEN_ROUTER_API_KEY,
OPENAI_API_KEY: process.env.OPENAI_API_KEY,
ANTHROPIC_API_KEY: process.env.ANTHROPIC_API_KEY,
AVIAN_API_KEY: process.env.AVIAN_API_KEY,
FIREWORKS_API_KEY: process.env.FIREWORKS_API_KEY,
CANOPYWAVE_API_KEY: process.env.CANOPYWAVE_API_KEY,
SILICONFLOW_API_KEY: process.env.SILICONFLOW_API_KEY,
Expand Down
53 changes: 44 additions & 9 deletions web/src/app/api/v1/chat/completions/_post.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,12 @@ import type { NextRequest } from 'next/server'

import type { ChatCompletionRequestBody } from '@/llm-api/types'

import {
AvianError,
handleAvianNonStream,
handleAvianStream,
isAvianModel,
} from '@/llm-api/avian'
import {
CanopyWaveError,
handleCanopyWaveNonStream,
Expand Down Expand Up @@ -469,11 +475,12 @@ export async function postChatCompletions(params: {
// Handle streaming vs non-streaming
try {
if (bodyStream) {
// Streaming request — route to SiliconFlow/CanopyWave/Fireworks for supported models
// Streaming request — route to provider for supported models
const useSiliconFlow = false // isSiliconFlowModel(typedBody.model)
const useCanopyWave = false // isCanopyWaveModel(typedBody.model)
const useFireworks = isFireworksModel(typedBody.model)
const useOpenAIDirect = !useFireworks && isOpenAIDirectModel(typedBody.model)
const useAvian = isAvianModel(typedBody.model)
const useFireworks = !useAvian && isFireworksModel(typedBody.model)
const useOpenAIDirect = !useAvian && !useFireworks && isOpenAIDirectModel(typedBody.model)
const stream = useSiliconFlow
? await handleSiliconFlowStream({
body: typedBody,
Expand All @@ -494,6 +501,16 @@ export async function postChatCompletions(params: {
logger,
insertMessageBigquery,
})
: useAvian
? await handleAvianStream({
body: typedBody,
userId,
stripeCustomerId,
agentId,
fetch,
logger,
insertMessageBigquery,
})
: useFireworks
? await handleFireworksStream({
body: typedBody,
Expand Down Expand Up @@ -544,13 +561,14 @@ export async function postChatCompletions(params: {
},
})
} else {
// Non-streaming request — route to SiliconFlow/CanopyWave/Fireworks for supported models
// Non-streaming request — route to provider for supported models
// TEMPORARILY DISABLED: route through OpenRouter
const model = typedBody.model
const useSiliconFlow = false // isSiliconFlowModel(model)
const useCanopyWave = false // isCanopyWaveModel(model)
const useFireworks = isFireworksModel(model)
const shouldUseOpenAIEndpoint = !useFireworks && isOpenAIDirectModel(model)
const useAvianNonStream = isAvianModel(model)
const useFireworks = !useAvianNonStream && isFireworksModel(model)
const shouldUseOpenAIEndpoint = !useAvianNonStream && !useFireworks && isOpenAIDirectModel(model)

const nonStreamRequest = useSiliconFlow
? handleSiliconFlowNonStream({
Expand All @@ -572,6 +590,16 @@ export async function postChatCompletions(params: {
logger,
insertMessageBigquery,
})
: useAvianNonStream
? handleAvianNonStream({
body: typedBody,
userId,
stripeCustomerId,
agentId,
fetch,
logger,
insertMessageBigquery,
})
: useFireworks
? handleFireworksNonStream({
body: typedBody,
Expand Down Expand Up @@ -622,6 +650,10 @@ export async function postChatCompletions(params: {
if (error instanceof OpenRouterError) {
openrouterError = error
}
let avianError: AvianError | undefined
if (error instanceof AvianError) {
avianError = error
}
let fireworksError: FireworksError | undefined
if (error instanceof FireworksError) {
fireworksError = error
Expand All @@ -641,7 +673,7 @@ export async function postChatCompletions(params: {

// Log detailed error information for debugging
const errorDetails = openrouterError?.toJSON()
const providerLabel = siliconflowError ? 'SiliconFlow' : canopywaveError ? 'CanopyWave' : fireworksError ? 'Fireworks' : openaiError ? 'OpenAI' : 'OpenRouter'
const providerLabel = avianError ? 'Avian' : siliconflowError ? 'SiliconFlow' : canopywaveError ? 'CanopyWave' : fireworksError ? 'Fireworks' : openaiError ? 'OpenAI' : 'OpenRouter'
logger.error(
{
error: getErrorObject(error),
Expand All @@ -655,8 +687,8 @@ export async function postChatCompletions(params: {
? typedBody.messages.length
: 0,
messages: typedBody.messages,
providerStatusCode: (openrouterError ?? fireworksError ?? canopywaveError ?? siliconflowError ?? openaiError)?.statusCode,
providerStatusText: (openrouterError ?? fireworksError ?? canopywaveError ?? siliconflowError ?? openaiError)?.statusText,
providerStatusCode: (openrouterError ?? avianError ?? fireworksError ?? canopywaveError ?? siliconflowError ?? openaiError)?.statusCode,
providerStatusText: (openrouterError ?? avianError ?? fireworksError ?? canopywaveError ?? siliconflowError ?? openaiError)?.statusText,
openrouterErrorCode: errorDetails?.error?.code,
openrouterErrorType: errorDetails?.error?.type,
openrouterErrorMessage: errorDetails?.error?.message,
Expand All @@ -681,6 +713,9 @@ export async function postChatCompletions(params: {
if (error instanceof OpenRouterError) {
return NextResponse.json(error.toJSON(), { status: error.statusCode })
}
if (error instanceof AvianError) {
return NextResponse.json(error.toJSON(), { status: error.statusCode })
}
if (error instanceof FireworksError) {
return NextResponse.json(error.toJSON(), { status: error.statusCode })
}
Expand Down
Loading