跳转至主要内容
版本: v0.1

Router API 参考

语义路由(Semantic Router)提供了一个基于 gRPC 的 API,可与 Envoy 的外部处理(ExtProc)协议无缝集成。本文档涵盖了 API 端点、请求/响应格式以及集成模式。

API 概览

语义路由作为一个 ExtProc 服务器运行,通过 Envoy 代理处理 HTTP 请求。它不直接暴露 REST 端点,而是处理通过 Envoy 路由的 OpenAI 兼容 API 请求。

注意:除了 ExtProc 路径外,该项目还在 8080 端口启动了一个轻量级 HTTP 分类 API,用于健康检查/信息查询及分类实用程序。OpenAI 兼容的 /v1/models 端点由该 HTTP API (8080) 提供,并可以通过路由规则选择性地通过 Envoy (8801) 暴露。

端口与端点映射

  • 8801 (HTTP, Envoy 公共入口)

    • 针对 OpenAI 兼容请求(如 POST /v1/chat/completions)的典型客户端入口。
    • 如果您添加了 Envoy 路由,可以将 GET /v1/models 代理到路由器的 8080 端口;否则 8801 端口上的 /v1/models 可能会返回 “no healthy upstream”。
  • 8080 (HTTP, 分类 API)

    • GET /v1/models → OpenAI 兼容的模型列表(包含合成模型 MoM
    • GET /health → 分类 API 健康状况
    • GET /info/models → 已加载的分类器模型 + 系统信息
    • GET /info/classifier → 分类器配置详情
    • POST /api/v1/classify/intent|pii|security|batch → 直接分类实用工具
  • 50051 (gRPC, ExtProc)

    • Envoy 外部处理 (ExtProc),用于 /v1/chat/completions 的路径内分类/路由。
    • 非 HTTP 端口;无法通过 curl 直接访问。
  • 9190 (HTTP, Prometheus 指标)

    • GET /metrics → Prometheus 抓取端点(全局进程指标)。

请求流程

OpenAI API 兼容性

路由器处理标准的 OpenAI API 请求

模型端点

列出可用模型,并包含一个合成的 "MoM" (Mixture of Models) 模型。该模型使用路由器的意图分类功能为每个请求选择最佳的底层模型。

  • 端点:GET /v1/models
  • 响应
{
"object": "list",
"data": [
{ "id": "MoM", "object": "model", "created": 1726890000, "owned_by": "semantic-router" },
{ "id": "gpt-4o-mini", "object": "model", "created": 1726890000, "owned_by": "upstream-endpoint" },
{ "id": "llama-3.1-8b-instruct", "object": "model", "created": 1726890000, "owned_by": "upstream-endpoint" }
]
}

备注

  • 具体的模型列表来源于您在 config.yaml 中配置的 vLLM 端点(参见 vllm_endpoints[].models)。
  • 特殊的 MoM (Mixture of Models) 模型始终存在,并指示路由器自动分类并路由到最佳后端模型。为了向后兼容,模型名称 auto 也被接受作为别名。

聊天补全端点

端点: POST /v1/chat/completions

请求格式

{
"model": "gpt-3.5-turbo",
"messages": [
{
"role": "user",
"content": "What is the derivative of x^2?"
}
],
"max_tokens": 150,
"temperature": 0.7,
"tools": [
{
"type": "function",
"function": {
"name": "calculator",
"description": "Perform mathematical calculations"
}
}
]
}

响应格式

{
"id": "chatcmpl-abc123",
"object": "chat.completion",
"created": 1677858242,
"model": "gpt-3.5-turbo",
"choices": [
{
"index": 0,
"message": {
"role": "assistant",
"content": "The derivative of x^2 is 2x."
},
"finish_reason": "stop"
}
],
"usage": {
"prompt_tokens": 12,
"completion_tokens": 8,
"total_tokens": 20
},
"routing_metadata": {
"selected_model": "mathematics",
"confidence": 0.96,
"processing_time_ms": 15,
"cache_hit": false,
"security_checks": {
"pii_detected": false,
"jailbreak_detected": false
}
}
}

路由请求头

路由器向请求和响应中添加元数据头

请求头(由路由器添加)

请求头名称描述示例
x-vsr-destination-endpoint所选后端端点endpoint1
x-selected-model确定的模型类别mathematics
x-routing-confidence分类置信度0.956
x-request-id唯一请求标识符req-abc123
x-cache-status缓存命中/未命中状态miss

响应头(由路由器添加)

请求头名称描述示例
x-processing-time总处理时间 (ms)45
x-classification-time分类时间 (ms)12
x-security-checks安全检查结果pii:false,jailbreak:false
x-tools-selected选择的工具数量2

健康检查 API

路由器提供用于监控的健康检查端点

路由器健康状况

端点: GET https://:8080/health

{
"status": "healthy",
"version": "1.0.0",
"uptime": 3600,
"models": {
"category_classifier": "loaded",
"pii_detector": "loaded",
"jailbreak_guard": "loaded"
},
"cache": {
"status": "healthy",
"entries": 1247,
"hit_rate": 0.73
},
"endpoints": {
"endpoint1": "healthy",
"endpoint2": "healthy",
"endpoint3": "degraded"
}
}

指标 API

提供兼容 Prometheus 的指标

端点: GET https://:9090/metrics

关键指标

# Request metrics
semantic_router_requests_total{endpoint="endpoint1",category="mathematics",status="success"} 1247
semantic_router_request_duration_seconds{endpoint="endpoint1"} 0.045

# Classification metrics
semantic_router_classification_accuracy{category="mathematics"} 0.94
semantic_router_classification_duration_seconds 0.012

# Cache metrics
semantic_router_cache_hit_ratio 0.73
semantic_router_cache_size 1247

# Security metrics
semantic_router_pii_detections_total{action="block"} 23
semantic_router_jailbreak_attempts_total{action="block"} 5

# Error metrics
llm_request_errors_total{model="gpt-4",reason="timeout"} 12
llm_request_errors_total{model="claude-3",reason="upstream_5xx"} 3
llm_request_errors_total{model="phi4",reason="upstream_4xx"} 5
llm_request_errors_total{model="phi4",reason="pii_policy_denied"} 8

推理模式指标

路由器暴露了专用的 Prometheus 计数器,用于监控各模型系列的推理模式决策和模板使用情况。这些指标由路由器发出,可由您的 Prometheus 服务器抓取。

  • llm_reasoning_decisions_total{category, model, enabled, effort}

    • 描述:按类别和选定模型统计的推理决策计数,包括是否启用推理以及应用的努力程度(effort level)。
    • 标签
      • category: 路由过程中确定的类别名称
      • model: 请求最终选用的模型
      • enabled: 根据决策结果为 "true" 或 "false"
      • effort: 启用时使用的努力程度(例如:low|medium|high)
  • llm_reasoning_template_usage_total{family, param}

    • 描述:模型系列特定模板参数应用于请求的次数统计。
    • 标签
      • family: 归一化的模型系列(例如:qwen3, deepseek, gpt-oss, gpt)
      • param: 应用的模板参数名称(例如:enable_thinking, thinking, reasoning_effort)
  • llm_reasoning_effort_usage_total{family, effort}

    • 描述:为给定模型系列设置推理努力程度的次数统计。
    • 标签
      • family: 归一化的模型系列
      • effort: 努力程度(例如:low|medium|high)

PromQL 示例

# Reasoning decisions by category and model (last 5m)
sum by (category, model, enabled, effort) (
rate(llm_reasoning_decisions_total[5m])
)

# Template usage by model family and parameter (last 5m)
sum by (family, param) (
rate(llm_reasoning_template_usage_total[5m])
)

# Effort distribution by model family (last 5m)
sum by (family, effort) (
rate(llm_reasoning_effort_usage_total[5m])
)

成本与路由指标

路由器暴露了额外的指标,用于成本核算和路由决策。

  • llm_model_cost_total{model, currency}

    • 描述:归属于每个模型的累计总成本(根据 token 使用情况和每百万 token 价格计算),按货币单位标记。
    • 标签
      • model: 请求使用的模型名称
      • currency: 货币代码(例如:"USD")
  • llm_routing_reason_codes_total{reason_code, model}

    • 描述:按原因代码和所选模型统计的路由决策计数。
    • 标签
      • reason_code: 路由决策发生的原因(例如:auto_routing, model_specified, pii_policy_alternative_selected)
      • model: 最终选定的模型

PromQL 示例

# Cost by model and currency over the last hour
sum by (model, currency) (increase(llm_model_cost_total[1h]))

# Or, if you only use USD, a common query is:
sum by (model) (increase(llm_model_cost_total{currency="USD"}[1h]))

# Routing decisions by reason code over the last 15 minutes
sum by (reason_code) (increase(llm_routing_reason_codes_total[15m]))

请求错误指标

路由器按模型和原因跟踪请求级故障,以便您可以监控绝对错误吞吐量和失败请求的占比。

  • llm_request_errors_total{model, reason}
    • 描述:按失败原因分类的请求错误总数
    • 标签
      • model: 失败请求的目标模型名称
      • reason: 错误类别(timeout, upstream_4xx, upstream_5xx, pii_policy_denied, jailbreak_block, parse_error, serialization_error, cancellation, classification_failed, unknown)

PromQL 查询示例

# Total errors by reason over the last hour
sum by (reason) (increase(llm_request_errors_total[1h]))

# Error throughput (errors/sec) by model over the last 15 minutes.
# Helpful for incident response because it shows how many failing requests are impacting users.
sum by (model) (rate(llm_request_errors_total[15m]))

# Error ratio (% of requests failing) by model over the last 15 minutes.
# Use increase() to align numerator and denominator with the same lookback window.
100 * sum by (model) (increase(llm_request_errors_total[15m])) /
sum by (model) (increase(llm_model_requests_total[15m]))

# PII policy blocks over the last 24 hours
sum(increase(llm_request_errors_total{reason="pii_policy_denied"}[24h]))

TTFT 和 TPOT 指标

首字延迟 (TTFT) 和逐字延迟 (TPOT) 作为 Prometheus 直方图导出,并可以通过 histogram_quantile 在 p95 级别进行可视化。

  • llm_model_ttft_seconds{model}
    • 直方图:暴露 _bucket, _sum, _count
    • 描述:自路由器开始处理请求起的首字生成时间
    • 按模型的 p95 示例(最近 5 分钟)
histogram_quantile(0.95, sum(rate(llm_model_ttft_seconds_bucket[5m])) by (le, model))
  • llm_model_tpot_seconds{model}
    • 直方图:暴露 _bucket, _sum, _count
    • 描述:每个输出 token 的秒数(补全延迟 / 补全 token 数)
    • 按模型的 p95 示例(最近 5 分钟)
histogram_quantile(0.95, sum(rate(llm_model_tpot_seconds_bucket[5m])) by (le, model))

这些指标已包含在提供的 Grafana 仪表板中(位于 deploy/llm-router-dashboard.json),显示为 “TTFT (p95) by Model” 和 “TPOT (p95) by Model (sec/token)”。

流式传输 (SSE) 注意事项

  • 对于服务器发送事件 (SSE) 响应,路由器测量第一个流式正文块(即第一个 token)的 TTFT,而不是响应头的时间。
  • 无需手动更改 Envoy 配置:ExtProc 处理器会自动为 SSE 响应设置 response_body_mode: STREAMED 的模式覆盖,以便第一个数据块立即到达 ExtProc。
  • 先决条件:Envoy 的 ext_proc 过滤器必须设置 allow_mode_override: trueconfig/envoy.yamlconfig/envoy-docker.yaml 中的默认配置已包含此项)。在静态处理模式中保留 response_body_mode: BUFFERED 是可以的;路由器会在运行时针对 SSE 将其切换为 STREAMED。

