mirror of
https://github.com/maybe-finance/maybe.git
synced 2025-08-10 07:55:21 +02:00
Start refactor
This commit is contained in:
parent
67716f3006
commit
5ab59d44aa
4 changed files with 277 additions and 0 deletions
56
app/models/provider/openai/function_caller.rb
Normal file
56
app/models/provider/openai/function_caller.rb
Normal file
|
@ -0,0 +1,56 @@
|
|||
class Provider::Openai::FunctionCaller
|
||||
def initialize(functions)
|
||||
@functions = functions
|
||||
end
|
||||
|
||||
def openai_tools
|
||||
functions.map do |fn|
|
||||
{
|
||||
type: "function",
|
||||
name: fn.name,
|
||||
description: fn.description,
|
||||
parameters: fn.params_schema,
|
||||
strict: fn.strict_mode?
|
||||
}
|
||||
end
|
||||
end
|
||||
|
||||
def build_results_input(function_calls)
|
||||
function_calls.map do |fc|
|
||||
{
|
||||
type: "function_call_output",
|
||||
call_id: fc.provider_call_id,
|
||||
output: fc.result.to_json
|
||||
}
|
||||
end
|
||||
end
|
||||
|
||||
def fulfill_request(function_request)
|
||||
fn_name = function_request[:name]
|
||||
fn_args = JSON.parse(function_request[:arguments])
|
||||
fn = get_function(fn_name)
|
||||
result = fn.call(fn_args)
|
||||
|
||||
Provider::LlmProvider::FunctionCall.new(
|
||||
provider_id: function_request[:id],
|
||||
provider_call_id: function_request[:call_id],
|
||||
name: fn_name,
|
||||
arguments: fn_args,
|
||||
result: result
|
||||
)
|
||||
rescue => e
|
||||
fn_execution_details = {
|
||||
fn_name: fn_name,
|
||||
fn_args: fn_args
|
||||
}
|
||||
|
||||
raise Provider::Openai::Error.new(e, fn_execution_details)
|
||||
end
|
||||
|
||||
private
|
||||
attr_reader :functions
|
||||
|
||||
def get_function(name)
|
||||
functions.find { |f| f.name == name }
|
||||
end
|
||||
end
|
41
app/models/provider/openai/parser.rb
Normal file
41
app/models/provider/openai/parser.rb
Normal file
|
@ -0,0 +1,41 @@
|
|||
module Provider::Openai::Parser
|
||||
extend ActiveSupport::Concern
|
||||
|
||||
private
|
||||
def extract_id(chat_response)
|
||||
chat_response.dig("id")
|
||||
end
|
||||
|
||||
def extract_model(chat_response)
|
||||
chat_response.dig("model")
|
||||
end
|
||||
|
||||
def extract_messages(chat_response)
|
||||
message_items = chat_response.dig("output").filter { |item| item.dig("type") == "message" }
|
||||
|
||||
message_items.map do |message_item|
|
||||
output_text = message_item.dig("content").map do |content|
|
||||
text = content.dig("text")
|
||||
refusal = content.dig("refusal")
|
||||
|
||||
text || refusal
|
||||
end.flatten.join("\n")
|
||||
|
||||
{
|
||||
id: message_item.dig("id"),
|
||||
output_text: output_text
|
||||
}
|
||||
end
|
||||
end
|
||||
|
||||
def extract_function_requests(chat_response)
|
||||
chat_response.dig("output").filter { |item| item.dig("type") == "function_call" }.map do |function_call|
|
||||
{
|
||||
id: function_call.dig("id"),
|
||||
call_id: function_call.dig("call_id"),
|
||||
name: function_call.dig("name"),
|
||||
arguments: function_call.dig("arguments")
|
||||
}
|
||||
end
|
||||
end
|
||||
end
|
92
test/vcr_cassettes/openai/chat/basic_streaming_response.yml
Normal file
92
test/vcr_cassettes/openai/chat/basic_streaming_response.yml
Normal file
|
@ -0,0 +1,92 @@
|
|||
---
|
||||
http_interactions:
|
||||
- request:
|
||||
method: post
|
||||
uri: https://api.openai.com/v1/responses
|
||||
body:
|
||||
encoding: UTF-8
|
||||
string: '{"model":"gpt-4o","input":[{"role":"user","content":"This is a chat
|
||||
test. If it''s working, respond with a single word: Yes"}],"instructions":null,"tools":[],"previous_response_id":null,"stream":true}'
|
||||
headers:
|
||||
Content-Type:
|
||||
- application/json
|
||||
Authorization:
|
||||
- Bearer <OPENAI_ACCESS_TOKEN>
|
||||
Accept-Encoding:
|
||||
- gzip;q=1.0,deflate;q=0.6,identity;q=0.3
|
||||
Accept:
|
||||
- "*/*"
|
||||
User-Agent:
|
||||
- Ruby
|
||||
response:
|
||||
status:
|
||||
code: 200
|
||||
message: OK
|
||||
headers:
|
||||
Date:
|
||||
- Fri, 28 Mar 2025 15:39:24 GMT
|
||||
Content-Type:
|
||||
- text/event-stream; charset=utf-8
|
||||
Transfer-Encoding:
|
||||
- chunked
|
||||
Connection:
|
||||
- keep-alive
|
||||
Openai-Version:
|
||||
- '2020-10-01'
|
||||
Openai-Organization:
|
||||
- "<OPENAI_ORGANIZATION_ID>"
|
||||
X-Request-Id:
|
||||
- req_d322d0e3734ad9d7556c6acb446fc5f0
|
||||
Openai-Processing-Ms:
|
||||
- '108'
|
||||
Strict-Transport-Security:
|
||||
- max-age=31536000; includeSubDomains; preload
|
||||
Cf-Cache-Status:
|
||||
- DYNAMIC
|
||||
Set-Cookie:
|
||||
- __cf_bm=i3l062va7vbmU7I3NufOoYNcofDavIJwJDRXwQ0CXck-1743176364-1.0.1.1-BWKA2yUjk0GI0pHrKSQ0eaTFa6Ysu5ysjy__ArpQaO1b3LEMatIliQ6rQEMN6M_eH7XfbV.nokO3hqCjRcg9yPgzKLjbQ3hpRcvQwMWObCE;
|
||||
path=/; expires=Fri, 28-Mar-25 16:09:24 GMT; domain=.api.openai.com; HttpOnly;
|
||||
Secure; SameSite=None
|
||||
- _cfuvid=1VMIq2Dlu_MQEWybPb9LkaURnoyi7_O2ueZQyKKUGss-1743176364050-0.0.1.1-604800000;
|
||||
path=/; domain=.api.openai.com; HttpOnly; Secure; SameSite=None
|
||||
X-Content-Type-Options:
|
||||
- nosniff
|
||||
Server:
|
||||
- cloudflare
|
||||
Cf-Ray:
|
||||
- 9278384d09ec0042-ORD
|
||||
Alt-Svc:
|
||||
- h3=":443"; ma=86400
|
||||
body:
|
||||
encoding: UTF-8
|
||||
string: |+
|
||||
event: response.created
|
||||
data: {"type":"response.created","response":{"id":"resp_67e6c2abe9188192aad5b26bece9136f0e544b3cddf93be3","object":"response","created_at":1743176363,"status":"in_progress","error":null,"incomplete_details":null,"instructions":null,"max_output_tokens":null,"model":"gpt-4o-2024-08-06","output":[],"parallel_tool_calls":true,"previous_response_id":null,"reasoning":{"effort":null,"generate_summary":null},"store":true,"temperature":1.0,"text":{"format":{"type":"text"}},"tool_choice":"auto","tools":[],"top_p":1.0,"truncation":"disabled","usage":null,"user":null,"metadata":{}}}
|
||||
|
||||
event: response.in_progress
|
||||
data: {"type":"response.in_progress","response":{"id":"resp_67e6c2abe9188192aad5b26bece9136f0e544b3cddf93be3","object":"response","created_at":1743176363,"status":"in_progress","error":null,"incomplete_details":null,"instructions":null,"max_output_tokens":null,"model":"gpt-4o-2024-08-06","output":[],"parallel_tool_calls":true,"previous_response_id":null,"reasoning":{"effort":null,"generate_summary":null},"store":true,"temperature":1.0,"text":{"format":{"type":"text"}},"tool_choice":"auto","tools":[],"top_p":1.0,"truncation":"disabled","usage":null,"user":null,"metadata":{}}}
|
||||
|
||||
event: response.output_item.added
|
||||
data: {"type":"response.output_item.added","output_index":0,"item":{"type":"message","id":"msg_67e6c2ac6df08192977335b57cb9e93b0e544b3cddf93be3","status":"in_progress","role":"assistant","content":[]}}
|
||||
|
||||
event: response.content_part.added
|
||||
data: {"type":"response.content_part.added","item_id":"msg_67e6c2ac6df08192977335b57cb9e93b0e544b3cddf93be3","output_index":0,"content_index":0,"part":{"type":"output_text","text":"","annotations":[]}}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","item_id":"msg_67e6c2ac6df08192977335b57cb9e93b0e544b3cddf93be3","output_index":0,"content_index":0,"delta":"Yes"}
|
||||
|
||||
event: response.output_text.done
|
||||
data: {"type":"response.output_text.done","item_id":"msg_67e6c2ac6df08192977335b57cb9e93b0e544b3cddf93be3","output_index":0,"content_index":0,"text":"Yes"}
|
||||
|
||||
event: response.content_part.done
|
||||
data: {"type":"response.content_part.done","item_id":"msg_67e6c2ac6df08192977335b57cb9e93b0e544b3cddf93be3","output_index":0,"content_index":0,"part":{"type":"output_text","text":"Yes","annotations":[]}}
|
||||
|
||||
event: response.output_item.done
|
||||
data: {"type":"response.output_item.done","output_index":0,"item":{"type":"message","id":"msg_67e6c2ac6df08192977335b57cb9e93b0e544b3cddf93be3","status":"completed","role":"assistant","content":[{"type":"output_text","text":"Yes","annotations":[]}]}}
|
||||
|
||||
event: response.completed
|
||||
data: {"type":"response.completed","response":{"id":"resp_67e6c2abe9188192aad5b26bece9136f0e544b3cddf93be3","object":"response","created_at":1743176363,"status":"completed","error":null,"incomplete_details":null,"instructions":null,"max_output_tokens":null,"model":"gpt-4o-2024-08-06","output":[{"type":"message","id":"msg_67e6c2ac6df08192977335b57cb9e93b0e544b3cddf93be3","status":"completed","role":"assistant","content":[{"type":"output_text","text":"Yes","annotations":[]}]}],"parallel_tool_calls":true,"previous_response_id":null,"reasoning":{"effort":null,"generate_summary":null},"store":true,"temperature":1.0,"text":{"format":{"type":"text"}},"tool_choice":"auto","tools":[],"top_p":1.0,"truncation":"disabled","usage":{"input_tokens":43,"input_tokens_details":{"cached_tokens":0},"output_tokens":2,"output_tokens_details":{"reasoning_tokens":0},"total_tokens":45},"user":null,"metadata":{}}}
|
||||
|
||||
recorded_at: Fri, 28 Mar 2025 15:39:24 GMT
|
||||
recorded_with: VCR 6.3.1
|
||||
...
|
88
test/vcr_cassettes/openai/chat/streaming_tool_calls.yml
Normal file
88
test/vcr_cassettes/openai/chat/streaming_tool_calls.yml
Normal file
|
@ -0,0 +1,88 @@
|
|||
---
|
||||
http_interactions:
|
||||
- request:
|
||||
method: post
|
||||
uri: https://api.openai.com/v1/responses
|
||||
body:
|
||||
encoding: UTF-8
|
||||
string: '{"model":"gpt-4o","input":[{"role":"user","content":"What is my net
|
||||
worth?"}],"instructions":"Use the tools available to you to answer the user''s
|
||||
question.","tools":[{"type":"function","name":"get_net_worth","description":"Gets
|
||||
user net worth data","parameters":{"type":"object","properties":{},"required":[],"additionalProperties":false},"strict":true}],"previous_response_id":null,"stream":true}'
|
||||
headers:
|
||||
Content-Type:
|
||||
- application/json
|
||||
Authorization:
|
||||
- Bearer <OPENAI_ACCESS_TOKEN>
|
||||
Accept-Encoding:
|
||||
- gzip;q=1.0,deflate;q=0.6,identity;q=0.3
|
||||
Accept:
|
||||
- "*/*"
|
||||
User-Agent:
|
||||
- Ruby
|
||||
response:
|
||||
status:
|
||||
code: 200
|
||||
message: OK
|
||||
headers:
|
||||
Date:
|
||||
- Fri, 28 Mar 2025 16:00:21 GMT
|
||||
Content-Type:
|
||||
- text/event-stream; charset=utf-8
|
||||
Transfer-Encoding:
|
||||
- chunked
|
||||
Connection:
|
||||
- keep-alive
|
||||
Openai-Version:
|
||||
- '2020-10-01'
|
||||
Openai-Organization:
|
||||
- "<OPENAI_ORGANIZATION_ID>"
|
||||
X-Request-Id:
|
||||
- req_ad078d9f7585eeca17c02cefb4c42180
|
||||
Openai-Processing-Ms:
|
||||
- '148'
|
||||
Strict-Transport-Security:
|
||||
- max-age=31536000; includeSubDomains; preload
|
||||
Cf-Cache-Status:
|
||||
- DYNAMIC
|
||||
Set-Cookie:
|
||||
- __cf_bm=DeYZEoQ8_t7.BWV67SmSSxrDr9sFE5nnkoANG7Nlg9E-1743177621-1.0.1.1-ymQrM3gryBLo9KRlSh4lzlyMPwqDX1SeHkwfiWOKaNct.cKs.WBfgURo.mAmK14ZjlbbuwfPOf7FShuzpS2Irud6C3woK9juoi2rOtdcYGo;
|
||||
path=/; expires=Fri, 28-Mar-25 16:30:21 GMT; domain=.api.openai.com; HttpOnly;
|
||||
Secure; SameSite=None
|
||||
- _cfuvid=WpIK2gkR1WKCUZWHMZKlTwVW73ibegrhn1ZhIpdswe0-1743177621976-0.0.1.1-604800000;
|
||||
path=/; domain=.api.openai.com; HttpOnly; Secure; SameSite=None
|
||||
X-Content-Type-Options:
|
||||
- nosniff
|
||||
Server:
|
||||
- cloudflare
|
||||
Cf-Ray:
|
||||
- 92785706590bcf4e-CMH
|
||||
Alt-Svc:
|
||||
- h3=":443"; ma=86400
|
||||
body:
|
||||
encoding: UTF-8
|
||||
string: |+
|
||||
event: response.created
|
||||
data: {"type":"response.created","response":{"id":"resp_67e6c795ca8c8192b015934c18b8abe602f810c3e60dc7d6","object":"response","created_at":1743177621,"status":"in_progress","error":null,"incomplete_details":null,"instructions":"Use the tools available to you to answer the user's question.","max_output_tokens":null,"model":"gpt-4o-2024-08-06","output":[],"parallel_tool_calls":true,"previous_response_id":null,"reasoning":{"effort":null,"generate_summary":null},"store":true,"temperature":1.0,"text":{"format":{"type":"text"}},"tool_choice":"auto","tools":[{"type":"function","description":"Gets user net worth data","name":"get_net_worth","parameters":{"type":"object","properties":{},"required":[],"additionalProperties":false},"strict":true}],"top_p":1.0,"truncation":"disabled","usage":null,"user":null,"metadata":{}}}
|
||||
|
||||
event: response.in_progress
|
||||
data: {"type":"response.in_progress","response":{"id":"resp_67e6c795ca8c8192b015934c18b8abe602f810c3e60dc7d6","object":"response","created_at":1743177621,"status":"in_progress","error":null,"incomplete_details":null,"instructions":"Use the tools available to you to answer the user's question.","max_output_tokens":null,"model":"gpt-4o-2024-08-06","output":[],"parallel_tool_calls":true,"previous_response_id":null,"reasoning":{"effort":null,"generate_summary":null},"store":true,"temperature":1.0,"text":{"format":{"type":"text"}},"tool_choice":"auto","tools":[{"type":"function","description":"Gets user net worth data","name":"get_net_worth","parameters":{"type":"object","properties":{},"required":[],"additionalProperties":false},"strict":true}],"top_p":1.0,"truncation":"disabled","usage":null,"user":null,"metadata":{}}}
|
||||
|
||||
event: response.output_item.added
|
||||
data: {"type":"response.output_item.added","output_index":0,"item":{"type":"function_call","id":"fc_67e6c796cbdc81928207d8649a4da80a02f810c3e60dc7d6","call_id":"call_ZpaYGc3RGRfles9vnoFgVQ1m","name":"get_net_worth","arguments":"","status":"in_progress"}}
|
||||
|
||||
event: response.function_call_arguments.delta
|
||||
data: {"type":"response.function_call_arguments.delta","item_id":"fc_67e6c796cbdc81928207d8649a4da80a02f810c3e60dc7d6","output_index":0,"delta":"{}"}
|
||||
|
||||
event: response.function_call_arguments.done
|
||||
data: {"type":"response.function_call_arguments.done","item_id":"fc_67e6c796cbdc81928207d8649a4da80a02f810c3e60dc7d6","output_index":0,"arguments":"{}"}
|
||||
|
||||
event: response.output_item.done
|
||||
data: {"type":"response.output_item.done","output_index":0,"item":{"type":"function_call","id":"fc_67e6c796cbdc81928207d8649a4da80a02f810c3e60dc7d6","call_id":"call_ZpaYGc3RGRfles9vnoFgVQ1m","name":"get_net_worth","arguments":"{}","status":"completed"}}
|
||||
|
||||
event: response.completed
|
||||
data: {"type":"response.completed","response":{"id":"resp_67e6c795ca8c8192b015934c18b8abe602f810c3e60dc7d6","object":"response","created_at":1743177621,"status":"completed","error":null,"incomplete_details":null,"instructions":"Use the tools available to you to answer the user's question.","max_output_tokens":null,"model":"gpt-4o-2024-08-06","output":[{"type":"function_call","id":"fc_67e6c796cbdc81928207d8649a4da80a02f810c3e60dc7d6","call_id":"call_ZpaYGc3RGRfles9vnoFgVQ1m","name":"get_net_worth","arguments":"{}","status":"completed"}],"parallel_tool_calls":true,"previous_response_id":null,"reasoning":{"effort":null,"generate_summary":null},"store":true,"temperature":1.0,"text":{"format":{"type":"text"}},"tool_choice":"auto","tools":[{"type":"function","description":"Gets user net worth data","name":"get_net_worth","parameters":{"type":"object","properties":{},"required":[],"additionalProperties":false},"strict":true}],"top_p":1.0,"truncation":"disabled","usage":{"input_tokens":271,"input_tokens_details":{"cached_tokens":0},"output_tokens":13,"output_tokens_details":{"reasoning_tokens":0},"total_tokens":284},"user":null,"metadata":{}}}
|
||||
|
||||
recorded_at: Fri, 28 Mar 2025 16:00:23 GMT
|
||||
recorded_with: VCR 6.3.1
|
||||
...
|
Loading…
Add table
Add a link
Reference in a new issue