/** * CEL Fields Configuration for Routing Rules * Defines available fields for building routing rule expressions */ import { getProviderLabel } from "@/lib/constants/logs"; export interface CELFieldDefinition { name: string; label: string; placeholder?: string; inputType?: "text" | "select" | "keyValue" | "number"; valueEditorType?: | "text" | "select" | "keyValue" | "number" | "textarea" | "budgetNumber" | ((operator: string) => "text" | "select" | "keyValue" | "number" | "textarea" | "budgetNumber"); operators?: string[]; defaultOperator?: string; defaultValue?: any; values?: Array<{ name: string; label: string; disabled?: boolean }>; metricOptions?: Array<{ name: string; label: string }>; // For budgetNumber type description?: string; // Helpful note for the user } export const baseRoutingFields: CELFieldDefinition[] = [ { name: "model", label: "Model", placeholder: "e.g., gpt-4, claude-3-sonnet", inputType: "text", valueEditorType: (operator: string) => operator === "=" || operator === "!=" ? "select" : operator === "in" || operator === "notIn" ? "select" : "text", operators: ["=", "!=", "in", "notIn", "contains", "beginsWith", "endsWith", "matches"], defaultOperator: "=", }, { name: "provider", label: "Provider", placeholder: "Select provider", inputType: "select", valueEditorType: (operator: string) => operator === "matches" ? "text" : operator === "in" || operator === "notIn" ? "select" : "select", operators: ["=", "!=", "in", "notIn", "matches", "null", "notNull"], defaultOperator: "=", }, { name: "request_type", label: "Request Type", placeholder: "Select request type", inputType: "select", valueEditorType: (operator: string) => operator === "matches" ? "text" : operator === "in" || operator === "notIn" ? "select" : "select", operators: ["=", "!=", "in", "notIn", "matches"], defaultOperator: "=", values: [ { name: "text_completion", label: "Text Completion" }, { name: "chat_completion", label: "Chat Completion" }, { name: "responses", label: "Responses" }, { name: "embedding", label: "Embeddings" }, { name: "image_generation", label: "Image Generation" }, { name: "image_edit", label: "Image Edit" }, { name: "image_variation", label: "Image Variation" }, { name: "speech", label: "Speech" }, { name: "transcription", label: "Transcription" }, { name: "count_tokens", label: "Count Tokens" }, { name: "rerank", label: "Rerank" }, { name: "video_generation", label: "Video Generation" }, ], description: "Filter rules by the type of API request (chat, text, embeddings, images, audio, etc.)", }, { name: "headers", label: "Header", placeholder: "e.g., authorization, x-custom-header (use lowercase)", inputType: "keyValue", valueEditorType: "keyValue", operators: ["=", "!=", "contains", "beginsWith", "endsWith", "matches", "null", "notNull"], defaultOperator: "=", }, { name: "tokens_used", label: "Tokens Used (%)", placeholder: "e.g., 80", inputType: "text", valueEditorType: "number", operators: ["=", "!=", ">", "<", ">=", "<="], defaultOperator: ">=", description: "Check token usage as percentage. Checked against max of model and provider configs.", }, { name: "request", label: "Request (%)", placeholder: "e.g., 80", inputType: "text", valueEditorType: "number", operators: ["=", "!=", ">", "<", ">=", "<="], defaultOperator: ">=", description: "Check request usage as percentage. Checked against max of model and provider configs.", }, { name: "budget_used", label: "Budget Used (%)", placeholder: "e.g., 50", inputType: "text", valueEditorType: "number", operators: ["=", "!=", ">", "<", ">=", "<="], defaultOperator: ">=", description: "Check budget usage as percentage. Checked against max of model and provider configs.", }, { name: "params", label: "Query Parameter", placeholder: "e.g., api_key, user_id", inputType: "keyValue", valueEditorType: "keyValue", operators: ["=", "!=", "contains", "beginsWith", "endsWith", "matches", "null", "notNull"], defaultOperator: "=", }, ]; /** * Get routing fields with dynamic providers and models * Provider field values are populated dynamically from available providers * Metric options for rate limits and budget are populated from available providers and models */ export function getRoutingFields(providers: string[] = [], models: string[] = []): CELFieldDefinition[] { // Create provider field values const providerValues = providers.length > 0 ? providers.map((provider) => ({ name: provider, label: getProviderLabel(provider), })) : [{ name: "_no_providers", label: "No providers configured", disabled: true }]; // Create model field values const modelValues = models.length > 0 ? models.map((model) => ({ name: model, label: model, })) : []; // Create metric options for scope input: providers + models const scopeOptions = [ { name: "", label: "(provider-level)" }, // Empty scope for provider-level ...providers.map((provider) => ({ name: provider, label: `${provider} (provider)`, })), ...models.map((model) => ({ name: model, label: `${model} (model)`, })), ]; // Update provider field with dynamic values and rate limit/budget fields with scope options const fieldsWithDynamicValues = baseRoutingFields.map((field) => { if (field.name === "provider") { return { ...field, values: providerValues, }; } if (field.name === "model") { return { ...field, values: modelValues, }; } if (field.name === "tokens_used" || field.name === "request" || field.name === "budget_used") { return { ...field, metricOptions: scopeOptions, }; } return field; }); return fieldsWithDynamicValues; } export const PROVIDER_DISPLAY_NAMES: Record = { openai: "OpenAI", anthropic: "Anthropic", azure: "Azure OpenAI", gemini: "Google Gemini", vertex: "Vertex AI", cohere: "Cohere", };