feat: ai-proxy plugin#11499
Conversation
f5d902f to
b98a48f
Compare
0af5364 to
f25f21a
Compare
| end | ||
|
|
||
|
|
||
|
|
There was a problem hiding this comment.
If there are no substantive changes, please do not change the code style.
| if header_auth == "Bearer token" or query_auth == "apikey" then | ||
| ngx.req.read_body() | ||
| local body, err = ngx.req.get_body_data() | ||
| local esc = body:gsub('"\\\""', '\"') |
There was a problem hiding this comment.
Is this still necessary? Just a confirmation that if it is then no changes are needed.
| }' | ||
| ``` | ||
|
|
||
| Since `passthrough` is not enabled upstream node can be any arbitrary value because it won't be contacted. |
There was a problem hiding this comment.
- design question: why do we need the parameter
passthrough? is it meant to be a toggle? - is this sentence needed here?
There was a problem hiding this comment.
design question: why do we need the parameter passthrough? is it meant to be a toggle?
yes, kinda. when it's enabled, the response from LLM will be set as the request body to the upstream.
is this sentence needed here?
I think it's important to let users know, instead to let them be puzzled about it and then figure out later.
| "plugins/degraphql", | ||
| "plugins/body-transformer" | ||
| "plugins/body-transformer", | ||
| "plugins/ai-proxy" |
There was a problem hiding this comment.
The recently added ai-* plugins should probably all go into a new category. Can be amended in a different PR.
Co-authored-by: Traky Deng <[email protected]>
| end | ||
|
|
||
|
|
||
| function _M.get_request_body_table() |
There was a problem hiding this comment.
Decode body according content-type or rename the method, e.g. get_json_request_body_table?
| return 400, "unsupported content-type: " .. ct | ||
| end | ||
|
|
||
| local request_table, err = core.request.get_request_body_table() |
There was a problem hiding this comment.
new naming style?
request_table, count_num, flag_boolean?
There was a problem hiding this comment.
I don't understand what you mean.
There was a problem hiding this comment.
Although adding a suffix makes it more readable, it is not the style used in the project.
There was a problem hiding this comment.
oh I got it now 😂 . request_table makes it more readable and it can be understood that it's not a string.
| return | ||
| end | ||
|
|
||
| if core.table.try_read_attr(conf, "model", "options", "stream") then |
There was a problem hiding this comment.
| if core.table.try_read_attr(conf, "model", "options", "stream") then | |
| if request_table.stream then |
| local res, err, httpc = ai_driver.request(conf, request_table, ctx) | ||
| if not res then | ||
| core.log.error("failed to send request to AI service: ", err) | ||
| return 500 |
There was a problem hiding this comment.
use the predefined Ngx constants?
ngx.HTTP_NOT_ACCEPTABLE = 406
ngx.HTTP_REQUEST_TIMEOUT = 408
ngx.HTTP_CONFLICT = 409
ngx.HTTP_GONE = 410
ngx.HTTP_UPGRADE_REQUIRED = 426
ngx.HTTP_TOO_MANY_REQUESTS = 429
ngx.HTTP_CLOSE = 444
ngx.HTTP_ILLEGAL = 451
ngx.HTTP_INTERNAL_SERVER_ERROR = 500
ngx.HTTP_METHOD_NOT_IMPLEMENTED = 501
ngx.HTTP_BAD_GATEWAY = 502
ngx.HTTP_SERVICE_UNAVAILABLE = 503
ngx.HTTP_GATEWAY_TIMEOUT = 504
ngx.HTTP_VERSION_NOT_SUPPORTED = 505
ngx.HTTP_INSUFFICIENT_STORAGE = 507
| local body_reader = res.body_reader | ||
| if not body_reader then | ||
| core.log.error("LLM sent no response body") | ||
| return 500 |
| end | ||
|
|
||
| local query_params = "" | ||
| if conf.auth.query and type(conf.auth.query) == "table" then |
There was a problem hiding this comment.
| if conf.auth.query and type(conf.auth.query) == "table" then | |
| if type(conf.auth.query) == "table" then |
| end | ||
| end | ||
|
|
||
| local path = (parsed_url.path or "/v1/chat/completions") .. query_params |
There was a problem hiding this comment.
| local path = (parsed_url.path or "/v1/chat/completions") .. query_params | |
| local path = (parsed_url.path or DEFAULT_URI) .. query_params |
|
|
||
| local query_params = "" | ||
| if conf.auth.query and type(conf.auth.query) == "table" then | ||
| query_params = core.string.encode_args(conf.auth.query) |
There was a problem hiding this comment.
FYI, from lua-resty-http, params.query already support table type
local function _format_request(self, params)
local version = params.version
local headers = params.headers or {}
local query = params.query or ""
if type(query) == "table" then
query = "?" .. ngx_encode_args(query)
elseif query ~= "" and str_sub(query, 1, 1) ~= "?" then
query = "?" .. query
end
-- Initialize request
local req = {
str_upper(params.method),
" ",
self.path_prefix or "",
params.path,
query,
HTTP[version],
-- Pre-allocate slots for minimum headers and carriage return.
"",
"",
"",
}
local c = 7 -- req table index it's faster to do this inline vs table.insert
-- Append headers
for key, values in pairs(headers) do
key = tostring(key)
if type(values) == "table" then
for _, value in pairs(values) do
req[c] = key .. ": " .. tostring(value) .. "\r\n"
c = c + 1
end
else
req[c] = key .. ": " .. tostring(values) .. "\r\n"
c = c + 1
end
end
-- Close headers
req[c] = "\r\n"
return tbl_concat(req)
end
| fmt.Fprintf(w, "data: %s\n\n", time.Now().Format(time.RFC3339)) | ||
|
|
||
| // Flush the data immediately to the client | ||
| if f, ok := w.(http.Flusher); ok { |
There was a problem hiding this comment.
do type assertion outside the loop? :D
|
|
||
| local res, err = httpc:request(params) | ||
| if not res then | ||
| return 500, "failed to send request to LLM server: " .. err |
There was a problem hiding this comment.
| return 500, "failed to send request to LLM server: " .. err | |
| return internal_server_error, "failed to send request to LLM server: " .. err |
| fmt.Fprintf(w, "[ERROR]") | ||
| return | ||
| } | ||
| // A simple loop that sends a message every 2 seconds |
There was a problem hiding this comment.
| // A simple loop that sends a message every 2 seconds | |
| // A simple loop that sends a message every 500ms |
Parameterize it?
Description
Implement the AI-Proxy Plugin.
Checklist