Skip to content
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

fix(ai-proxy): (multiple)(AG-154) fixed tools-functions calls coming back empty #13760

Merged
merged 5 commits into from
Nov 25, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
message: "**ai-proxy**: Fixed a bug where tools (function) calls to Anthropic would return empty results."
type: bugfix
scope: Plugin
3 changes: 3 additions & 0 deletions changelog/unreleased/kong/ai-bedrock-fix-function-calling.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
message: "**ai-proxy**: Fixed a bug where tools (function) calls to Bedrock would return empty results."
type: bugfix
scope: Plugin
3 changes: 3 additions & 0 deletions changelog/unreleased/kong/ai-bedrock-fix-guardrails.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
message: "**ai-proxy**: Fixed a bug where Bedrock Guardrail config was ignored."
type: bugfix
scope: Plugin
3 changes: 3 additions & 0 deletions changelog/unreleased/kong/ai-cohere-fix-function-calling.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
message: "**ai-proxy**: Fixed a bug where tools (function) calls to Cohere would return empty results."
type: bugfix
scope: Plugin
3 changes: 3 additions & 0 deletions changelog/unreleased/kong/ai-gemini-blocks-content-safety.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
message: "**ai-proxy**: Fixed a bug where Gemini provider would return an error if content safety failed in AI Proxy."
type: bugfix
scope: Plugin
3 changes: 3 additions & 0 deletions changelog/unreleased/kong/ai-gemini-fix-function-calling.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
message: "**ai-proxy**: Fixed a bug where tools (function) calls to Gemini (or via Vertex) would return empty results."
type: bugfix
scope: Plugin
95 changes: 84 additions & 11 deletions kong/llm/drivers/anthropic.lua
Original file line number Diff line number Diff line change
Expand Up @@ -44,25 +44,54 @@ local function kong_messages_to_claude_prompt(messages)
return buf:get()
end

local inject_tool_calls = function(tool_calls)
local tools
for _, n in ipairs(tool_calls) do
tools = tools or {}
table.insert(tools, {
type = "tool_use",
id = n.id,
name = n["function"].name,
input = cjson.decode(n["function"].arguments)
})
end

return tools
end

-- reuse the messages structure of prompt
-- extract messages and system from kong request
local function kong_messages_to_claude_messages(messages)
local msgs, system, n = {}, nil, 1

for _, v in ipairs(messages) do
if v.role ~= "assistant" and v.role ~= "user" then
if v.role ~= "assistant" and v.role ~= "user" and v.role ~= "tool" then
system = v.content

else
msgs[n] = v
if v.role == "assistant" and v.tool_calls then
msgs[n] = {
role = v.role,
content = inject_tool_calls(v.tool_calls),
}
elseif v.role == "tool" then
msgs[n] = {
role = "user",
content = {{
type = "tool_result",
tool_use_id = v.tool_call_id,
content = v.content
}},
}
else
msgs[n] = v
end
n = n + 1
end
end

return msgs, system
end


local function to_claude_prompt(req)
if req.prompt then
return kong_prompt_to_claude_prompt(req.prompt)
Expand All @@ -83,6 +112,21 @@ local function to_claude_messages(req)
return nil, nil, "request is missing .messages command"
end

local function to_tools(in_tools)
local out_tools = {}

for i, v in ipairs(in_tools) do
if v['function'] then
v['function'].input_schema = v['function'].parameters
v['function'].parameters = nil

table.insert(out_tools, v['function'])
end
end

return out_tools
end

local transformers_to = {
["llm/v1/chat"] = function(request_table, model)
local messages = {}
Expand All @@ -98,6 +142,10 @@ local transformers_to = {
messages.model = model.name or request_table.model
messages.stream = request_table.stream or false -- explicitly set this if nil

-- handle function calling translation from OpenAI format
messages.tools = request_table.tools and to_tools(request_table.tools)
messages.tool_choice = request_table.tool_choice

return messages, "application/json", nil
end,

Expand Down Expand Up @@ -243,16 +291,37 @@ local transformers_from = {
local function extract_text_from_content(content)
local buf = buffer.new()
for i, v in ipairs(content) do
if i ~= 1 then
buf:put("\n")
if v.text then
if i ~= 1 then
buf:put("\n")
end
buf:put(v.text)
end

buf:put(v.text)
end

return buf:tostring()
end

local function extract_tools_from_content(content)
local tools
for i, v in ipairs(content) do
if v.type == "tool_use" then
tools = tools or {}

table.insert(tools, {
id = v.id,
type = "function",
['function'] = {
name = v.name,
arguments = cjson.encode(v.input),
}
})
end
end

return tools
end

if response_table.content then
local usage = response_table.usage

Expand All @@ -275,13 +344,14 @@ local transformers_from = {
message = {
role = "assistant",
content = extract_text_from_content(response_table.content),
tool_calls = extract_tools_from_content(response_table.content)
},
finish_reason = response_table.stop_reason,
},
},
usage = usage,
model = response_table.model,
object = "chat.content",
object = "chat.completion",
}

return cjson.encode(res)
Expand Down Expand Up @@ -330,7 +400,10 @@ function _M.from_format(response_string, model_info, route_type)
end

local ok, response_string, err, metadata = pcall(transform, response_string, model_info, route_type)
if not ok or err then
if not ok then
err = response_string
end
if err then
return nil, fmt("transformation failed from type %s://%s: %s",
model_info.provider,
route_type,
Expand Down Expand Up @@ -488,7 +561,7 @@ function _M.configure_request(conf)
end
end

-- if auth_param_location is "form", it will have already been set in a pre-request hook
-- if auth_param_location is "body", it will have already been set in a pre-request hook
return true, nil
end

Expand Down
Loading
Loading