计价配置

为您的模型提供每百万 token 的计价,以便路由器计算请求成本并发出指标/日志。

model_config:
phi4:
pricing:
currency: USD
prompt_per_1m: 0.07
completion_per_1m: 0.35
"mistral-small3.1":
pricing:
currency: USD
prompt_per_1m: 0.1
completion_per_1m: 0.3
gemma3:27b:
pricing:
currency: USD
prompt_per_1m: 0.067
completion_per_1m: 0.267

备注

  • 计价是可选的;如果省略,成本将被视为 0,并且仅发出 token 指标。
  • 成本计算公式为:(prompt_tokens * prompt_per_1m + completion_tokens * completion_per_1m) / 1,000,000(以配置的货币为单位)。

gRPC ExtProc API

用于与 ExtProc 协议直接集成

服务定义

syntax = "proto3";

package envoy.service.ext_proc.v3;

service ExternalProcessor {
rpc Process(stream ProcessingRequest) returns (stream ProcessingResponse);
}

message ProcessingRequest {
oneof request {
RequestHeaders request_headers = 1;
RequestBody request_body = 2;
ResponseHeaders response_headers = 3;
ResponseBody response_body = 4;
}
}

message ProcessingResponse {
oneof response {
RequestHeadersResponse request_headers = 1;
RequestBodyResponse request_body = 2;
ResponseHeadersResponse response_headers = 3;
ResponseBodyResponse response_body = 4;
}
}

