Skip to content

feat(ai-proxy): add batches & files support #2355

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 8 commits into from
Jun 3, 2025

Conversation

daixijun
Copy link
Contributor

@daixijun daixijun commented May 31, 2025

Ⅰ. Describe what this PR did

添加 openai 、 Qwen 对 /v1/files 与 /v1/batches 接口的支持

Ⅱ. Does this pull request fix one issue?

Ⅲ. Why don't you add test cases (unit test/integration test)?

Ⅳ. Describe how to verify it

以通义千问为例

docker-compose.yaml

services:
  envoy:
    image: higress-registry.cn-hangzhou.cr.aliyuncs.com/higress/gateway:v2.1.3
    entrypoint: /usr/local/bin/envoy
    command: -c /etc/envoy/envoy.yaml --component-log-level wasm:debug
    networks:
      - wasmtest
    ports:
      - '10000:10000'
    volumes:
      - ./envoy.yaml:/etc/envoy/envoy.yaml
      - ./plugin.wasm:/etc/envoy/plugin.wasm

networks:
  wasmtest: {}

envoy.yaml

# File generated by hgctl. Modify as required.

admin:
  address:
    socket_address:
      protocol: TCP
      address: 0.0.0.0
      port_value: 9901
static_resources:
  listeners:
    - name: listener_0
      address:
        socket_address:
          protocol: TCP
          address: 0.0.0.0
          port_value: 10000
      filter_chains:
        - filters:
            - name: envoy.filters.network.http_connection_manager
              typed_config:
                "@type": type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager
                scheme_header_transformation:
                  scheme_to_overwrite: https
                stat_prefix: ingress_http
                # Output envoy logs to stdout
                access_log:
                  - name: envoy.access_loggers.stdout
                    typed_config:
                      "@type": type.googleapis.com/envoy.extensions.access_loggers.stream.v3.StdoutAccessLog
                # Modify as required
                route_config:
                  name: local_route
                  virtual_hosts:
                    - name: local_service
                      domains: ["*"]
                      routes:
                        - match:
                            prefix: "/"
                          route:
                            cluster: qwen
                            timeout: 300s
                http_filters:
                  - name: wasmtest
                    typed_config:
                      "@type": type.googleapis.com/udpa.type.v1.TypedStruct
                      type_url: type.googleapis.com/envoy.extensions.filters.http.wasm.v3.Wasm
                      value:
                        config:
                          name: wasmtest
                          vm_config:
                            runtime: envoy.wasm.runtime.v8
                            code:
                              local:
                                filename: /etc/envoy/plugin.wasm
                          configuration:
                            "@type": "type.googleapis.com/google.protobuf.StringValue"
                            value: |
                              {
                                "provider": {
                                  "type": "qwen",
                                  "qwenEnableCompatible": true,
                                  "openaiCustomUrl": "dashscope.aliyuncs.com/compatible-mode/v1/",
                                  "apiTokens": [
                                      "sk-xxx"
                                  ]
                                }
                              }
                  - name: envoy.filters.http.router
                    typed_config:
                      "@type": type.googleapis.com/envoy.extensions.filters.http.router.v3.Router
  clusters:
    - name: qwen
      connect_timeout: 30s
      type: LOGICAL_DNS
      dns_lookup_family: V4_ONLY
      lb_policy: ROUND_ROBIN
      load_assignment:
        cluster_name: google
        endpoints:
          - lb_endpoints:
              - endpoint:
                  address:
                    socket_address:
                      address: dashscope.aliyuncs.com
                      port_value: 443
      transport_socket:
        name: envoy.transport_sockets.tls
        typed_config:
          "@type": type.googleapis.com/envoy.extensions.transport_sockets.tls.v3.UpstreamTlsContext
          "sni": "dashscope.aliyuncs.com"

Upload file

