feat: add Vercel AI Gateway provider with pricing support#689
feat: add Vercel AI Gateway provider with pricing support#689naaa760 wants to merge 1 commit intoMerit-Systems:masterfrom
Conversation
|
@naaa760 is attempting to deploy a commit to the Merit Systems Team on Vercel. A member of the Team first needs to authorize it. |
| const cost = getCostPerToken( | ||
| this.getModel(), | ||
| prompt_tokens, | ||
| completion_tokens | ||
| ); |
There was a problem hiding this comment.
The handleChatCompletionResponse method will throw an UnknownModelError when processing responses for provider-prefixed models (e.g., "openai/gpt-4", "anthropic/claude-3") because it attempts to look up pricing for the full prefixed model name, which doesn't exist in the pricing database.
View Details
📝 Patch Details
diff --git a/packages/app/server/src/providers/VercelAIGatewayProvider.ts b/packages/app/server/src/providers/VercelAIGatewayProvider.ts
index aefc3062..39bf3e5f 100644
--- a/packages/app/server/src/providers/VercelAIGatewayProvider.ts
+++ b/packages/app/server/src/providers/VercelAIGatewayProvider.ts
@@ -10,6 +10,19 @@ import { Decimal } from '@prisma/client/runtime/library';
export class VercelAIGatewayProvider extends BaseProvider {
private readonly VERCEL_AI_GATEWAY_BASE_URL = 'https://ai-gateway.vercel.sh/v1';
+ /**
+ * Extract base model name from provider-prefixed model (e.g., "openai/gpt-4" -> "gpt-4")
+ * Returns the original model if no prefix is found
+ */
+ private getBaseModelName(): string {
+ const model = this.getModel();
+ if (model.includes('/')) {
+ const [, ...modelParts] = model.split('/');
+ return modelParts.join('/');
+ }
+ return model;
+ }
+
getType(): ProviderType {
return ProviderType.VERCEL_AI_GATEWAY;
}
@@ -81,7 +94,7 @@ export class VercelAIGatewayProvider extends BaseProvider {
}
const cost = getCostPerToken(
- this.getModel(),
+ this.getBaseModelName(),
prompt_tokens,
completion_tokens
);
@@ -110,17 +123,18 @@ export class VercelAIGatewayProvider extends BaseProvider {
let cost = new Decimal(0);
let metadata: LlmTransactionMetadata;
const model = this.getModel();
+ const baseModel = this.getBaseModelName();
- const modelPrice = getModelPrice(model);
+ const modelPrice = getModelPrice(baseModel);
if (endpointType === 'transcription') {
try {
const transcriptionData = JSON.parse(data);
const text = transcriptionData.text || '';
- if (modelPrice && isValidModel(model)) {
+ if (modelPrice && isValidModel(baseModel)) {
const textTokens = Math.ceil(text.length / 4);
- cost = getCostPerToken(model, 0, textTokens);
+ cost = getCostPerToken(baseModel, 0, textTokens);
} else {
cost = new Decimal(0.01);
}
@@ -135,7 +149,7 @@ export class VercelAIGatewayProvider extends BaseProvider {
};
} catch (error) {
logger.error(`Error parsing transcription response: ${error}`);
- cost = modelPrice && isValidModel(model) ? new Decimal(0) : new Decimal(0.01);
+ cost = modelPrice && isValidModel(baseModel) ? new Decimal(0) : new Decimal(0.01);
metadata = {
providerId: 'transcription',
provider: this.getType(),
@@ -149,9 +163,9 @@ export class VercelAIGatewayProvider extends BaseProvider {
const inputText = (requestBody?.input as string) || '';
const characterCount = inputText.length;
- if (modelPrice && isValidModel(model)) {
+ if (modelPrice && isValidModel(baseModel)) {
const inputTokens = Math.ceil(characterCount / 4);
- cost = getCostPerToken(model, inputTokens, 0);
+ cost = getCostPerToken(baseModel, inputTokens, 0);
} else {
const costPerCharacter = new Decimal(0.000015);
cost = costPerCharacter.mul(characterCount);
@@ -166,7 +180,7 @@ export class VercelAIGatewayProvider extends BaseProvider {
totalTokens: characterCount,
};
} else {
- cost = modelPrice && isValidModel(model) ? new Decimal(0) : new Decimal(0.01);
+ cost = modelPrice && isValidModel(baseModel) ? new Decimal(0) : new Decimal(0.01);
metadata = {
providerId: 'audio',
provider: this.getType(),
@@ -184,4 +198,3 @@ export class VercelAIGatewayProvider extends BaseProvider {
};
}
}
-
Analysis
VercelAIGatewayProvider throws UnknownModelError for provider-prefixed models
What fails: VercelAIGatewayProvider.handleChatCompletionResponse() calls getCostPerToken(this.getModel(), ...) with provider-prefixed model names like "openai/gpt-4", but pricing database only contains base model names like "gpt-4"
How to reproduce:
// Create provider with prefixed model and process a chat completion response
const provider = new VercelAIGatewayProvider(false, 'openai/gpt-4');
const mockResponse = JSON.stringify({
usage: { prompt_tokens: 10, completion_tokens: 10, total_tokens: 20 }
});
provider.handleBody(mockResponse); // Throws UnknownModelErrorResult: getCostPerToken() calls isValidModel("openai/gpt-4") which returns false, then throws UnknownModelError: Invalid model: openai/gpt-4
Expected: Should extract base model "gpt-4" for pricing lookup per Vercel AI Gateway docs which confirm "creator/model-name" format is correct
Note: Same issue affects handleAudioResponse() method for transcription and speech endpoints
Changes:
VercelAIGatewayProviderclassfix: #573