处理方法

请求头处理

func (r *Router) handleRequestHeaders(headers *ProcessingRequest_RequestHeaders) *ProcessingResponse {
// Extract request metadata
// Set up request context
// Return continue response
}

请求体处理

func (r *Router) handleRequestBody(body *ProcessingRequest_RequestBody) *ProcessingResponse {
// Parse OpenAI request
// Classify query intent
// Run security checks
// Select optimal model
// Return routing headers
}

错误处理

错误响应格式

{
"error": {
"message": "PII detected in request",
"type": "security_violation",
"code": "pii_detected",
"details": {
"entities_found": ["EMAIL", "PERSON"],
"action_taken": "block"
}
}
}

HTTP 状态码

状态描述
200成功
400错误请求(输入格式错误)
403禁止(安全违规)
429请求过多(频率限制)
500内部服务器错误
503服务不可用(后端宕机)

配置 API

运行时配置更新

端点: POST /admin/config/update

{
"classification": {
"confidence_threshold": 0.8
},
"security": {
"enable_pii_detection": true
},
"cache": {
"ttl_seconds": 7200
}
}

WebSocket API (可选)

用于实时流式响应

端点: ws://:8801/v1/chat/stream

const ws = new WebSocket('ws://:8801/v1/chat/stream');

ws.send(JSON.stringify({
"model": "gpt-3.5-turbo",
"messages": [{"role": "user", "content": "Tell me a story"}],
"stream": true
}));