$ curl -s http://127.0.0.1:10000/v1/files \
-F purpose="batch" \
-F file="@BatchTaskCreate.jsonl"
{
  "id": "file-batch-7845fab470d8418cb07d1b7f",
  "object": "file",
  "bytes": 9104,
  "created_at": 1748659403,
  "filename": "BatchTaskCreate.jsonl",
  "purpose": "batch",
  "status": "processed"
}

List files

$ curl -s http://127.0.0.1:10000/v1/files
{
  "object": "list",
  "has_more": false,
  "data": [
    {
      "id": "file-batch-7845fab470d8418cb07d1b7f",
      "object": "file",
      "bytes": 9104,
      "created_at": 1748659404,
      "filename": "BatchTaskCreate.jsonl",
      "purpose": "batch",
      "status": "processed"
    },
    {
      "id": "file-batch_output-mKjljWQT4JbHn9GnqdDRuCWS",
      "object": "file",
      "bytes": 534,
      "created_at": 1748621857,
      "filename": "7fe9e7fe-4f24-45ea-b90b-6642715641b5_1748621856436_error.jsonl",
      "purpose": "batch_output",
      "status": "processed"
    },
    {
      "id": "file-batch-9bffa2634c274e03b425afa4",
      "object": "file",
      "bytes": 9104,
      "created_at": 1748621073,
      "filename": "BatchTaskCreate.jsonl",
      "purpose": "batch",
      "status": "processed"
    },
    {
      "id": "file-batch-61271ca8a18546ef9ef67706",
      "object": "file",
      "bytes": 9104,
      "created_at": 1748602930,
      "filename": "BatchTaskCreate.jsonl",
      "purpose": "batch",
      "status": "processed"
    }
  ]
}

Retrieve File

$ curl -s http://127.0.0.1:10000/v1/files/file-batch-7845fab470d8418cb07d1b7f
{
  "id": "file-batch-7845fab470d8418cb07d1b7f",
  "object": "file",
  "bytes": 9104,
  "created_at": 1748659403,
  "filename": "BatchTaskCreate.jsonl",
  "purpose": "batch",
  "status": "processed",
  "meta": {}
}

Create Batch

$ curl -s http://127.0.0.1:10000/v1/batches \
-H "Content-Type: application/json" \
-d '{
     "input_file_id": "file-batch-7845fab470d8418cb07d1b7f", 
     "endpoint": "/v1/chat/completions", 
     "completion_window": "24h", 
     "metadata": {
       "ds_name":"任务名称",
       "ds_description":"任务描述"
     }
   }'
{
  "id": "batch_d2334048-816a-4c6f-a318-2aee1c201730",
  "object": "batch",
  "endpoint": "/v1/chat/completions",
  "errors": null,
  "input_file_id": "file-batch-7845fab470d8418cb07d1b7f",
  "completion_window": "24h",
  "status": "validating",
  "output_file_id": null,
  "error_file_id": null,
  "created_at": 1748659669,
  "in_progress_at": null,
  "expires_at": null,
  "finalizing_at": null,
  "completed_at": null,
  "failed_at": null,
  "expired_at": null,
  "cancelling_at": null,
  "cancelled_at": null,
  "request_counts": {
    "total": 0,
    "completed": 0,
    "failed": 0
  },
  "metadata": {
    "ds_name": "任务名称",
    "ds_description": "任务描述"
  }
}

List Batches

$ curl -s http://127.0.0.1:10000/v1/batches
{
  "object": "list",
  "data": [
    {
      "id": "batch_d2334048-816a-4c6f-a318-2aee1c201730",
      "object": "batch",
      "endpoint": "/v1/chat/completions",
      "errors": null,
      "input_file_id": "file-batch-7845fab470d8418cb07d1b7f",
      "completion_window": "24h",
      "status": "completed",
      "output_file_id": null,
      "error_file_id": "file-batch_output-kQMMqeOKDss58Ujcvlp7Xtfb",
      "created_at": 1748659669,
      "in_progress_at": 1748659669,
      "expires_at": null,
      "finalizing_at": 1748659670,
      "completed_at": 1748659670,
      "failed_at": null,
      "expired_at": null,
      "cancelling_at": null,
      "cancelled_at": null,
      "request_counts": {
        "total": 1,
        "completed": 0,
        "failed": 1
      },
      "metadata": {
        "ds_name": "任务名称",
        "ds_description": "任务描述"
      }
    }
  ],
  "first_id": "batch_d2334048-816a-4c6f-a318-2aee1c201730",
  "last_id": "batch_d2334048-816a-4c6f-a318-2aee1c201730",
  "has_more": false
}

