Copilot commented on code in PR #12941:
URL: https://github.com/apache/apisix/pull/12941#discussion_r2802885516
##########
apisix/plugins/ai-rag.lua:
##########
@@ -14,65 +14,113 @@
-- See the License for the specific language governing permissions and
-- limitations under the License.
--
-local next = next
-local require = require
-local ngx_req = ngx.req
+local next = next
+local require = require
+local ngx_req = ngx.req
+local table = table
+local ipairs = ipairs
+local pcall = pcall
+local tostring = tostring
-local http = require("resty.http")
local core = require("apisix.core")
-local azure_openai_embeddings =
require("apisix.plugins.ai-rag.embeddings.azure_openai").schema
-local azure_ai_search_schema =
require("apisix.plugins.ai-rag.vector-search.azure_ai_search").schema
+local openai_base_embeddings_schema =
require("apisix.plugins.ai-rag.embeddings.openai-base").schema
+local azure_ai_search_schema =
require("apisix.plugins.ai-rag.vector-search.azure-ai-search").schema
+local cohere_rerank_schema =
require("apisix.plugins.ai-rag.rerank.cohere").schema
local HTTP_INTERNAL_SERVER_ERROR = ngx.HTTP_INTERNAL_SERVER_ERROR
local HTTP_BAD_REQUEST = ngx.HTTP_BAD_REQUEST
+local embeddings_drivers = {}
+local vector_search_drivers = {}
+local rerank_drivers = {}
+
+local plugin_name = "ai-rag"
+
+local input_strategy_enum = {
+ last = "last",
+ all = "all"
+}
+
local schema = {
type = "object",
properties = {
- type = "object",
embeddings_provider = {
type = "object",
- properties = {
- azure_openai = azure_openai_embeddings
+ oneOf = {
+ {
+ properties = {
+ openai = openai_base_embeddings_schema
+ },
+ required = { "openai" },
+ additionalProperties = false
+ },
+ {
+ properties = {
+ ["azure-openai"] = openai_base_embeddings_schema
+ },
+ required = { "azure-openai" },
+ additionalProperties = false
+ },
+ {
+ properties = {
+ ["openai-compatible"] = openai_base_embeddings_schema
+ },
+ required = { "openai-compatible" },
+ additionalProperties = false
+ }
},
- -- ensure only one provider can be configured while implementing
support for
- -- other providers
- required = { "azure_openai" },
- maxProperties = 1,
+ description = "Configuration for the embeddings provider."
},
vector_search_provider = {
type = "object",
- properties = {
- azure_ai_search = azure_ai_search_schema
+ oneOf = {
+ {
+ properties = {
+ ["azure-ai-search"] = azure_ai_search_schema
+ },
+ required = { "azure-ai-search" },
+ additionalProperties = false
+ }
},
- -- ensure only one provider can be configured while implementing
support for
- -- other providers
- required = { "azure_ai_search" },
- maxProperties = 1
+ description = "Configuration for the vector search provider."
},
- },
- required = { "embeddings_provider", "vector_search_provider" }
-}
-
-local request_schema = {
- type = "object",
- properties = {
- ai_rag = {
+ rerank_provider = {
+ type = "object",
+ oneOf = {
+ {
+ properties = {
+ cohere = cohere_rerank_schema
+ },
+ required = { "cohere" },
+ additionalProperties = false
+ }
+ },
+ description = "Configuration for the rerank provider."
+ },
+ rag_config = {
type = "object",
properties = {
- vector_search = {},
- embeddings = {},
+ input_strategy = {
+ type = "string",
+ enum = { input_strategy_enum.last, input_strategy_enum.all
},
+ default = input_strategy_enum.last,
+ description = "Strategy for extracting input text from
messages."
+ .. "'last' uses the last user message"
Review Comment:
The description for `input_strategy` is missing spaces between sentences. It
should have spaces after the periods to improve readability.
```suggestion
description = "Strategy for extracting input text from
messages. "
.. "'last' uses the last user message. "
```
##########
docs/zh/latest/plugins/ai-rag.md:
##########
@@ -176,60 +162,29 @@ curl "http://127.0.0.1:9180/apisix/admin/routes" -X PUT \
}
}
}
-}'
+}''
```
-向路由发送 POST 请求,在请求体中包含向量字段名称、嵌入模型维度和输入提示:
+向路由发送 POST 请求:
```shell
curl "http://127.0.0.1:9080/rag" -X POST \
-H "Content-Type: application/json" \
- -d '{
- "ai_rag":{
- "vector_search":{
- "fields":"contentVector"
- },
- "embeddings":{
- "input":"Which Azure services are good for DevOps?",
- "dimensions":1024
- }
- }
- }'
+ -d ''{
+ "messages": [
+ {
+ "role": "user",
+ "content": "Which Azure services are good for DevOps?"
+ }
+ ]
+ }''
Review Comment:
The shell command syntax is incorrect. Line 165 has `}''` (two single
quotes) and line 180 also has `}''`. In shell, when using `-d` with curl, the
closing should be `}'` (single quote). The double quotes are causing malformed
JSON. Compare with the English version (line 165 in English doc) which
correctly uses `}'`.
##########
docs/en/latest/plugins/ai-rag.md:
##########
@@ -37,69 +37,40 @@ description: The ai-rag Plugin enhances LLM outputs with
Retrieval-Augmented Gen
The `ai-rag` Plugin provides Retrieval-Augmented Generation (RAG) capabilities
with LLMs. It facilitates the efficient retrieval of relevant documents or
information from external data sources, which are used to enhance the LLM
responses, thereby improving the accuracy and contextual relevance of the
generated outputs.
-The Plugin supports using [Azure
OpenAI](https://azure.microsoft.com/en-us/products/ai-services/openai-service)
and [Azure AI
Search](https://azure.microsoft.com/en-us/products/ai-services/ai-search)
services for generating embeddings and performing vector search.
-
-**_As of now only [Azure
OpenAI](https://azure.microsoft.com/en-us/products/ai-services/openai-service)
and [Azure AI
Search](https://azure.microsoft.com/en-us/products/ai-services/ai-search)
services are supported for generating embeddings and performing vector search
respectively. PRs for introducing support for other service providers are
welcomed._**
+The Plugin supports using
[OpenAI](https://platform.openai.com/docs/api-reference/embeddings) or [Azure
OpenAI](https://learn.microsoft.com/en-us/azure/search/vector-search-how-to-generate-embeddings?tabs=rest-api)
services for generating embeddings, [Azure AI
Search](https://azure.microsoft.com/en-us/products/ai-services/ai-search)
services for performing vector search, and optionally [Cohere
Rerank](https://docs.cohere.com/docs/rerank-overview) services for reranking
the retrieval results.
## Attributes
-| Name | Required | Type |
Description
|
-| ----------------------------------------------- | ------------ | -------- |
-----------------------------------------------------------------------------------------------------------------------------------------
|
-| embeddings_provider | True | object |
Configurations of the embedding models provider.
|
-| embeddings_provider.azure_openai | True | object |
Configurations of [Azure
OpenAI](https://azure.microsoft.com/en-us/products/ai-services/openai-service)
as the embedding models provider. |
-| embeddings_provider.azure_openai.endpoint | True | string |
Azure OpenAI embedding model endpoint.
|
-| embeddings_provider.azure_openai.api_key | True | string |
Azure OpenAI API key.
|
-| vector_search_provider | True | object |
Configuration for the vector search provider.
|
-| vector_search_provider.azure_ai_search | True | object |
Configuration for Azure AI Search.
|
-| vector_search_provider.azure_ai_search.endpoint | True | string |
Azure AI Search endpoint.
|
-| vector_search_provider.azure_ai_search.api_key | True | string |
Azure AI Search API key.
|
-
-## Request Body Format
-
-The following fields must be present in the request body.
-
-| Field | Type | Description
|
-| -------------------- | -------- |
-------------------------------------------------------------------------------------------------------------------------------
|
-| ai_rag | object | Request body RAG specifications.
|
-| ai_rag.embeddings | object | Request parameters required to generate
embeddings. Contents will depend on the API specification of the configured
provider. |
-| ai_rag.vector_search | object | Request parameters required to perform
vector search. Contents will depend on the API specification of the configured
provider. |
-
-- Parameters of `ai_rag.embeddings`
-
- - Azure OpenAI
-
- | Name | Required | Type | Description
|
- | --------------- | ------------ | -------- |
--------------------------------------------------------------------------------------------------------------------------
|
- | input | True | string | Input text used to compute
embeddings, encoded as a string.
|
- | user | False | string | A unique identifier
representing your end-user, which can help in monitoring and detecting abuse.
|
- | encoding_format | False | string | The format to return the
embeddings in. Can be either `float` or `base64`. Defaults to `float`.
|
- | dimensions | False | integer | The number of dimensions
the resulting output embeddings should have. Only supported in text-embedding-3
and later models. |
-
-For other parameters please refer to the [Azure OpenAI embeddings
documentation](https://learn.microsoft.com/en-us/azure/ai-services/openai/reference#embeddings).
-
-- Parameters of `ai_rag.vector_search`
-
- - Azure AI Search
-
- | Field | Required | Type | Description |
- | --------- | ------------ | -------- | ---------------------------- |
- | fields | True | String | Fields for the vector search. |
-
- For other parameters please refer the [Azure AI Search
documentation](https://learn.microsoft.com/en-us/rest/api/searchservice/documents/search-post).
-
-Example request body:
-
-```json
-{
- "ai_rag": {
- "vector_search": { "fields": "contentVector" },
- "embeddings": {
- "input": "which service is good for devops",
- "dimensions": 1024
- }
- }
-}
-```
+| Name | Required | Type | Valid
Values | Description
|
+| ----------------------------------------------- | ------------ | -------- |
--- |
-----------------------------------------------------------------------------------------------------------------------------------------
|
+| embeddings_provider | True | object |
openai, azure-openai, openai-compatible | Configurations of the embedding
models provider. Must and can only specify one. Currently supports `openai`,
`azure-openai`, `openai-compatible`.
|
+| vector_search_provider | True | object |
azure-ai-search | Configuration for the vector search provider.
|
+| vector_search_provider.azure-ai-search | True | object |
| Configuration for Azure AI Search.
|
+| vector_search_provider.azure-ai-search.endpoint | True | string |
| Azure AI Search endpoint.
|
+| vector_search_provider.azure-ai-search.api_key | True | string |
| Azure AI Search API key.
|
+| vector_search_provider.azure-ai-search.fields | True | string |
| Target fields for vector search.
|
+| vector_search_provider.azure-ai-search.select | True | string |
| Fields to select in the response.
|
+| vector_search_provider.azure-ai-search.exhaustive| False | boolean |
| Whether to perform an exhaustive search. Defaults to `true`.
|
+| vector_search_provider.azure-ai-search.k | False | integer |
>0 | Number of nearest neighbors to return. Defaults to 5.
|
+| rerank_provider | False | object |
cohere | Configuration for the rerank provider.
|
+| rerank_provider.cohere | False | object |
| Configuration for Cohere Rerank.
|
+| rerank_provider.cohere.endpoint | False | string |
| Cohere Rerank API endpoint. Defaults to `https://api.cohere.ai/v1/rerank`.
|
+| rerank_provider.cohere.api_key | True | string |
| Cohere API key.
|
+| rerank_provider.cohere.model | False | string |
| Rerank model name. Defaults to `Cohere-rerank-v4.0-fast`.
|
Review Comment:
The documentation states that `rerank_provider.cohere.model` defaults to
`Cohere-rerank-v4.0-fast`, but this default is not present in the schema
definition (apisix/plugins/ai-rag/rerank/cohere.lua lines 36-39). The schema
does not specify a default value for the `model` property, so either the
documentation should be updated to reflect this, or a default should be added
to the schema.
##########
docs/zh/latest/plugins/ai-rag.md:
##########
@@ -151,15 +126,26 @@ curl "http://127.0.0.1:9180/apisix/admin/routes" -X PUT \
"plugins": {
"ai-rag": {
"embeddings_provider": {
- "azure_openai": {
+ "azure-openai": {
"endpoint": "'"$AZ_EMBEDDINGS_ENDPOINT"'",
"api_key": "'"$AZ_OPENAI_API_KEY"'"
}
},
"vector_search_provider": {
- "azure_ai_search": {
+ "azure-ai-search": {
"endpoint": "'"$AZ_AI_SEARCH_ENDPOINT"'",
- "api_key": "'"$AZ_AI_SEARCH_KEY"'"
+ "api_key": "'"$AZ_AI_SEARCH_KEY"'",
+ "fields": "contentVector",
+ "select": "content",
+ "k": 10
+ }
+ },
+ "rerank_provider": {
+ "cohere": {
+ "endpoint":"'"$COHERE_DOMAIN"'",
+ "api_key": "'"$COHERE_API_KEY"'",
+ "model": ""'"COHERE_MODEL"'",
Review Comment:
There's a syntax error in the shell command. Line 147 has `"model":
""'"COHERE_MODEL"'",` which has an extra double quote before the variable
substitution and is missing the `$` prefix for the environment variable. It
should be `"model": "'"$COHERE_MODEL"'",` to match the pattern used for other
variables in the same file (see lines 130, 131, 136, 137, 145, 146).
```suggestion
"model": "'"$COHERE_MODEL"'",
```
##########
apisix/plugins/ai-rag/rerank/cohere.lua:
##########
@@ -0,0 +1,117 @@
+--
+-- Licensed to the Apache Software Foundation (ASF) under one or more
+-- contributor license agreements. See the NOTICE file distributed with
+-- this work for additional information regarding copyright ownership.
+-- The ASF licenses this file to You under the Apache License, Version 2.0
+-- (the "License"); you may not use this file except in compliance with
+-- the License. You may obtain a copy of the License at
+--
+-- http://www.apache.org/licenses/LICENSE-2.0
+--
+-- Unless required by applicable law or agreed to in writing, software
+-- distributed under the License is distributed on an "AS IS" BASIS,
+-- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+-- See the License for the specific language governing permissions and
+-- limitations under the License.
+--
+local core = require("apisix.core")
+local http = require("resty.http")
+local type = type
+local ipairs = ipairs
+
+local _M = {}
+
+_M.schema = {
+ type = "object",
+ properties = {
+ endpoint = {
+ type = "string",
+ default = "https://api.cohere.ai/v2/rerank",
+ description = "The endpoint for the Cohere Rerank API."
+ },
+ api_key = {
+ type = "string",
+ description = "The API key for authentication."
+ },
+ model = {
+ type = "string",
+ description = "The model to use for reranking."
+ },
+ top_n = {
+ type = "integer",
+ minimum = 1,
+ default = 3,
+ description = "The number of top results to return."
+ }
+ },
+ required = { "api_key", "model" }
Review Comment:
The schema requires both `api_key` and `model` (line 47), but `model` has a
default value specified in line 38. Since a default is provided, `model` should
not be in the `required` array, or the default should be removed. Having both a
default and marking it as required is contradictory.
##########
apisix/plugins/ai-rag.lua:
##########
@@ -82,67 +130,141 @@ function _M.check_schema(conf)
end
+local function get_input_text(messages, strategy)
+ if not messages or #messages == 0 then
+ return nil
+ end
+
+ if strategy == input_strategy_enum.last then
+ for i = #messages, 1, -1 do
+ if messages[i].role == "user" then
+ return messages[i].content
+ end
+ end
+ elseif strategy == input_strategy_enum.all then
+ local contents = {}
+ for _, msg in ipairs(messages) do
+ if msg.role == "user" then
+ core.table.insert(contents, msg.content)
+ end
+ end
+ if #contents > 0 then
+ return table.concat(contents, "\n")
+ end
+ end
+ return nil
+end
+
+
+local function load_driver(category, name, cache)
+ local driver = cache[name]
+ if driver then
+ return driver
+ end
+
+ local pkg_path = "apisix.plugins.ai-rag." .. category .. "." .. name
+ local ok, mod = pcall(require, pkg_path)
+ if not ok then
+ return nil, "failed to load module " .. pkg_path .. ", err: " ..
tostring(mod)
+ end
+
+ cache[name] = mod
+ return mod
+end
+
+
+local function inject_context_into_messages(messages, docs)
+ if not docs or #docs == 0 then
+ return
+ end
+
+ local context_str = core.table.concat(docs, "\n\n")
+ local augment = {
+ role = "user",
+ content = "Context:\n" .. context_str
+ }
+ if #messages > 0 then
+ -- Insert context before the last message (which is typically the
user's latest query)
+ -- to ensure the LLM considers the context relevant to the immediate
question.
+ core.table.insert(messages, #messages, augment)
Review Comment:
The `inject_context_into_messages` function uses
`core.table.insert(messages, #messages, augment)` to insert the context before
the last message. However, `table.insert(t, pos, value)` inserts at position
`pos`, shifting existing elements. This means the context will be inserted at
position `#messages`, which pushes the last message to position `#messages +
1`. This is correct behavior, but it might be clearer to add a comment
explaining that the context is being inserted as the second-to-last message.
##########
apisix/plugins/ai-rag.lua:
##########
@@ -82,67 +130,141 @@ function _M.check_schema(conf)
end
+local function get_input_text(messages, strategy)
+ if not messages or #messages == 0 then
+ return nil
+ end
+
+ if strategy == input_strategy_enum.last then
+ for i = #messages, 1, -1 do
+ if messages[i].role == "user" then
+ return messages[i].content
+ end
+ end
+ elseif strategy == input_strategy_enum.all then
+ local contents = {}
+ for _, msg in ipairs(messages) do
+ if msg.role == "user" then
+ core.table.insert(contents, msg.content)
+ end
+ end
+ if #contents > 0 then
+ return table.concat(contents, "\n")
+ end
+ end
+ return nil
+end
+
+
+local function load_driver(category, name, cache)
+ local driver = cache[name]
+ if driver then
+ return driver
+ end
+
+ local pkg_path = "apisix.plugins.ai-rag." .. category .. "." .. name
+ local ok, mod = pcall(require, pkg_path)
+ if not ok then
+ return nil, "failed to load module " .. pkg_path .. ", err: " ..
tostring(mod)
+ end
+
+ cache[name] = mod
+ return mod
+end
+
+
+local function inject_context_into_messages(messages, docs)
+ if not docs or #docs == 0 then
+ return
+ end
+
+ local context_str = core.table.concat(docs, "\n\n")
+ local augment = {
+ role = "user",
+ content = "Context:\n" .. context_str
+ }
+ if #messages > 0 then
+ -- Insert context before the last message (which is typically the
user's latest query)
+ -- to ensure the LLM considers the context relevant to the immediate
question.
+ core.table.insert(messages, #messages, augment)
+ else
+ core.table.insert_tail(messages, augment)
+ end
+end
+
+
function _M.access(conf, ctx)
- local httpc = http.new()
local body_tab, err = core.request.get_json_request_body_table()
if not body_tab then
return HTTP_BAD_REQUEST, err
end
- if not body_tab["ai_rag"] then
- core.log.error("request body must have \"ai-rag\" field")
- return HTTP_BAD_REQUEST
- end
-
- local embeddings_provider = next(conf.embeddings_provider)
- local embeddings_provider_conf =
conf.embeddings_provider[embeddings_provider]
- local embeddings_driver = require("apisix.plugins.ai-rag.embeddings." ..
embeddings_provider)
- local vector_search_provider = next(conf.vector_search_provider)
- local vector_search_provider_conf =
conf.vector_search_provider[vector_search_provider]
- local vector_search_driver =
require("apisix.plugins.ai-rag.vector-search." ..
- vector_search_provider)
+ -- 1. Extract Input
+ local rag_conf = conf.rag_config or {}
+ local input_strategy = rag_conf.input_strategy or input_strategy_enum.last
+ local input_text = get_input_text(body_tab.messages, input_strategy)
- local vs_req_schema = vector_search_driver.request_schema
- local emb_req_schema = embeddings_driver.request_schema
+ if not input_text then
+ core.log.warn("no user input found for embedding")
+ return
Review Comment:
When no user input is found, the function returns early (line 209) without
an error status. This silently allows the request to proceed without RAG
augmentation, which could be unexpected behavior. Consider returning an error
status (e.g., HTTP_BAD_REQUEST) to clearly indicate that the request cannot be
processed without a user message.
```suggestion
return HTTP_BAD_REQUEST, "no user input found for embedding"
```
##########
docs/zh/latest/plugins/ai-rag.md:
##########
@@ -37,69 +37,40 @@ description: ai-rag 插件通过检索增强生成(RAG)增强 LLM 输出,
`ai-rag` 插件为 LLM 提供检索增强生成(Retrieval-Augmented
Generation,RAG)功能。它促进从外部数据源高效检索相关文档或信息,这些信息用于增强 LLM 响应,从而提高生成输出的准确性和上下文相关性。
-该插件支持使用 [Azure
OpenAI](https://azure.microsoft.com/en-us/products/ai-services/openai-service)
和 [Azure AI
Search](https://azure.microsoft.com/en-us/products/ai-services/ai-search)
服务来生成嵌入和执行向量搜索。
-
-**_目前仅支持 [Azure
OpenAI](https://azure.microsoft.com/en-us/products/ai-services/openai-service)
和 [Azure AI
Search](https://azure.microsoft.com/en-us/products/ai-services/ai-search)
服务来生成嵌入和执行向量搜索。欢迎提交 PR 以引入对其他服务提供商的支持。_**
+该插件支持使用 [OpenAI](https://platform.openai.com/docs/api-reference/embeddings) 或
[Azure
OpenAI](https://learn.microsoft.com/en-us/azure/search/vector-search-how-to-generate-embeddings?tabs=rest-api)
服务生成嵌入,使用 [Azure AI
Search](https://azure.microsoft.com/en-us/products/ai-services/ai-search)
服务执行向量搜索,以及可选的 [Cohere Rerank](https://docs.cohere.com/docs/rerank-overview)
服务对检索结果进行重排序。
## 属性
-| 名称 | 必选项 | 类型 | 描述
|
-| ----------------------------------------------- | ------------ | -------- |
-----------------------------------------------------------------------------------------------------------------------------------------
|
-| embeddings_provider | 是 | object |
嵌入模型提供商的配置。
|
-| embeddings_provider.azure_openai | 是 | object |
[Azure
OpenAI](https://azure.microsoft.com/en-us/products/ai-services/openai-service)
作为嵌入模型提供商的配置。 |
-| embeddings_provider.azure_openai.endpoint | 是 | string |
Azure OpenAI 嵌入模型端点。
|
-| embeddings_provider.azure_openai.api_key | 是 | string |
Azure OpenAI API 密钥。
|
-| vector_search_provider | 是 | object |
向量搜索提供商的配置。
|
-| vector_search_provider.azure_ai_search | 是 | object |
Azure AI Search 的配置。
|
-| vector_search_provider.azure_ai_search.endpoint | 是 | string |
Azure AI Search 端点。
|
-| vector_search_provider.azure_ai_search.api_key | 是 | string |
Azure AI Search API 密钥。
|
-
-## 请求体格式
-
-请求体中必须包含以下字段。
-
-| 字段 | 类型 | 描述
|
-| -------------------- | -------- |
-------------------------------------------------------------------------------------------------------------------------------
|
-| ai_rag | object | 请求体 RAG 规范。
|
-| ai_rag.embeddings | object | 生成嵌入所需的请求参数。内容将取决于配置的提供商的 API 规范。 |
-| ai_rag.vector_search | object | 执行向量搜索所需的请求参数。内容将取决于配置的提供商的 API 规范。 |
-
-- `ai_rag.embeddings` 的参数
-
- - Azure OpenAI
-
- | 名称 | 必选项 | 类型 | 描述
|
- | --------------- | ------------ | -------- |
--------------------------------------------------------------------------------------------------------------------------
|
- | input | 是 | string | 用于计算嵌入的输入文本,编码为字符串。
|
- | user | 否 | string | 代表您的最终用户的唯一标识符,可以帮助监控和检测滥用。
|
- | encoding_format | 否 | string | 返回嵌入的格式。可以是 `float` 或
`base64`。默认为 `float`。 |
- | dimensions | 否 | integer | 结果输出嵌入应具有的维数。仅在
text-embedding-3 及更高版本的模型中支持。 |
-
-有关其他参数,请参阅 [Azure OpenAI
嵌入文档](https://learn.microsoft.com/en-us/azure/ai-services/openai/reference#embeddings)。
-
-- `ai_rag.vector_search` 的参数
-
- - Azure AI Search
-
- | 字段 | 必选项 | 类型 | 描述 |
- | --------- | ------------ | -------- | ---------------------------- |
- | fields | 是 | String | 向量搜索的字段。 |
-
- 有关其他参数,请参阅 [Azure AI Search
文档](https://learn.microsoft.com/en-us/rest/api/searchservice/documents/search-post)。
-
-示例请求体:
-
-```json
-{
- "ai_rag": {
- "vector_search": { "fields": "contentVector" },
- "embeddings": {
- "input": "which service is good for devops",
- "dimensions": 1024
- }
- }
-}
-```
+| 名称 | 必选项 | 类型 | 有效值 | 描述
|
+| ----------------------------------------------- | ------------ | -------- |
--- |
-----------------------------------------------------------------------------------------------------------------------------------------
|
+| embeddings_provider | 是 | object |
openai, azure-openai, openai-compatible | 嵌入模型提供商的配置。必须且只能指定一种,当前支持 `openai`,
`azure-openai`, `openai-compatible`
|
+| vector_search_provider | 是 | object |
azure-ai-search | 向量搜索提供商的配置。
|
+| vector_search_provider.azure-ai-search | 是 | object | |
Azure AI Search 的配置。
|
+| vector_search_provider.azure-ai-search.endpoint | 是 | string | |
Azure AI Search 端点。
|
+| vector_search_provider.azure-ai-search.api_key | 是 | string | |
Azure AI Search API 密钥。
|
+| vector_search_provider.azure-ai-search.fields | 是 | string | |
向量搜索的目标字段。
|
+| vector_search_provider.azure-ai-search.select | 是 | string | |
响应中选择返回的字段。
|
+| vector_search_provider.azure-ai-search.exhaustive| 否 | boolean | |
是否进行详尽搜索。默认为 `true`。
|
+| vector_search_provider.azure-ai-search.k | 否 | integer | >0
| 返回的最近邻数量。默认为 5。
|
+| rerank_provider | 否 | object |
cohere | 重排序提供商的配置。
|
+| rerank_provider.cohere | 否 | object | |
Cohere Rerank 的配置。
|
+| rerank_provider.cohere.endpoint | 否 | string | |
Cohere Rerank API 端点。默认为 `https://api.cohere.ai/v1/rerank`。
|
+| rerank_provider.cohere.api_key | 是 | string | |
Cohere API 密钥。
|
+| rerank_provider.cohere.model | 否 | string | |
重排序模型名称。默认为 `Cohere-rerank-v4.0-fast`。
|
+| rerank_provider.cohere.top_n | 否 | integer | |
重排序后保留的文档数量。默认为 3。
|
+| rag_config | 否 | object | |
RAG 流程的通用配置。
|
+| rag_config.input_strategy | 否 | string | |
提取用户输入文本的策略。可选值:`last`(仅最后一条消息),`all`(所有用户消息拼接)。默认为 `last`。
|
+
+### embeddings_provider 属性
+
+当前支持`openai`,`azure`,`openai-compatible`,所有子字段均位于
`embeddings_provider.<provider>` 对象下(例如 `embeddings_provider.openai.api_key`)。
Review Comment:
In the Chinese documentation, there's a typo in line 66. It says "azure" but
should be "azure-openai" to match the actual provider names (openai,
azure-openai, openai-compatible).
```suggestion
当前支持`openai`,`azure-openai`,`openai-compatible`,所有子字段均位于
`embeddings_provider.<provider>` 对象下(例如 `embeddings_provider.openai.api_key`)。
```
##########
t/plugin/ai-rag.t:
##########
@@ -383,10 +499,19 @@ passed
-=== TEST 12: send request with embedding input missing
+=== TEST 10: Verify Context Injection (With Rerank)
+--- log_level: debug
--- request
POST /echo
-{"ai_rag":{"vector_search":{"fields":"something"},"embeddings":{"input":"which
service is good for devops"}}}
---- error_code: 200
+{
+ "messages": [
+ {
+ "role": "user",
+ "content": "What is Apache APISIX?"
+ }
+ ]
+}
+--- error_log
+Number of documents retrieved: 1
--- response_body eval
-qr/\{"messages":\[\{"content":"passed","role":"user"\}\]\}|\{"messages":\[\{"role":"user","content":"passed"\}\]\}/
+qr/Apache APISIX is a dynamic, real-time, high-performance API Gateway.*What
is Apache APISIX/
Review Comment:
The test suite does not include any tests for the `input_strategy`
configuration option, specifically the "all" strategy which concatenates all
user messages. This is a new feature that should have test coverage to ensure
it works correctly, especially for edge cases like multiple user messages or
mixed message types.
```suggestion
qr/Apache APISIX is a dynamic, real-time, high-performance API Gateway.*What
is Apache APISIX/
=== TEST 11: Happy Path (With Rerank, input_strategy all)
--- config
location /t_input_all {
content_by_lua_block {
local t = require("lib.test_admin").test
local code, body = t('/apisix/admin/routes/2',
ngx.HTTP_PUT,
[[{
"uri": "/echo",
"plugins": {
"ai-rag": {
"embeddings_provider": {
"openai": {
"endpoint":
"http://127.0.0.1:3623/embeddings",
"api_key": "correct-key"
}
},
"vector_search_provider": {
"azure-ai-search": {
"endpoint":
"http://127.0.0.1:3623/indexes/rag-apisix/docs/search",
"api_key": "correct-key",
"fields": "text_vector",
"select": "chunk",
"k": 10
}
},
"rerank_provider": {
"cohere": {
"endpoint":
"http://127.0.0.1:3623/rerank",
"api_key": "correct-key",
"model": "Cohere-rerank-v4.0-fast",
"top_n": 1
}
},
"input_strategy": "all"
}
},
"upstream": {
"type": "roundrobin",
"nodes": {
"127.0.0.1:1980": 1
},
"scheme": "http",
"pass_host": "node"
}
}]]
)
if code >= 300 then
ngx.status = code
end
ngx.say(body)
}
}
--- response_body
passed
=== TEST 12: Verify Context Injection (With Rerank, input_strategy all)
--- log_level: debug
--- request
POST /echo
{
"messages": [
{
"role": "user",
"content": "What is Apache APISIX?"
},
{
"role": "user",
"content": "Explain it briefly."
}
]
}
--- error_log
Number of documents retrieved: 1
--- response_body eval
qr/Apache APISIX is a dynamic, real-time, high-performance API Gateway.*What
is Apache APISIX\?.*Explain it briefly\./
```
##########
docs/en/latest/plugins/ai-rag.md:
##########
@@ -37,69 +37,40 @@ description: The ai-rag Plugin enhances LLM outputs with
Retrieval-Augmented Gen
The `ai-rag` Plugin provides Retrieval-Augmented Generation (RAG) capabilities
with LLMs. It facilitates the efficient retrieval of relevant documents or
information from external data sources, which are used to enhance the LLM
responses, thereby improving the accuracy and contextual relevance of the
generated outputs.
-The Plugin supports using [Azure
OpenAI](https://azure.microsoft.com/en-us/products/ai-services/openai-service)
and [Azure AI
Search](https://azure.microsoft.com/en-us/products/ai-services/ai-search)
services for generating embeddings and performing vector search.
-
-**_As of now only [Azure
OpenAI](https://azure.microsoft.com/en-us/products/ai-services/openai-service)
and [Azure AI
Search](https://azure.microsoft.com/en-us/products/ai-services/ai-search)
services are supported for generating embeddings and performing vector search
respectively. PRs for introducing support for other service providers are
welcomed._**
+The Plugin supports using
[OpenAI](https://platform.openai.com/docs/api-reference/embeddings) or [Azure
OpenAI](https://learn.microsoft.com/en-us/azure/search/vector-search-how-to-generate-embeddings?tabs=rest-api)
services for generating embeddings, [Azure AI
Search](https://azure.microsoft.com/en-us/products/ai-services/ai-search)
services for performing vector search, and optionally [Cohere
Rerank](https://docs.cohere.com/docs/rerank-overview) services for reranking
the retrieval results.
## Attributes
-| Name | Required | Type |
Description
|
-| ----------------------------------------------- | ------------ | -------- |
-----------------------------------------------------------------------------------------------------------------------------------------
|
-| embeddings_provider | True | object |
Configurations of the embedding models provider.
|
-| embeddings_provider.azure_openai | True | object |
Configurations of [Azure
OpenAI](https://azure.microsoft.com/en-us/products/ai-services/openai-service)
as the embedding models provider. |
-| embeddings_provider.azure_openai.endpoint | True | string |
Azure OpenAI embedding model endpoint.
|
-| embeddings_provider.azure_openai.api_key | True | string |
Azure OpenAI API key.
|
-| vector_search_provider | True | object |
Configuration for the vector search provider.
|
-| vector_search_provider.azure_ai_search | True | object |
Configuration for Azure AI Search.
|
-| vector_search_provider.azure_ai_search.endpoint | True | string |
Azure AI Search endpoint.
|
-| vector_search_provider.azure_ai_search.api_key | True | string |
Azure AI Search API key.
|
-
-## Request Body Format
-
-The following fields must be present in the request body.
-
-| Field | Type | Description
|
-| -------------------- | -------- |
-------------------------------------------------------------------------------------------------------------------------------
|
-| ai_rag | object | Request body RAG specifications.
|
-| ai_rag.embeddings | object | Request parameters required to generate
embeddings. Contents will depend on the API specification of the configured
provider. |
-| ai_rag.vector_search | object | Request parameters required to perform
vector search. Contents will depend on the API specification of the configured
provider. |
-
-- Parameters of `ai_rag.embeddings`
-
- - Azure OpenAI
-
- | Name | Required | Type | Description
|
- | --------------- | ------------ | -------- |
--------------------------------------------------------------------------------------------------------------------------
|
- | input | True | string | Input text used to compute
embeddings, encoded as a string.
|
- | user | False | string | A unique identifier
representing your end-user, which can help in monitoring and detecting abuse.
|
- | encoding_format | False | string | The format to return the
embeddings in. Can be either `float` or `base64`. Defaults to `float`.
|
- | dimensions | False | integer | The number of dimensions
the resulting output embeddings should have. Only supported in text-embedding-3
and later models. |
-
-For other parameters please refer to the [Azure OpenAI embeddings
documentation](https://learn.microsoft.com/en-us/azure/ai-services/openai/reference#embeddings).
-
-- Parameters of `ai_rag.vector_search`
-
- - Azure AI Search
-
- | Field | Required | Type | Description |
- | --------- | ------------ | -------- | ---------------------------- |
- | fields | True | String | Fields for the vector search. |
-
- For other parameters please refer the [Azure AI Search
documentation](https://learn.microsoft.com/en-us/rest/api/searchservice/documents/search-post).
-
-Example request body:
-
-```json
-{
- "ai_rag": {
- "vector_search": { "fields": "contentVector" },
- "embeddings": {
- "input": "which service is good for devops",
- "dimensions": 1024
- }
- }
-}
-```
+| Name | Required | Type | Valid
Values | Description
|
+| ----------------------------------------------- | ------------ | -------- |
--- |
-----------------------------------------------------------------------------------------------------------------------------------------
|
+| embeddings_provider | True | object |
openai, azure-openai, openai-compatible | Configurations of the embedding
models provider. Must and can only specify one. Currently supports `openai`,
`azure-openai`, `openai-compatible`.
|
+| vector_search_provider | True | object |
azure-ai-search | Configuration for the vector search provider.
|
+| vector_search_provider.azure-ai-search | True | object |
| Configuration for Azure AI Search.
|
+| vector_search_provider.azure-ai-search.endpoint | True | string |
| Azure AI Search endpoint.
|
+| vector_search_provider.azure-ai-search.api_key | True | string |
| Azure AI Search API key.
|
+| vector_search_provider.azure-ai-search.fields | True | string |
| Target fields for vector search.
|
+| vector_search_provider.azure-ai-search.select | True | string |
| Fields to select in the response.
|
+| vector_search_provider.azure-ai-search.exhaustive| False | boolean |
| Whether to perform an exhaustive search. Defaults to `true`.
|
+| vector_search_provider.azure-ai-search.k | False | integer |
>0 | Number of nearest neighbors to return. Defaults to 5.
|
+| rerank_provider | False | object |
cohere | Configuration for the rerank provider.
|
+| rerank_provider.cohere | False | object |
| Configuration for Cohere Rerank.
|
+| rerank_provider.cohere.endpoint | False | string |
| Cohere Rerank API endpoint. Defaults to `https://api.cohere.ai/v1/rerank`.
|
+| rerank_provider.cohere.api_key | True | string |
| Cohere API key.
|
+| rerank_provider.cohere.model | False | string |
| Rerank model name. Defaults to `Cohere-rerank-v4.0-fast`.
|
+| rerank_provider.cohere.top_n | False | integer |
| Number of top results to keep after reranking. Defaults to 3.
|
+| rag_config | False | object |
| General configuration for the RAG process.
|
+| rag_config.input_strategy | False | string |
| Strategy for extracting input text from messages. Values: `last` (last user
message), `all` (concatenate all user messages). Defaults to `last`.
|
+
+### embeddings_provider attributes
+
+Currently supports `openai`, `azure-openai`, `openai-compatible`. All
sub-fields are located under the `embeddings_provider.<provider>` object (e.g.,
`embeddings_provider.openai.api_key`).
+
+| Name | Required | Type | Description
|
+|-------------|--------|---------|----------------------------------------------------------------------|
+| `endpoint` | True | string | API service endpoint.<br>? OpenAI:
`https://api.openai.com/v1`<br>? Azure:
`https://<your-resource>.openai.azure.com/` |
Review Comment:
The bullet point formatting in line 70 uses incorrect symbols. The Chinese
documentation uses `?` instead of `•` for bullet points. This should use `•` to
match standard markdown formatting and maintain consistency.
```suggestion
| `endpoint` | True | string | API service endpoint.<br>• OpenAI:
`https://api.openai.com/v1`<br>• Azure:
`https://<your-resource>.openai.azure.com/` |
```
##########
apisix/plugins/ai-rag.lua:
##########
@@ -82,67 +130,141 @@ function _M.check_schema(conf)
end
+local function get_input_text(messages, strategy)
+ if not messages or #messages == 0 then
+ return nil
+ end
+
+ if strategy == input_strategy_enum.last then
+ for i = #messages, 1, -1 do
+ if messages[i].role == "user" then
+ return messages[i].content
+ end
+ end
+ elseif strategy == input_strategy_enum.all then
+ local contents = {}
+ for _, msg in ipairs(messages) do
+ if msg.role == "user" then
+ core.table.insert(contents, msg.content)
+ end
+ end
+ if #contents > 0 then
+ return table.concat(contents, "\n")
+ end
+ end
+ return nil
+end
+
+
+local function load_driver(category, name, cache)
+ local driver = cache[name]
+ if driver then
+ return driver
+ end
+
+ local pkg_path = "apisix.plugins.ai-rag." .. category .. "." .. name
+ local ok, mod = pcall(require, pkg_path)
+ if not ok then
+ return nil, "failed to load module " .. pkg_path .. ", err: " ..
tostring(mod)
+ end
+
+ cache[name] = mod
+ return mod
+end
+
+
+local function inject_context_into_messages(messages, docs)
+ if not docs or #docs == 0 then
+ return
+ end
+
+ local context_str = core.table.concat(docs, "\n\n")
+ local augment = {
+ role = "user",
+ content = "Context:\n" .. context_str
+ }
+ if #messages > 0 then
+ -- Insert context before the last message (which is typically the
user's latest query)
+ -- to ensure the LLM considers the context relevant to the immediate
question.
+ core.table.insert(messages, #messages, augment)
+ else
+ core.table.insert_tail(messages, augment)
+ end
+end
+
+
function _M.access(conf, ctx)
- local httpc = http.new()
local body_tab, err = core.request.get_json_request_body_table()
if not body_tab then
return HTTP_BAD_REQUEST, err
end
- if not body_tab["ai_rag"] then
- core.log.error("request body must have \"ai-rag\" field")
- return HTTP_BAD_REQUEST
- end
-
- local embeddings_provider = next(conf.embeddings_provider)
- local embeddings_provider_conf =
conf.embeddings_provider[embeddings_provider]
- local embeddings_driver = require("apisix.plugins.ai-rag.embeddings." ..
embeddings_provider)
- local vector_search_provider = next(conf.vector_search_provider)
- local vector_search_provider_conf =
conf.vector_search_provider[vector_search_provider]
- local vector_search_driver =
require("apisix.plugins.ai-rag.vector-search." ..
- vector_search_provider)
+ -- 1. Extract Input
+ local rag_conf = conf.rag_config or {}
+ local input_strategy = rag_conf.input_strategy or input_strategy_enum.last
+ local input_text = get_input_text(body_tab.messages, input_strategy)
- local vs_req_schema = vector_search_driver.request_schema
- local emb_req_schema = embeddings_driver.request_schema
+ if not input_text then
+ core.log.warn("no user input found for embedding")
+ return
+ end
- request_schema.properties.ai_rag.properties.vector_search = vs_req_schema
- request_schema.properties.ai_rag.properties.embeddings = emb_req_schema
+ -- 2. Load Drivers
+ local embeddings_provider_name = next(conf.embeddings_provider)
+ local embeddings_conf = conf.embeddings_provider[embeddings_provider_name]
+ local embeddings_driver, err = load_driver("embeddings",
embeddings_provider_name,
+ embeddings_drivers)
+ if not embeddings_driver then
+ core.log.error("failed to load embeddings driver: ", err)
+ return HTTP_INTERNAL_SERVER_ERROR, "failed to load embeddings driver"
+ end
- local ok, err = core.schema.check(request_schema, body_tab)
- if not ok then
- core.log.error("request body fails schema check: ", err)
- return HTTP_BAD_REQUEST
+ local vector_search_provider_name = next(conf.vector_search_provider)
+ local vector_search_conf =
conf.vector_search_provider[vector_search_provider_name]
+ local vector_search_driver, err = load_driver("vector-search",
vector_search_provider_name,
+ vector_search_drivers)
+ if not vector_search_driver then
+ core.log.error("failed to load vector search driver: ", err)
+ return HTTP_INTERNAL_SERVER_ERROR, "failed to load vector search
driver"
end
- local embeddings, status, err =
embeddings_driver.get_embeddings(embeddings_provider_conf,
-
body_tab["ai_rag"].embeddings, httpc)
+ -- 3. Get Embeddings
+ local embeddings, status, err =
embeddings_driver.get_embeddings(embeddings_conf, input_text)
if not embeddings then
core.log.error("could not get embeddings: ", err)
return status, err
end
- local search_body = body_tab["ai_rag"].vector_search
- search_body.embeddings = embeddings
- local res, status, err =
vector_search_driver.search(vector_search_provider_conf,
- search_body, httpc)
- if not res then
+ -- 4. Vector Search
+ local docs, status, err = vector_search_driver.search(vector_search_conf,
embeddings)
+ if not docs then
core.log.error("could not get vector_search result: ", err)
return status, err
end
- -- remove ai_rag from request body because their purpose is served
- -- also, these values will cause failure when proxying requests to LLM.
- body_tab["ai_rag"] = nil
+ -- 5. Rerank
+ if conf.rerank_provider then
+ local rerank_provider_name = next(conf.rerank_provider)
+ local rerank_conf = conf.rerank_provider[rerank_provider_name]
+ local rerank_driver, err = load_driver("rerank", rerank_provider_name,
rerank_drivers)
+
+ if not rerank_driver then
+ core.log.error("failed to load rerank driver: ", err)
+ return HTTP_INTERNAL_SERVER_ERROR, "failed to load rerank driver"
+ end
- if not body_tab.messages then
- body_tab.messages = {}
+ local reranked_docs, err = rerank_driver.rerank(rerank_conf, docs,
input_text)
+ if reranked_docs then
+ docs = reranked_docs
+ else
+ core.log.error("rerank failed: ", err)
+ return HTTP_INTERNAL_SERVER_ERROR, "rerank failed"
Review Comment:
The error handling for rerank is misleading. The code expects
`rerank_driver.rerank()` to return two values (docs, err) on line 256, but the
cohere.lua implementation only returns a single value (docs). This means `err`
will always be nil, and the error handling on lines 260-261 is dead code that
can never execute. The rerank gracefully falls back to original docs on failure
(as intended), but this should be made explicit. Either: 1) Have rerank return
(docs, nil) on success and (docs, err_msg) on fallback, or 2) Remove the dead
error handling code and add a comment explaining the fallback behavior.
```suggestion
-- rerank drivers are expected to handle failures internally and
-- fall back to returning the original docs on error.
local reranked_docs = rerank_driver.rerank(rerank_conf, docs,
input_text)
if reranked_docs then
docs = reranked_docs
```
##########
apisix/plugins/ai-rag/rerank/cohere.lua:
##########
@@ -0,0 +1,117 @@
+--
+-- Licensed to the Apache Software Foundation (ASF) under one or more
+-- contributor license agreements. See the NOTICE file distributed with
+-- this work for additional information regarding copyright ownership.
+-- The ASF licenses this file to You under the Apache License, Version 2.0
+-- (the "License"); you may not use this file except in compliance with
+-- the License. You may obtain a copy of the License at
+--
+-- http://www.apache.org/licenses/LICENSE-2.0
+--
+-- Unless required by applicable law or agreed to in writing, software
+-- distributed under the License is distributed on an "AS IS" BASIS,
+-- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+-- See the License for the specific language governing permissions and
+-- limitations under the License.
+--
+local core = require("apisix.core")
+local http = require("resty.http")
+local type = type
+local ipairs = ipairs
+
+local _M = {}
+
+_M.schema = {
+ type = "object",
+ properties = {
+ endpoint = {
+ type = "string",
+ default = "https://api.cohere.ai/v2/rerank",
+ description = "The endpoint for the Cohere Rerank API."
+ },
+ api_key = {
+ type = "string",
+ description = "The API key for authentication."
+ },
+ model = {
+ type = "string",
+ description = "The model to use for reranking."
+ },
+ top_n = {
+ type = "integer",
+ minimum = 1,
+ default = 3,
+ description = "The number of top results to return."
+ }
+ },
+ required = { "api_key", "model" }
+}
+
+function _M.rerank(conf, docs, query)
+ if not docs or #docs == 0 then
+ return docs
+ end
+
+ local top_n = conf.top_n or 3
+ if #docs <= top_n then
+ return docs
+ end
+
+ -- Construct documents for Cohere Rerank API
+ local documents = {}
+ for _, doc in ipairs(docs) do
+ local doc_content = doc
+ if type(doc) == "table" then
+ doc_content = doc.content or core.json.encode(doc)
+ end
+ core.table.insert(documents, doc_content)
+ end
+
+ local body = {
+ model = conf.model,
+ query = query,
+ top_n = top_n,
+ documents = documents
+ }
+
+ local body_str, err = core.json.encode(body)
+ if not body_str then
+ core.log.error("failed to encode rerank body: ", err)
+ return docs -- fallback
+ end
+
+ local httpc = http.new()
+ local res, err = httpc:request_uri(conf.endpoint, {
+ method = "POST",
+ headers = {
+ ["Content-Type"] = "application/json",
+ ["Authorization"] = "Bearer " .. conf.api_key
+ },
+ body = body_str
+ })
+
+ if not res or res.status ~= 200 then
+ core.log.error("rerank failed: ", err or (res and res.status))
+ return docs -- fallback
+ end
+
+ local res_body = core.json.decode(res.body)
+ if not res_body or not res_body.results then
+ return docs
+ end
+
+ local new_docs = {}
+ for _, result in ipairs(res_body.results) do
+ -- The vector search API returns 0-based indices; Lua tables are
1-based.
Review Comment:
The comment on line 105 states "The vector search API returns 0-based
indices" but this is actually referring to the Cohere Rerank API, not the
vector search API. The comment should be clarified to say "The Cohere Rerank
API returns 0-based indices" for accuracy.
```suggestion
-- The Cohere Rerank API returns 0-based indices; Lua tables are
1-based.
```
##########
apisix/plugins/ai-rag/rerank/cohere.lua:
##########
@@ -0,0 +1,117 @@
+--
+-- Licensed to the Apache Software Foundation (ASF) under one or more
+-- contributor license agreements. See the NOTICE file distributed with
+-- this work for additional information regarding copyright ownership.
+-- The ASF licenses this file to You under the Apache License, Version 2.0
+-- (the "License"); you may not use this file except in compliance with
+-- the License. You may obtain a copy of the License at
+--
+-- http://www.apache.org/licenses/LICENSE-2.0
+--
+-- Unless required by applicable law or agreed to in writing, software
+-- distributed under the License is distributed on an "AS IS" BASIS,
+-- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+-- See the License for the specific language governing permissions and
+-- limitations under the License.
+--
+local core = require("apisix.core")
+local http = require("resty.http")
+local type = type
+local ipairs = ipairs
+
+local _M = {}
+
+_M.schema = {
+ type = "object",
+ properties = {
+ endpoint = {
+ type = "string",
+ default = "https://api.cohere.ai/v2/rerank",
Review Comment:
The default endpoint for Cohere Rerank is set to
`https://api.cohere.ai/v2/rerank` (line 29), but in the documentation examples
(docs/en/latest/plugins/ai-rag.md line 99 and docs/zh/latest/plugins/ai-rag.md
line 99), the default is stated as `https://api.cohere.ai/v1/rerank`. There's
an API version mismatch between the code (v2) and documentation (v1). This
should be consistent.
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: [email protected]
For queries about this service, please contact Infrastructure at:
[email protected]