ws.onmessage = function(event) {
const chunk = JSON.parse(event.data);
console.log(chunk.choices[0].delta.content);
};

客户端库

Python 客户端

import requests

class SemanticRouterClient:
def __init__(self, base_url="https://:8801"):
self.base_url = base_url

def chat_completion(self, messages, model="gpt-3.5-turbo", **kwargs):
response = requests.post(
f"{self.base_url}/v1/chat/completions",
json={
"model": model,
"messages": messages,
**kwargs
}
)
return response.json()

def get_health(self):
response = requests.get(f"{self.base_url}/health")
return response.json()

# Usage
client = SemanticRouterClient()
result = client.chat_completion([
{"role": "user", "content": "What is 2 + 2?"}
])

JavaScript 客户端

class SemanticRouterClient {
constructor(baseUrl = 'https://:8801') {
this.baseUrl = baseUrl;
}

async chatCompletion(messages, model = 'gpt-3.5-turbo', options = {}) {
const response = await fetch(`${this.baseUrl}/v1/chat/completions`, {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({
model,
messages,
...options
})
});

return response.json();
}

async getHealth() {
const response = await fetch(`${this.baseUrl}/health`);
return response.json();
}
}

// Usage
const client = new SemanticRouterClient();
const result = await client.chatCompletion([
{ role: 'user', content: 'Solve x^2 + 5x + 6 = 0' }
]);

频率限制

路由器使用以下响应头实现频率限制

频率限制响应头

X-RateLimit-Limit: 1000
X-RateLimit-Remaining: 999
X-RateLimit-Reset: 1640995200
X-RateLimit-Retry-After: 60

频率限制响应

{
"error": {
"message": "Rate limit exceeded",
"type": "rate_limit_error",
"code": "too_many_requests",
"details": {
"limit": 1000,
"window": "1h",
"retry_after": 60
}
}
}

最佳实践

1. 请求优化

# Include relevant context
messages = [
{
"role": "system",
"content": "You are a mathematics tutor."
},
{
"role": "user",
"content": "Explain derivatives in simple terms"
}
]

# Use appropriate tools
tools = [
{
"type": "function",
"function": {
"name": "calculator",
"description": "For mathematical calculations"
}
}
]

2. 错误处理

try:
response = client.chat_completion(messages)
if 'error' in response:
handle_router_error(response['error'])
else:
process_response(response)

except requests.exceptions.Timeout:
handle_timeout_error()
except requests.exceptions.ConnectionError:
handle_connection_error()

3. 监控集成

import time

start_time = time.time()
response = client.chat_completion(messages)
duration = time.time() - start_time

# Log routing metadata
routing_info = response.get('routing_metadata', {})
logger.info(f"Request routed to {routing_info.get('selected_model')} "
f"with confidence {routing_info.get('confidence')} "
f"in {duration:.2f}s")

后续步骤

有关更高级的 API 用法和自定义集成,请参考示例目录或加入我们的社区讨论。