Retrieve Batch

$ curl -s http://127.0.0.1:10000/v1/batches/batch_d2334048-816a-4c6f-a318-2aee1c201730
{
  "id": "batch_d2334048-816a-4c6f-a318-2aee1c201730",
  "object": "batch",
  "endpoint": "/v1/chat/completions",
  "errors": null,
  "input_file_id": "file-batch-7845fab470d8418cb07d1b7f",
  "completion_window": "24h",
  "status": "completed",
  "output_file_id": null,
  "error_file_id": "file-batch_output-kQMMqeOKDss58Ujcvlp7Xtfb",
  "created_at": 1748659669,
  "in_progress_at": 1748659669,
  "expires_at": null,
  "finalizing_at": 1748659670,
  "completed_at": 1748659670,
  "failed_at": null,
  "expired_at": null,
  "cancelling_at": null,
  "cancelled_at": null,
  "request_counts": {
    "total": 1,
    "completed": 0,
    "failed": 1
  },
  "metadata": {
    "ds_name": "任务名称",
    "ds_description": "任务描述"
  }
}

Ⅴ. Special notes for reviews

@daixijun daixijun requested review from cr7258, CH3CHO and rinfx as code owners May 31, 2025 02:57
Copy link

lingma-agents bot commented May 31, 2025

feat(ai-proxy): 添加对/v1/files和/v1/batches接口的支持

变更文件

文件路径 变更说明
plugins/​wasm-go/​extensions/​ai-proxy/​main.go 添加对/v1/files和/v1/batches路径的路由检测逻辑,通过字符串匹配识别API类型
plugins/​wasm-go/​extensions/​ai-proxy/​provider/​openai.go 定义OpenAI服务的/v1/batches和/v1/files接口路径常量,并更新能力映射配置
plugins/​wasm-go/​extensions/​ai-proxy/​provider/​qwen.go 增加Qwen兼容模式的/v1/files和/v1/batches路径配置,优化API名称判断逻辑

时序图

Sequence diagram for API routing logic
participant Client
participant Router as AIProxyRouter
participant Provider as OpenAI/QwenProvider

Client->>Router: 发送/v1/files或/v1/batches请求
Router->>Router: 通过getApiName解析路径
Router->>Provider: 调用对应接口处理逻辑
Provider-->>Router: 返回处理结果
Router-->>Client: 响应客户端请求
Loading

💡 小贴士

与 lingma-agents 交流的方式

📜 直接回复评论
直接回复本条评论,lingma-agents 将自动处理您的请求。例如:

  • 在当前代码中添加详细的注释说明。

  • 请详细介绍一下你说的 LRU 改造方案,并使用伪代码加以说明。

📜 在代码行处标记
在文件的特定位置创建评论并 @lingma-agents。例如:

  • @Lingma-Agent 分析这个方法的性能瓶颈并提供优化建议。

  • @Lingma-Agent 对这个方法生成优化代码。

📜 在讨论中提问
在任何讨论中 @lingma-agents 来获取帮助。例如:

  • @Lingma-Agent 请总结上述讨论并提出解决方案。

  • @Lingma-Agent 请根据讨论内容生成优化代码。

Copy link

@lingma-agents lingma-agents bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔍 代码评审报告

📋 评审意见详情

💡 单文件建议

✅ 未发现需要特别关注的代码问题。

🚀 跨文件建议

以下是对代码架构和设计的综合分析,聚焦于跨文件交互、系统一致性和潜在优化空间。
🔍 1. 路径匹配逻辑可能导致API路由错误

