Skip to content
Closed
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
1 change: 1 addition & 0 deletions .github/workflows/e2e.yml
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,7 @@ jobs:
LLM_BYTEDANCE_API_KEY: ${{ secrets.LLM_BYTEDANCE_API_KEY }}
LLM_MINIMAX_API_KEY: ${{ secrets.LLM_MINIMAX_API_KEY }}
LLM_EMBERCLOUD_API_KEY: ${{ secrets.LLM_EMBERCLOUD_API_KEY }}
LLM_RUNWARE_API_KEY: ${{ secrets.LLM_RUNWARE_API_KEY }}
LLM_ALIBABA_API_KEY__US_VIRGINIA: ${{ secrets.LLM_ALIBABA_API_KEY__US_VIRGINIA }}
LLM_ALIBABA_API_KEY__CN_BEIJING: ${{ secrets.LLM_ALIBABA_API_KEY__CN_BEIJING }}
LLM_XIAOMI_API_KEY: ${{ secrets.LLM_XIAOMI_API_KEY }}
Expand Down
117 changes: 59 additions & 58 deletions apps/gateway/src/chat/tools/transform-streaming-to-openai.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,19 +39,19 @@ function normalizeAnthropicUsage(usage: any): any {
...(outputTokens !== undefined && { completion_tokens: outputTokens }),
...(promptTokens !== null &&
outputTokens !== undefined && {
total_tokens: promptTokens + outputTokens,
}),
total_tokens: promptTokens + outputTokens,
}),
...(cacheRead !== null &&
cacheCreation !== null &&
(cacheRead > 0 || cacheCreation > 0) && {
prompt_tokens_details: {
cached_tokens: cacheRead,
...(cacheCreation > 0 && {
cache_write_tokens: cacheCreation,
cache_creation_tokens: cacheCreation,
}),
},
}),
prompt_tokens_details: {
cached_tokens: cacheRead,
...(cacheCreation > 0 && {
cache_write_tokens: cacheCreation,
cache_creation_tokens: cacheCreation,
}),
},
}),
};
return normalizedUsage;
}
Expand Down Expand Up @@ -405,7 +405,7 @@ export function transformStreamingToOpenai(

const promptTokenCount =
typeof usageMetadata.promptTokenCount === "number" &&
usageMetadata.promptTokenCount > 0
usageMetadata.promptTokenCount > 0
? usageMetadata.promptTokenCount
: calculatePromptTokensFromMessages(messagesForFallback);

Expand Down Expand Up @@ -570,10 +570,10 @@ export function transformStreamingToOpenai(
// it represents the thought process that led to the tool call
provider_extra: sig
? {
google: {
thought_signature: sig,
},
}
google: {
thought_signature: sig,
},
}
: undefined,
});

Expand Down Expand Up @@ -662,38 +662,38 @@ export function transformStreamingToOpenai(

const finishChoices = candidates.length
? candidates.map((candidate, candidateIdx) => {
const candidateParts: any[] = candidate?.content?.parts ?? [];
const candidateHasFunctionCalls = candidateParts.some(
(part) => part.functionCall,
);
const finishReason = candidate.finishReason as string | undefined;

return {
index:
typeof candidate.index === "number"
? candidate.index
: candidateIdx,
delta: { role: "assistant" },
finish_reason: mapFinishReasonToOpenai(
finishReason,
usedProvider,
candidateHasFunctionCalls,
promptBlockReason,
),
};
})
const candidateParts: any[] = candidate?.content?.parts ?? [];
const candidateHasFunctionCalls = candidateParts.some(
(part) => part.functionCall,
);
const finishReason = candidate.finishReason as string | undefined;

return {
index:
typeof candidate.index === "number"
? candidate.index
: candidateIdx,
delta: { role: "assistant" },
finish_reason: mapFinishReasonToOpenai(
finishReason,
usedProvider,
candidateHasFunctionCalls,
promptBlockReason,
),
};
})
: [
{
index: 0,
delta: { role: "assistant" },
finish_reason: mapFinishReasonToOpenai(
firstCandidate?.finishReason,
usedProvider,
false,
promptBlockReason,
),
},
];
{
index: 0,
delta: { role: "assistant" },
finish_reason: mapFinishReasonToOpenai(
firstCandidate?.finishReason,
usedProvider,
false,
promptBlockReason,
),
},
];

