Skip to content

Commit 90a63c4

Browse files
Add sys.download
1 parent fdd9404 commit 90a63c4

File tree

3 files changed

+83
-0
lines changed

3 files changed

+83
-0
lines changed

examples/bob.gpt

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
tools: bob
2+
3+
Ask Bob how he is doing and let me know exactly what he said.
4+
5+
---
6+
name: bob
7+
description: I'm Bob, a friendly guy.
8+
9+
When asked how I am doing, respond with "I'm doing great fellow friendly AI tool!"

examples/sqlite-download.gpt

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
tools: sys.download, sys.exec
2+
3+
Download https://www.sqlitetutorial.net/wp-content/uploads/2018/03/chinook.zip to the
4+
local directory. Expand the archive as there is a sqlite database in it.
5+
6+
First inspect the schema of the database to understand the table structure.
7+
8+
Form and run a SQL query to find the artist with the most number of albums and output
9+
the result of that.

pkg/builtin/builtin.go

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ package builtin
33
import (
44
"context"
55
"encoding/json"
6+
"errors"
67
"fmt"
78
"io"
89
"io/fs"
@@ -80,6 +81,13 @@ var tools = map[string]types.Tool{
8081
"name", "The environment variable name to lookup"),
8182
BuiltinFunc: SysGetenv,
8283
},
84+
"sys.download": {
85+
Description: "Downloads a URL, saving the contents to disk at a given location",
86+
Arguments: types.ObjectSchema(
87+
"url", "The URL to download, either http or https.",
88+
"location", "(optional) The on disk location to store the file. If no location is specified a temp location will be used. If the target file already exists it will not be overwritten and will fail."),
89+
BuiltinFunc: SysDownload,
90+
},
8391
}
8492

8593
func SysProgram() *types.Program {
@@ -308,3 +316,60 @@ func SysAbort(ctx context.Context, env []string, input string) (string, error) {
308316
}
309317
return "", fmt.Errorf("ABORT: %s", params.Message)
310318
}
319+
320+
func SysDownload(ctx context.Context, env []string, input string) (string, error) {
321+
var params struct {
322+
URL string `json:"url,omitempty"`
323+
Location string `json:"location,omitempty"`
324+
}
325+
if err := json.Unmarshal([]byte(input), &params); err != nil {
326+
return "", err
327+
}
328+
329+
checkExists := true
330+
if params.Location == "" {
331+
f, err := os.CreateTemp("", "gpt-download")
332+
if err != nil {
333+
return "", err
334+
}
335+
if err := f.Close(); err != nil {
336+
return "", err
337+
}
338+
checkExists = false
339+
params.Location = f.Name()
340+
}
341+
342+
if checkExists {
343+
if _, err := os.Stat(params.Location); err == nil {
344+
return "", fmt.Errorf("file %s already exists and can not be overwritten: %w", params.Location, err)
345+
} else if err != nil && !errors.Is(err, fs.ErrNotExist) {
346+
return "", err
347+
}
348+
}
349+
350+
log.Infof("download [%s] to [%s]", params.URL, params.Location)
351+
resp, err := http.Get(params.URL)
352+
if err != nil {
353+
return "", err
354+
}
355+
defer func() {
356+
_, _ = io.ReadAll(resp.Body)
357+
_ = resp.Body.Close()
358+
}()
359+
360+
if resp.StatusCode > 299 {
361+
return "", fmt.Errorf("invalid status code [%d] downloading [%s]: %s", resp.StatusCode, params.URL, resp.Status)
362+
}
363+
364+
f, err := os.Create(params.Location)
365+
if err != nil {
366+
return "", fmt.Errorf("failed to create [%s]: %w", params.Location, err)
367+
}
368+
defer f.Close()
369+
370+
if _, err := io.Copy(f, resp.Body); err != nil {
371+
return "", fmt.Errorf("failed copying data from [%s] to [%s]: %w", params.URL, params.Location, err)
372+
}
373+
374+
return params.Location, nil
375+
}

0 commit comments

Comments
 (0)