main.gogetApiName函数中,新增的/v1/batches/v1/files路径检查使用strings.Contains代替原来的strings.HasSuffix。这种模糊匹配可能导致误判,例如路径/v1/batchses/example会被错误识别为ApiNameBatches。建议改为精确匹配如strings.HasSuffix或使用路由框架的精确路径匹配,以避免路由逻辑错误。

📌 关键代码:

+if strings.Contains(path, "/v1/batches") {
+    return provider.ApiNameBatches
+}
+if strings.Contains(path, "/v1/files") {
+    return provider.ApiNameFiles
  • 未指定文件
🔍 2. 兼容模式路径映射不完整风险

qwen.goDefaultCapabilities方法中,新增的qwenCompatibleFilesPathqwenCompatibleBatchesPath路径未在非兼容模式下映射,可能导致非兼容模式下文件/批次API不可用。需检查所有新增功能是否在两种模式下均有对应路径定义。

📌 关键代码:

+string(ApiNameFiles):          qwenCompatibleFilesPath,
+string(ApiNameBatches):        qwenCompatibleBatchesPath,
🔍 3. API路由逻辑架构不一致

新增的/v1/batches/v1/files端点采用路径包含匹配,而原有/v1/models等端点使用后缀匹配。建议统一整个项目的API路径匹配策略(如全部使用精确后缀匹配或路由框架),避免混合模式导致的维护复杂度增加。

📌 关键代码:

+if strings.Contains(path, "/v1/batches") {
🔍 4. Qwen兼容模式路径覆盖不完整

qwen.goGetApiName函数中,新增的兼容路径如qwenCompatibleChatCompletionPath被正确识别,但未检查原有非兼容路径的覆盖情况。需确保所有新增功能在两种模式下的路径均被路由逻辑覆盖,避免功能缺失。

📌 关键代码:

+strings.Contains(path, qwenCompatibleChatCompletionPath):
+return ApiNameChatCompletion

💡 小贴士

与 lingma-agents 交流的方式

📜 直接回复评论
直接回复本条评论,lingma-agents 将自动处理您的请求。例如:

  • 在当前代码中添加详细的注释说明。

  • 请详细介绍一下你说的 LRU 改造方案,并使用伪代码加以说明。

📜 在代码行处标记
在文件的特定位置创建评论并 @lingma-agents。例如:

  • @Lingma-Agent 分析这个方法的性能瓶颈并提供优化建议。

  • @Lingma-Agent 对这个方法生成优化代码。

📜 在讨论中提问
在任何讨论中 @lingma-agents 来获取帮助。例如:

  • @Lingma-Agent 请总结上述讨论并提出解决方案。

  • @Lingma-Agent 请根据讨论内容生成优化代码。

@codecov-commenter
Copy link

codecov-commenter commented May 31, 2025

Codecov Report

All modified and coverable lines are covered by tests ✅

Project coverage is 46.06%. Comparing base (ef31e09) to head (bea5cd9).
Report is 532 commits behind head on main.

Additional details and impacted files

Impacted file tree graph

@@             Coverage Diff             @@
##             main    #2355       +/-   ##
===========================================
+ Coverage   35.91%   46.06%   +10.15%     
===========================================
  Files          69       81       +12     
  Lines       11576    13010     +1434     
===========================================
+ Hits         4157     5993     +1836     
+ Misses       7104     6671      -433     
- Partials      315      346       +31     

see 78 files with indirect coverage changes

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

Comment on lines 380 to 382
if strings.HasSuffix(path, "/v1/batches") {
return provider.ApiNameBatches
}
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

咦,这段上面好像已经写了?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

对, 这里是写重复了, 我移除掉

Copy link
Collaborator

@CH3CHO CH3CHO left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

@CH3CHO CH3CHO merged commit 33fc47c into alibaba:main Jun 3, 2025
12 checks passed
@daixijun daixijun deleted the feat/add-batches-files-support branch June 3, 2025 10:54
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants