easyai-ai-gateway/docs/server-main-integration.md

8.2 KiB
Raw Blame History

server-main 对接清单

1. 需要在 server-main 增加的内部接口

1.1 API Key 校验

POST /internal/platform/auth/verify-api-key
Authorization: Bearer ${SERVER_MAIN_INTERNAL_TOKEN}
Content-Type: application/json

{ "apiKey": "sk-..." }

返回:

{
  "sub": "user-id",
  "username": "demo",
  "role": ["user"],
  "tenantId": "tenant-id",
  "gatewayTenantId": "optional-gateway-tenant-id",
  "tenantKey": "team-a",
  "source": "server-main",
  "gatewayUserId": "optional-gateway-user-id",
  "userGroupId": "optional-primary-group-id",
  "userGroupKey": "pro",
  "userGroupKeys": ["pro", "image-plus"],
  "apiKeyId": "key-id",
  "apiKeySecret": "sk-...",
  "apiKeyName": "production-key"
}

1.2 文件上传

POST /v1/files/upload
Authorization: Bearer ${USER_JWT_OR_SK}
Content-Type: multipart/form-data

file=@result.png

AI Gateway 不维护独立 OSS 配置,也不向 server-main 申请预签名。需要上传本地中间产物、provider 临时 URL 转存、base64 解码结果时,统一组装 multipart 请求调用主服务开放上传接口,并记录主服务返回的 file id / URL / object key。

1.3 租户同步

AI Gateway 在独立模式下自己维护租户;接入 server-main 时保存主服务租户/组织同步副本,用于任务隔离、平台可见性、租户级限流和审计。

建议新增同步接口:

POST /internal/platform/tenants/sync
Authorization: Bearer ${SERVER_MAIN_INTERNAL_TOKEN}
Content-Type: application/json
Idempotency-Key: tenant:${source}:${externalTenantId}:${version}

请求体:

{
  "source": "server-main",
  "externalTenantId": "tenant-id",
  "tenantKey": "team-a",
  "name": "Team A",
  "status": "active",
  "planKey": "pro",
  "rateLimitPolicy": {
    "rules": [
      { "metric": "rpm", "limit": 500, "windowSeconds": 60 },
      { "metric": "concurrent", "limit": 20, "leaseTtlSeconds": 900 }
    ]
  },
  "sourceUpdatedAt": "2026-05-09T12:00:00Z"
}

要求:

  • Gateway 使用 source + externalTenantId 幂等 upsert 到 gateway_tenants
  • 租户禁用后,新任务拒绝入队;已运行任务按任务策略快照继续或由管理员取消。
  • 用户同步必须带可映射的 tenantId / tenantKey,使任务、用户、用户组、限流和平台可见性都能落到同一租户上下文。

1.4 用户同步

AI Gateway 需要在独立模式下自己维护用户,在接入 server-main 时保存主服务用户的同步副本。同步副本只用于模型调用策略、审计、任务归属和用户组解析,不承接主服务余额、订单、充值流水。

建议新增同步接口:

POST /internal/platform/users/sync
Authorization: Bearer ${SERVER_MAIN_INTERNAL_TOKEN}
Content-Type: application/json
Idempotency-Key: user:${source}:${externalUserId}:${version}

请求体:

{
  "source": "server-main",
  "externalUserId": "user-id",
  "username": "demo",
  "displayName": "Demo User",
  "email": "demo@example.com",
  "tenantId": "tenant-id",
  "tenantKey": "team-a",
  "roles": ["user"],
  "status": "active",
  "sourceUpdatedAt": "2026-05-09T12:00:00Z",
  "userGroupKeys": ["pro", "image-plus"]
}

要求:

  • Gateway 使用 source + externalUserId 幂等 upsert 到 gateway_users
  • status=disabled/locked/deletedGateway 应拒绝创建新任务;已运行任务按任务策略快照继续或由管理员取消。
  • 用户角色以主服务返回为准,但 Gateway 可以叠加本地管理角色,二者需要在 auth_profilemetadata 里可审计。
  • 用户组关系可以随用户同步一起带,也可以通过用户组同步接口单独维护,最终都落到 gateway_user_group_memberships

1.5 用户组与折扣策略同步

用户组是跨服务策略Gateway 需要按用户组执行模型调用折扣、TPM/RPM/并发、队列优先级;server-main 需要按用户组执行充值折扣、资源包赠送、余额流水。两边必须保持同一个 groupKey

建议新增同步接口:

POST /internal/platform/user-groups/sync
Authorization: Bearer ${SERVER_MAIN_INTERNAL_TOKEN}
Content-Type: application/json
Idempotency-Key: ${groupKey}:${version}

请求体:

{
  "groupKey": "enterprise",
  "name": "企业组",
  "rechargeDiscountPolicy": {
    "type": "tiered_bonus",
    "tiers": [{ "minAmount": 1000, "bonusRatio": 0.12 }]
  },
  "billingDiscountPolicy": {
    "defaultDiscountFactor": 0.9
  },
  "rateLimitPolicy": {
    "rules": [
      { "metric": "rpm", "limit": 1200, "windowSeconds": 60 },
      { "metric": "concurrent", "limit": 50, "leaseTtlSeconds": 900 }
    ]
  },
  "memberships": [
    { "principalType": "user", "principalId": "user-id" },
    { "principalType": "tenant", "principalId": "tenant-id" }
  ]
}

要求:

  • server-main 是充值、余额和订单事实源,负责执行 rechargeDiscountPolicy
  • Gateway 是模型执行事实源,负责执行 billingDiscountPolicyrateLimitPolicy、队列和并发策略。
  • 用户登录 / API Key 校验返回 claim 时,建议带上命中的 userGroupKey / userGroupIdGateway 也可以根据同步缓存二次解析。

1.6 任务进度回调到 server-main

AI Gateway 不直接替换原业务前端 WebSocket 通道。Gateway 配置任务进度回调地址,所有任务中间状态先写入 Gateway 本地事件表和 callback outbox再回调给 server-main,由 server-main 内部推送流程复用原 WebSocket 网关推送给业务前端。

POST /internal/platform/task-progress-callbacks
Authorization: Bearer ${SERVER_MAIN_INTERNAL_TOKEN}
Content-Type: application/json
Idempotency-Key: ${taskId}:${seq}
X-EasyAI-Event-Type: task.progress

请求体:

{
  "eventId": "uuid",
  "taskId": "gateway-task-id",
  "externalTaskId": "server-main-task-id",
  "userId": "user-id",
  "tenantId": "tenant-id",
  "apiKeyId": "optional",
  "kind": "images.generations",
  "model": "gpt-image-1",
  "seq": 12,
  "event": "progress",
  "status": "running",
  "phase": "polling",
  "progress": 0.42,
  "message": "Generating video frames",
  "payload": {},
  "createdAt": "2026-05-09T12:00:00Z"
}

server-main 处理要求:

  • 使用 Idempotency-KeytaskId + seq 幂等去重。
  • 根据 externalTaskId / taskId / userId / tenantId 定位原业务频道。
  • 复用现有 WebSocket 网关事件格式推给前端,尽量不改业务前端订阅协议。
  • 只负责推送与必要状态同步,不重新执行任务、不重新计算计费。

Gateway 侧配置:

TASK_PROGRESS_CALLBACK_ENABLED=true
TASK_PROGRESS_CALLBACK_URL=http://easyai-server-main:3000/internal/platform/task-progress-callbacks
TASK_PROGRESS_CALLBACK_TIMEOUT_MS=5000
TASK_PROGRESS_CALLBACK_MAX_ATTEMPTS=10

1.7 结算事件

POST /internal/platform/settlements
Authorization: Bearer ${SERVER_MAIN_INTERNAL_TOKEN}
Content-Type: application/json
Idempotency-Key: ${eventId}

结算事件中的 billings 由 AI Gateway 根据基准模型库、平台折扣、平台模型覆盖后的 effective pricing 计算。server-main 仍负责余额、资源包、账单锁和消费流水,不重新推导模型价格,只按幂等事件扣费。

2. server-main OpenaiService 薄门面

保留现有对内方法签名,内部新增 AiGatewayClient

  • createChatCompletion
  • generateImage
  • editImage
  • generateVideo
  • createEmbedding
  • estimateBilling

切流开关:

AI_GATEWAY_ENABLED=true
AI_GATEWAY_BASE_URL=http://easyai-ai-gateway:8088
AI_GATEWAY_INTERNAL_TOKEN=change-me
AI_GATEWAY_TASK_PROGRESS_CALLBACK_ENABLED=true

3. 迁移期双写与比对

高风险接口可短期 shadow

  1. 主路径仍走旧实现。
  2. 异步把同一请求投递到 Gateway dry-run。
  3. 比对候选平台、TPM/RPM/并发限流决策、预估扣费、参数预处理结果。
  4. 结果稳定后切主路径。

4. 不迁移项

  • refresh_token 签发和刷新。
  • 用户余额查询。
  • server-main 用户 API Key 的创建、撤销、列表。Gateway 独立模式会维护自己的本地 API Key。
  • server-main 账单锁、扣费流水。Gateway 独立模式会维护自己的钱包账户、充值订单和钱包流水。
  • OSS/COS/S3 上传配置和实际文件落库。
  • 对话与绘图历史最终落库。