transformedData = {
id: data.responseId ?? `chatcmpl-${Date.now()}`,
Expand Down Expand Up @@ -1300,18 +1300,18 @@ export function transformStreamingToOpenai(
}),
...(cacheWriteTokens > 0 &&
hasCacheCreationDetails && {
cache_creation: {
ephemeral_5m_input_tokens:
cacheDetails.cacheCreation5mTokens ??
Math.max(
0,
cacheWriteTokens -
(cacheDetails.cacheCreation1hTokens ?? 0),
),
ephemeral_1h_input_tokens:
cacheDetails.cacheCreation1hTokens ?? 0,
},
}),
cache_creation: {
ephemeral_5m_input_tokens:
cacheDetails.cacheCreation5mTokens ??
Math.max(
0,
cacheWriteTokens -
(cacheDetails.cacheCreation1hTokens ?? 0),
),
ephemeral_1h_input_tokens:
cacheDetails.cacheCreation1hTokens ?? 0,
},
}),
},
}),
},
Expand Down Expand Up @@ -1348,6 +1348,7 @@ export function transformStreamingToOpenai(
case "bytedance":
case "minimax":
case "embercloud":
case "runware":
case "xiaomi":
case "azure-ai-foundry":
case "vertex-openai":
Expand Down
12 changes: 8 additions & 4 deletions packages/actions/src/get-provider-endpoint.ts
Original file line number Diff line number Diff line change
Expand Up @@ -356,6 +356,9 @@ export function getProviderEndpoint(
case "embercloud":
url = "https://api.embercloud.ai";
break;
case "runware":
url = "https://api.runware.ai";
break;
case "deepinfra":
url = "https://api.deepinfra.com/v1/openai";
break;
Expand Down Expand Up @@ -486,10 +489,10 @@ export function getProviderEndpoint(

const awsRegionPrefix = region
? (
providers.find((p) => p.id === "aws-bedrock") as
| ProviderDefinition
| undefined
)?.regionConfig?.modelPrefixMap?.[region]
providers.find((p) => p.id === "aws-bedrock") as
| ProviderDefinition
| undefined
)?.regionConfig?.modelPrefixMap?.[region]
: undefined;
// envValueOrDefault honors skipEnvVars (BYOK), so the server's
// LLM_AWS_BEDROCK_REGION can't silently affect provider-key routing.
Expand Down Expand Up @@ -650,6 +653,7 @@ export function getProviderEndpoint(
case "minimax":
case "xiaomi":
case "embercloud":
case "runware":
case "custom":
default:
return `${url}/v1/chat/completions`;
Expand Down
1 change: 1 addition & 0 deletions packages/actions/src/get-provider-headers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,7 @@ export function getProviderHeaders(
case "zai":
case "canopywave":
case "embercloud":
case "runware":
case "deepinfra":
case "custom":
default:
Expand Down
38 changes: 38 additions & 0 deletions packages/models/src/models/alibaba.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1634,6 +1634,44 @@ export const alibabaModels = [
"tools",
],
},
{
providerId: "runware",
externalId: "alibaba:qwen@3.5-397b",
inputPrice: "0.5e-6",
outputPrice: "3.2e-6",
requestPrice: "0",
contextSize: 262144,
maxOutput: 65536,
reasoning: true,
streaming: true,
vision: true,
tools: true,
jsonOutput: true,
},
],
},
{
id: "qwen3.5-27b",
name: "Qwen3.5 27B",
description:
"Compact Qwen3.5 model with reasoning and agent capabilities.",
family: "alibaba",
releasedAt: new Date("2026-02-16"),
providers: [
{
providerId: "runware",
externalId: "alibaba:qwen@3.5-27b",
inputPrice: "0.24e-6",
outputPrice: "2.0e-6",
requestPrice: "0",
contextSize: 262144,
maxOutput: 65536,
reasoning: true,
streaming: true,
vision: true,
tools: true,
jsonOutput: true,
},
],
},
{
Expand Down
55 changes: 55 additions & 0 deletions packages/models/src/models/anthropic.ts
Original file line number Diff line number Diff line change
Expand Up @@ -530,6 +530,20 @@ export const anthropicModels = [
tools: true,
jsonOutputSchema: true,
},
{
providerId: "runware",
externalId: "anthropic:claude@sonnet-4.6",
inputPrice: "3.0e-6",
outputPrice: "15.0e-6",
requestPrice: "0",
contextSize: 1000000,
maxOutput: 64000,
streaming: true,
reasoning: true,
vision: true,
tools: true,
jsonOutput: true,
},
],
},
{
Expand Down Expand Up @@ -601,6 +615,19 @@ export const anthropicModels = [
jsonOutput: true,
jsonOutputSchema: true,
},
{
providerId: "runware",
externalId: "anthropic:claude@haiku-4.5",
inputPrice: "1.0e-6",
outputPrice: "5.0e-6",
requestPrice: "0",
contextSize: 200000,
maxOutput: 64000,
streaming: true,
vision: false,
tools: true,
jsonOutput: true,
},
],
},
{
Expand Down Expand Up @@ -1190,6 +1217,20 @@ export const anthropicModels = [
jsonOutputSchema: true,
supportedParameters: ["temperature", "max_tokens", "top_p", "effort"],
},
{
providerId: "runware",
externalId: "anthropic:claude@opus-4.7",
inputPrice: "5.0e-6",
outputPrice: "25.0e-6",
requestPrice: "0",
contextSize: 1000000,
maxOutput: 128000,
streaming: true,
reasoning: true,
vision: true,
tools: true,
jsonOutput: true,
},
],
},
{
Expand Down Expand Up @@ -1249,6 +1290,20 @@ export const anthropicModels = [
{ id: "au" },
],
},
{
providerId: "runware",
externalId: "anthropic:claude@opus-4.8",
inputPrice: "5.0e-6",
outputPrice: "25.0e-6",
requestPrice: "0",
contextSize: 1000000,
maxOutput: 128000,
streaming: true,
reasoning: true,
vision: true,
tools: true,
jsonOutput: true,
},
],
},
] as const satisfies ModelDefinition[];
30 changes: 30 additions & 0 deletions packages/models/src/models/deepseek.ts
Original file line number Diff line number Diff line change
Expand Up @@ -298,6 +298,21 @@ export const deepseekModels = [
tools: true,
jsonOutput: true,
},
{
providerId: "runware",
externalId: "deepseek:v4@pro",
inputPrice: "1.3e-6",
cachedInputPrice: "0.13e-6",
outputPrice: "2.6e-6",
requestPrice: "0",
contextSize: 1000000,
maxOutput: 393216,
streaming: true,
reasoning: true,
vision: false,
tools: true,
jsonOutput: true,
},
],
},
{
Expand Down Expand Up @@ -388,6 +403,21 @@ export const deepseekModels = [
tools: true,
jsonOutput: true,
},
{
providerId: "runware",
externalId: "deepseek:v4@flash",
inputPrice: "0.14e-6",
cachedInputPrice: "0.014e-6",
outputPrice: "0.28e-6",
requestPrice: "0",
contextSize: 1000000,
maxOutput: 393216,
streaming: true,
reasoning: true,
vision: false,
tools: true,
jsonOutput: true,
},
],
},
] as const satisfies ModelDefinition[];
Loading
Loading