Skip to content

Commit 77ae426

Browse files
authored
FEATURE: support upload.getUrl in custom tools (#1384)
* FEATURE: support upload.getUrl in custom tools Some tools need to share images with an API. A common pattern is for APIs to expect a URL. This allows converting upload://123123 to a proper CDN friendly URL from within a custom tool * no support for secure uploads, so be explicit about it.
1 parent 9f43df0 commit 77ae426

File tree

3 files changed

+80
-0
lines changed

3 files changed

+80
-0
lines changed

app/models/ai_tool.rb

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -142,6 +142,7 @@ def self.preamble
142142
* base_64_content (string): Base64 encoded content of the file.
143143
* Returns: { id: number, url: string, short_url: string } - Details of the created upload record.
144144
*
145+
* upload.getUrl(shortUrl): Given a short URL, eg upload://12345, returns the full CDN friendly URL of the upload.
145146
* 5. chain
146147
* Controls the execution flow.
147148
*

lib/personas/tool_runner.rb

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,7 @@ def framework_script
7272
7373
const upload = {
7474
create: _upload_create,
75+
getUrl: _upload_get_url,
7576
}
7677
7778
const chain = {
@@ -570,6 +571,24 @@ def attach_discourse(mini_racer_context)
570571
end
571572

572573
def attach_upload(mini_racer_context)
574+
mini_racer_context.attach(
575+
"_upload_get_url",
576+
->(short_url) do
577+
in_attached_function do
578+
return nil if short_url.blank?
579+
580+
sha1 = Upload.sha1_from_short_url(short_url)
581+
return nil if sha1.blank?
582+
583+
upload = Upload.find_by(sha1: sha1)
584+
return nil if upload.nil?
585+
# TODO we may need to introduce an API to unsecure, secure uploads
586+
return nil if upload.secure?
587+
588+
GlobalPath.full_cdn_url(upload.url)
589+
end
590+
end,
591+
)
573592
mini_racer_context.attach(
574593
"_upload_create",
575594
->(filename, base_64_content) do

spec/models/ai_tool_spec.rb

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -675,4 +675,64 @@ def stub_embeddings
675675
expect(ai_persona.temperature).to eq(0.5)
676676
end
677677
end
678+
679+
describe "upload URL resolution" do
680+
it "can resolve upload short URLs to public URLs" do
681+
upload =
682+
Fabricate(
683+
:upload,
684+
sha1: "abcdef1234567890abcdef1234567890abcdef12",
685+
url: "/uploads/default/original/1X/test.jpg",
686+
original_filename: "test.jpg",
687+
)
688+
689+
script = <<~JS
690+
function invoke(params) {
691+
return upload.getUrl(params.short_url);
692+
}
693+
JS
694+
695+
tool = create_tool(script: script)
696+
runner = tool.runner({ "short_url" => upload.short_url }, llm: nil, bot_user: nil)
697+
698+
result = runner.invoke
699+
700+
expect(result).to eq(GlobalPath.full_cdn_url(upload.url))
701+
end
702+
703+
it "returns null for invalid upload short URLs" do
704+
script = <<~JS
705+
function invoke(params) {
706+
return upload.getUrl(params.short_url);
707+
}
708+
JS
709+
710+
tool = create_tool(script: script)
711+
runner = tool.runner({ "short_url" => "upload://invalid" }, llm: nil, bot_user: nil)
712+
713+
result = runner.invoke
714+
715+
expect(result).to be_nil
716+
end
717+
718+
it "returns null for non-existent uploads" do
719+
script = <<~JS
720+
function invoke(params) {
721+
return upload.getUrl(params.short_url);
722+
}
723+
JS
724+
725+
tool = create_tool(script: script)
726+
runner =
727+
tool.runner(
728+
{ "short_url" => "upload://hwmUkTAL9mwhQuRMLsXw6tvDi5C.jpeg" },
729+
llm: nil,
730+
bot_user: nil,
731+
)
732+
733+
result = runner.invoke
734+
735+
expect(result).to be_nil
736+
end
737+
end
678738
end

0 commit comments

Comments
 (0)