diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..03caf97 --- /dev/null +++ b/.gitignore @@ -0,0 +1,4 @@ +test.gpt +bin/ +.idea/ +.vscode/ diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..9501a95 --- /dev/null +++ b/Makefile @@ -0,0 +1,3 @@ +.PHONY: build +build: + CGO_ENABLED=0 go build -o bin/gptscript-go-tool -tags "${GO_TAGS}" -ldflags "-s -w" . diff --git a/go.mod b/go.mod new file mode 100644 index 0000000..98353e6 --- /dev/null +++ b/go.mod @@ -0,0 +1,11 @@ +module github.com/gptscript-ai/datasets + +go 1.23.2 + +require github.com/stretchr/testify v1.9.0 + +require ( + github.com/davecgh/go-spew v1.1.1 // indirect + github.com/pmezard/go-difflib v1.0.0 // indirect + gopkg.in/yaml.v3 v3.0.1 // indirect +) diff --git a/go.sum b/go.sum new file mode 100644 index 0000000..60ce688 --- /dev/null +++ b/go.sum @@ -0,0 +1,10 @@ +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= +github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/main.go b/main.go new file mode 100644 index 0000000..a8e0a3e --- /dev/null +++ b/main.go @@ -0,0 +1,38 @@ +package main + +import ( + "fmt" + "os" + + "github.com/gptscript-ai/datasets/pkg/tools" +) + +func main() { + if len(os.Args) < 2 { + fmt.Println(`usage: gptscript-go-tool +subcommands: listDatasets, listElements, getElement, createDataset, addElement +env vars: GPTSCRIPT_WORKSPACE_DIR`) + } + + workspace := os.Getenv("GPTSCRIPT_WORKSPACE_DIR") + if workspace == "" { + fmt.Println("missing GPTSCRIPT_WORKSPACE_DIR") + os.Exit(1) + } + + switch os.Args[1] { + case "listDatasets": + tools.ListDatasets(workspace) + case "listElements": + tools.ListElements(workspace, os.Getenv("DATASETID")) + case "getElement": + tools.GetElement(workspace, os.Getenv("DATASETID"), os.Getenv("ELEMENT")) + case "createDataset": + tools.CreateDataset(workspace, os.Getenv("DATASETNAME"), os.Getenv("DATASETDESCRIPTION")) + case "addElement": + tools.AddElement(workspace, os.Getenv("DATASETID"), os.Getenv("ELEMENTNAME"), os.Getenv("ELEMENTDESCRIPTION"), []byte(os.Getenv("ELEMENTCONTENT"))) + default: + fmt.Printf("unknown command: %s\n", os.Args[1]) + os.Exit(1) + } +} diff --git a/pkg/dataset/dataset.go b/pkg/dataset/dataset.go new file mode 100644 index 0000000..e9a4598 --- /dev/null +++ b/pkg/dataset/dataset.go @@ -0,0 +1,110 @@ +package dataset + +import ( + "encoding/json" + "fmt" + "os" + "path/filepath" + + "github.com/gptscript-ai/datasets/pkg/util" +) + +type ElementMeta struct { + Name string `json:"name"` + Description string `json:"description"` +} + +type Element struct { + ElementMeta `json:",inline"` + File string `json:"file"` +} + +type DatasetMeta struct { + ID string `json:"id"` + Name string `json:"name"` + Description string `json:"description"` +} + +type Dataset struct { + DatasetMeta `json:",inline"` + BaseDir string `json:"baseDir,omitempty"` + Elements map[string]Element `json:"elements"` +} + +func (d *Dataset) GetID() string { + return d.ID +} + +func (d *Dataset) GetName() string { + return d.Name +} + +func (d *Dataset) GetDescription() string { + return d.Description +} + +func (d *Dataset) GetLength() int { + return len(d.Elements) +} + +func (d *Dataset) ListElements() []ElementMeta { + var elements []ElementMeta + for _, element := range d.Elements { + elements = append(elements, element.ElementMeta) + } + return elements +} + +func (d *Dataset) GetElement(name string) ([]byte, Element, error) { + e, exists := d.Elements[name] + if !exists { + return nil, Element{}, fmt.Errorf("element %s not found", name) + } + + contents, err := os.ReadFile(d.BaseDir + string(os.PathSeparator) + e.File) + if err != nil { + return nil, Element{}, fmt.Errorf("failed to read element %s: %w", name, err) + } + + return contents, e, nil +} + +func (d *Dataset) AddElement(name, description string, contents []byte) (Element, error) { + if _, exists := d.Elements[name]; exists { + return Element{}, fmt.Errorf("element %s already exists", name) + } + + fileName, err := util.EnsureUniqueFilename(d.BaseDir, util.ToFileName(name)) + if err != nil { + return Element{}, fmt.Errorf("failed to generate unique file name: %w", err) + } + + loc := filepath.Join(d.BaseDir, fileName) + if err := os.WriteFile(loc, contents, 0644); err != nil { + return Element{}, fmt.Errorf("failed to write element %s: %w", name, err) + } + + e := Element{ + ElementMeta: ElementMeta{ + Name: name, + Description: description, + }, + File: fileName, + } + + d.Elements[name] = e + return e, d.save() +} + +func (d *Dataset) save() error { + datasetJSON, err := json.Marshal(d) + if err != nil { + return fmt.Errorf("failed to marshal dataset: %w", err) + } + + if err := os.WriteFile(d.BaseDir+extension, datasetJSON, 0644); err != nil { + return fmt.Errorf("failed to write dataset file: %w", err) + } + + return nil +} diff --git a/pkg/dataset/dataset_test.go b/pkg/dataset/dataset_test.go new file mode 100644 index 0000000..c5eef76 --- /dev/null +++ b/pkg/dataset/dataset_test.go @@ -0,0 +1,110 @@ +package dataset + +import ( + "os" + "path/filepath" + "testing" + + "github.com/stretchr/testify/require" +) + +const testWorkspace = "testworkspace" + +func TestDatasetsRead(t *testing.T) { + wd, err := os.Getwd() + require.NoError(t, err) + + workspaceDir := filepath.Join(wd, testWorkspace) + m, err := NewManager(workspaceDir) + require.NoError(t, err) + + datasetMetas, err := m.ListDatasets() + require.NoError(t, err) + require.Len(t, datasetMetas, 2) + + datasetOne, err := m.GetDataset("one") + require.NoError(t, err) + require.Equal(t, "one", datasetOne.GetName()) + require.Equal(t, "The first test dataset", datasetOne.GetDescription()) + require.Equal(t, 2, datasetOne.GetLength()) + + oneMetas := datasetOne.ListElements() + require.Len(t, oneMetas, 2) + + oneOneBytes, _, err := datasetOne.GetElement("file1") + require.NoError(t, err) + require.Equal(t, "This is dataset 1, file 1.\n", string(oneOneBytes)) + + oneTwoBytes, _, err := datasetOne.GetElement("file2") + require.NoError(t, err) + require.Equal(t, "This is dataset 1, file 2.\n", string(oneTwoBytes)) + + datasetTwo, err := m.GetDataset("two") + require.NoError(t, err) + require.Equal(t, "two", datasetTwo.GetName()) + require.Equal(t, "The second test dataset", datasetTwo.GetDescription()) + require.Equal(t, 2, datasetTwo.GetLength()) + + twoMetas := datasetTwo.ListElements() + require.Len(t, twoMetas, 2) + + twoOneBytes, _, err := datasetTwo.GetElement("file1") + require.NoError(t, err) + require.Equal(t, "This is dataset 2, file 1.\n", string(twoOneBytes)) + + twoTwoBytes, _, err := datasetTwo.GetElement("file2") + require.NoError(t, err) + require.Equal(t, "This is dataset 2, file 2.\n", string(twoTwoBytes)) +} + +func TestDatasetWrite(t *testing.T) { + wd, err := os.Getwd() + require.NoError(t, err) + + workspaceDir := filepath.Join(wd, testWorkspace) + m, err := NewManager(workspaceDir) + require.NoError(t, err) + + t.Cleanup(func() { + threeFiles, _ := filepath.Glob(filepath.Join(workspaceDir, "datasets", "three", "*")) + + for _, file := range threeFiles { + _ = os.Remove(file) + } + + _ = os.Remove(filepath.Join(workspaceDir, "datasets", "three")) + _ = os.Remove(filepath.Join(workspaceDir, "datasets", "three.dataset.json")) + }) + + datasetThree, err := m.NewDataset("three", "The third test dataset") + require.NoError(t, err) + require.Equal(t, "three", datasetThree.GetName()) + require.Equal(t, "The third test dataset", datasetThree.GetDescription()) + require.Equal(t, 0, datasetThree.GetLength()) + + // Let's add a couple elements. + _, err = datasetThree.AddElement("file1", "The first file", []byte("This is dataset 3, file 1.\n")) + require.NoError(t, err) + require.Equal(t, 1, datasetThree.GetLength()) + + _, err = datasetThree.AddElement("file2", "The second file", []byte("This is dataset 3, file 2.\n")) + require.NoError(t, err) + require.Equal(t, 2, datasetThree.GetLength()) + + // Let's read it back. + datasetThree, err = m.GetDataset(datasetThree.GetID()) + require.NoError(t, err) + require.Equal(t, "three", datasetThree.GetName()) + require.Equal(t, "The third test dataset", datasetThree.GetDescription()) + + threeMetas := datasetThree.ListElements() + require.Len(t, threeMetas, 2) + + threeOneBytes, _, err := datasetThree.GetElement("file1") + require.NoError(t, err) + require.Equal(t, "This is dataset 3, file 1.\n", string(threeOneBytes)) + + threeTwoBytes, _, err := datasetThree.GetElement("file2") + require.NoError(t, err) + require.Equal(t, "This is dataset 3, file 2.\n", string(threeTwoBytes)) +} diff --git a/pkg/dataset/manager.go b/pkg/dataset/manager.go new file mode 100644 index 0000000..123fc70 --- /dev/null +++ b/pkg/dataset/manager.go @@ -0,0 +1,120 @@ +package dataset + +import ( + "crypto/rand" + "encoding/json" + "fmt" + "os" + "path/filepath" + + "github.com/gptscript-ai/datasets/pkg/util" +) + +const ( + datasets = "datasets" + extension = ".dataset.json" +) + +type Manager struct { + datasetDir string +} + +func NewManager(workspaceDir string) (Manager, error) { + datasetDir := filepath.Join(workspaceDir, datasets) + if _, err := os.Stat(datasetDir); os.IsNotExist(err) { + if err := os.MkdirAll(datasetDir, 0755); err != nil { + return Manager{}, fmt.Errorf("failed to create dataset directory: %w", err) + } + } + + return Manager{datasetDir: datasetDir}, nil +} + +func (m *Manager) ListDatasets() ([]DatasetMeta, error) { + files, err := filepath.Glob(filepath.Join(m.datasetDir, "*"+extension)) + if err != nil { + return nil, fmt.Errorf("failed to list dataset files: %w", err) + } + + var datasets []DatasetMeta + for _, file := range files { + var d Dataset + data, err := os.ReadFile(file) + if err != nil { + return nil, fmt.Errorf("failed to read dataset file %s: %w", file, err) + } + + if err = json.Unmarshal(data, &d); err != nil { + return nil, fmt.Errorf("failed to unmarshal dataset file %s: %w", file, err) + } + + datasets = append(datasets, d.DatasetMeta) + } + + return datasets, nil +} + +func (m *Manager) NewDataset(name, description string) (Dataset, error) { + randBytes := make([]byte, 16) + if _, err := rand.Read(randBytes); err != nil { + return Dataset{}, fmt.Errorf("failed to generate random bytes: %w", err) + } + + id := fmt.Sprintf("%x", randBytes) + dirName, err := util.EnsureUniqueFilename(m.datasetDir, util.ToFileName(name)) + if err != nil { + return Dataset{}, fmt.Errorf("failed to ensure unique filename: %w", err) + } + + baseDir := filepath.Join(m.datasetDir, dirName) + if err := os.Mkdir(baseDir, 0755); err != nil { + return Dataset{}, fmt.Errorf("failed to create dataset directory: %w", err) + } + + d := Dataset{ + DatasetMeta: DatasetMeta{ + ID: id, + Name: name, + Description: description, + }, + BaseDir: baseDir, + Elements: make(map[string]Element), + } + + // Now convert to JSON and save it to the workspace + datasetJSON, err := json.Marshal(d) + if err != nil { + return Dataset{}, fmt.Errorf("failed to marshal dataset: %w", err) + } + + if err := os.WriteFile(baseDir+extension, datasetJSON, 0644); err != nil { + return Dataset{}, fmt.Errorf("failed to write dataset file: %w", err) + } + + return d, nil +} + +func (m *Manager) GetDataset(id string) (Dataset, error) { + files, err := filepath.Glob(filepath.Join(m.datasetDir, "*"+extension)) + if err != nil { + return Dataset{}, fmt.Errorf("failed to list dataset files: %w", err) + } + + for _, file := range files { + var d Dataset + data, err := os.ReadFile(file) + if err != nil { + return Dataset{}, fmt.Errorf("failed to read dataset file %s: %w", file, err) + } + + if err = json.Unmarshal(data, &d); err != nil { + return Dataset{}, fmt.Errorf("failed to unmarshal dataset file %s: %w", file, err) + } + + if d.GetID() == id { + return d, nil + } + } + + return Dataset{}, fmt.Errorf("dataset with ID %s not found", id) +} diff --git a/pkg/dataset/testworkspace/datasets/one.dataset.json b/pkg/dataset/testworkspace/datasets/one.dataset.json new file mode 100644 index 0000000..39aa791 --- /dev/null +++ b/pkg/dataset/testworkspace/datasets/one.dataset.json @@ -0,0 +1,20 @@ +{ + "id": "one", + "name": "one", + "description": "The first test dataset", + "baseDir": "/Users/grant/devel/datasets/pkg/dataset/testworkspace/datasets/one", + "elements": { + "file1": { + "name": "file1", + "description": "the first file", + "type": "string", + "file": "file1" + }, + "file2": { + "name": "file2", + "description": "the second file", + "type": "string", + "file": "file2" + } + } +} diff --git a/pkg/dataset/testworkspace/datasets/one/file1 b/pkg/dataset/testworkspace/datasets/one/file1 new file mode 100644 index 0000000..381855f --- /dev/null +++ b/pkg/dataset/testworkspace/datasets/one/file1 @@ -0,0 +1 @@ +This is dataset 1, file 1. diff --git a/pkg/dataset/testworkspace/datasets/one/file2 b/pkg/dataset/testworkspace/datasets/one/file2 new file mode 100644 index 0000000..5b05765 --- /dev/null +++ b/pkg/dataset/testworkspace/datasets/one/file2 @@ -0,0 +1 @@ +This is dataset 1, file 2. diff --git a/pkg/dataset/testworkspace/datasets/two.dataset.json b/pkg/dataset/testworkspace/datasets/two.dataset.json new file mode 100644 index 0000000..3a1ad9c --- /dev/null +++ b/pkg/dataset/testworkspace/datasets/two.dataset.json @@ -0,0 +1,20 @@ +{ + "id": "two", + "name": "two", + "description": "The second test dataset", + "baseDir": "/Users/grant/devel/datasets/pkg/dataset/testworkspace/datasets/two", + "elements": { + "file1": { + "name": "file1", + "description": "the first file", + "type": "string", + "file": "file1" + }, + "file2": { + "name": "file2", + "description": "the second file", + "type": "string", + "file": "file2" + } + } +} diff --git a/pkg/dataset/testworkspace/datasets/two/file1 b/pkg/dataset/testworkspace/datasets/two/file1 new file mode 100644 index 0000000..604865f --- /dev/null +++ b/pkg/dataset/testworkspace/datasets/two/file1 @@ -0,0 +1 @@ +This is dataset 2, file 1. diff --git a/pkg/dataset/testworkspace/datasets/two/file2 b/pkg/dataset/testworkspace/datasets/two/file2 new file mode 100644 index 0000000..40fc1c4 --- /dev/null +++ b/pkg/dataset/testworkspace/datasets/two/file2 @@ -0,0 +1 @@ +This is dataset 2, file 2. diff --git a/pkg/tools/addElement.go b/pkg/tools/addElement.go new file mode 100644 index 0000000..677309d --- /dev/null +++ b/pkg/tools/addElement.go @@ -0,0 +1,37 @@ +package tools + +import ( + "encoding/json" + "fmt" + "os" + + "github.com/gptscript-ai/datasets/pkg/dataset" +) + +func AddElement(workspace, datasetID, name, description string, content []byte) { + m, err := dataset.NewManager(workspace) + if err != nil { + fmt.Printf("failed to create dataset manager: %v\n", err) + os.Exit(1) + } + + d, err := m.GetDataset(datasetID) + if err != nil { + fmt.Printf("failed to get dataset: %v\n", err) + os.Exit(1) + } + + e, err := d.AddElement(name, description, content) + if err != nil { + fmt.Printf("failed to create element: %v\n", err) + os.Exit(1) + } + + elementJSON, err := json.Marshal(e.ElementMeta) + if err != nil { + fmt.Printf("failed to marshal element: %v\n", err) + os.Exit(1) + } + + fmt.Print(string(elementJSON)) +} diff --git a/pkg/tools/createDataset.go b/pkg/tools/createDataset.go new file mode 100644 index 0000000..0e88618 --- /dev/null +++ b/pkg/tools/createDataset.go @@ -0,0 +1,33 @@ +package tools + +import ( + "encoding/json" + "fmt" + "os" + + "github.com/gptscript-ai/datasets/pkg/dataset" +) + +func CreateDataset(workspace, name, description string) { + m, err := dataset.NewManager(workspace) + if err != nil { + fmt.Printf("failed to create dataset manager: %v\n", err) + os.Exit(1) + } + + d, err := m.NewDataset(name, description) + if err != nil { + fmt.Printf("failed to create dataset: %v\n", err) + os.Exit(1) + } + + d.BaseDir = "" // This field is an implementation detail that we don't need to care about + + datasetJSON, err := json.Marshal(d) + if err != nil { + fmt.Printf("failed to marshal dataset: %v\n", err) + os.Exit(1) + } + + fmt.Print(string(datasetJSON)) +} diff --git a/pkg/tools/getElement.go b/pkg/tools/getElement.go new file mode 100644 index 0000000..0beb350 --- /dev/null +++ b/pkg/tools/getElement.go @@ -0,0 +1,49 @@ +package tools + +import ( + "encoding/json" + "fmt" + "os" + + "github.com/gptscript-ai/datasets/pkg/dataset" +) + +type elem struct { + Contents string `json:"contents,omitempty"` + Name string `json:"name"` + Description string `json:"description,omitempty"` +} + +func GetElement(workspace, datasetID, elementName string) { + m, err := dataset.NewManager(workspace) + if err != nil { + fmt.Printf("failed to create dataset manager: %v\n", err) + os.Exit(1) + } + + d, err := m.GetDataset(datasetID) + if err != nil { + fmt.Printf("failed to get dataset: %v\n", err) + os.Exit(1) + } + + elementContents, e, err := d.GetElement(elementName) + if err != nil { + fmt.Printf("failed to get element: %v\n", err) + os.Exit(1) + } + + element := elem{ + Contents: string(elementContents), + Name: e.Name, + Description: e.Description, + } + + elementJSON, err := json.Marshal(element) + if err != nil { + fmt.Printf("failed to marshal element: %v\n", err) + os.Exit(1) + } + + fmt.Print(string(elementJSON)) +} diff --git a/pkg/tools/listDatasets.go b/pkg/tools/listDatasets.go new file mode 100644 index 0000000..a6293ff --- /dev/null +++ b/pkg/tools/listDatasets.go @@ -0,0 +1,31 @@ +package tools + +import ( + "encoding/json" + "fmt" + "os" + + "github.com/gptscript-ai/datasets/pkg/dataset" +) + +func ListDatasets(workspaceDir string) { + m, err := dataset.NewManager(workspaceDir) + if err != nil { + fmt.Printf("failed to create dataset manager: %v\n", err) + os.Exit(1) + } + + datasets, err := m.ListDatasets() + if err != nil { + fmt.Printf("failed to list datasets: %v\n", err) + os.Exit(1) + } + + datasetsJSON, err := json.Marshal(datasets) + if err != nil { + fmt.Printf("failed to marshal datasets: %v\n", err) + os.Exit(1) + } + + fmt.Print(string(datasetsJSON)) +} diff --git a/pkg/tools/listElements.go b/pkg/tools/listElements.go new file mode 100644 index 0000000..294d93c --- /dev/null +++ b/pkg/tools/listElements.go @@ -0,0 +1,32 @@ +package tools + +import ( + "encoding/json" + "fmt" + "os" + + "github.com/gptscript-ai/datasets/pkg/dataset" +) + +func ListElements(workspace, datasetID string) { + m, err := dataset.NewManager(workspace) + if err != nil { + fmt.Printf("failed to create dataset manager: %v\n", err) + os.Exit(1) + } + + d, err := m.GetDataset(datasetID) + if err != nil { + fmt.Printf("failed to get dataset: %v\n", err) + os.Exit(1) + } + + elements := d.ListElements() + elementsJSON, err := json.Marshal(elements) + if err != nil { + fmt.Printf("failed to marshal elements: %v\n", err) + os.Exit(1) + } + + fmt.Print(string(elementsJSON)) +} diff --git a/pkg/util/util.go b/pkg/util/util.go new file mode 100644 index 0000000..b56d99f --- /dev/null +++ b/pkg/util/util.go @@ -0,0 +1,34 @@ +package util + +import ( + "fmt" + "os" + "path/filepath" + "strings" + "unicode" +) + +// ToFileName converts a name to be alphanumeric plus underscores. +func ToFileName(name string) string { + return strings.Map(func(c rune) rune { + if !unicode.IsLetter(c) && !unicode.IsDigit(c) { + return '_' + } + return c + }, name) +} + +func EnsureUniqueFilename(base, name string) (string, error) { + var counter int + uniqueName := name + for { + if _, err := os.Stat(filepath.Join(base, uniqueName)); err == nil { + counter++ + uniqueName = fmt.Sprintf("%s_%d", name, counter) + } else if !os.IsNotExist(err) { + return "", err + } else { + return uniqueName, nil + } + } +} diff --git a/pkg/util/util_test.go b/pkg/util/util_test.go new file mode 100644 index 0000000..8d407c4 --- /dev/null +++ b/pkg/util/util_test.go @@ -0,0 +1,82 @@ +package util + +import ( + "os" + "path/filepath" + "testing" + + "github.com/stretchr/testify/require" +) + +func TestToFileName(t *testing.T) { + tests := []struct { + name string + output string + }{ + { + name: "test", + output: "test", + }, + { + name: "test@", + output: "test_", + }, + { + name: "test@123", + output: "test_123", + }, + { + name: "TEST_TEST-TEST", + output: "TEST_TEST_TEST", + }, + { + name: "123", + output: "123", + }, + { + name: "_", + output: "_", + }, + { + name: "!!!!!!!!!!!!!!!!!!!", + output: "___________________", + }, + } + + for _, test := range tests { + if ToFileName(test.name) != test.output { + t.Errorf("ToFileName(%s) != %s", test.name, test.output) + } + } +} + +func TestEnsureUniqueFilename(t *testing.T) { + wd, err := os.Getwd() + require.NoError(t, err) + + base, err := os.MkdirTemp(wd, "test") + require.NoError(t, err) + + t.Cleanup(func() { + _ = os.RemoveAll(base) + }) + + name, err := EnsureUniqueFilename(base, "test") + require.NoError(t, err) + require.Equal(t, "test", name) + _ = os.WriteFile(filepath.Join(base, "test"), []byte{}, 0644) + + name, err = EnsureUniqueFilename(base, "test") + require.NoError(t, err) + require.Equal(t, "test_1", name) + _ = os.WriteFile(filepath.Join(base, "test_1"), []byte{}, 0644) + + name, err = EnsureUniqueFilename(base, "test") + require.NoError(t, err) + require.Equal(t, "test_2", name) + _ = os.WriteFile(filepath.Join(base, "test_2"), []byte{}, 0644) + + name, err = EnsureUniqueFilename(base, "test") + require.NoError(t, err) + require.Equal(t, "test_3", name) +} diff --git a/tool.gpt b/tool.gpt new file mode 100644 index 0000000..fb4727c --- /dev/null +++ b/tool.gpt @@ -0,0 +1,37 @@ +Name: List Datasets +Description: Lists all available datasets + +#!${GPTSCRIPT_TOOL_DIR}/bin/gptscript-go-tool listDatasets + +--- +Name: List Elements +Description: Lists metadata for all elements in a dataset +Param: datasetID: the ID of the dataset + +#!${GPTSCRIPT_TOOL_DIR}/bin/gptscript-go-tool listElements + +--- +Name: Get Element +Description: Gets a particular element's metadata and contents +Param: datasetID: the ID of the dataset +Param: element: the name of the element + +#!${GPTSCRIPT_TOOL_DIR}/bin/gptscript-go-tool getElement + +--- +Name: Create Dataset +Description: Creates a new, empty dataset +Param: datasetName: the name of the new dataset +Param: datasetDescription: a description of the new dataset + +#!${GPTSCRIPT_TOOL_DIR}/bin/gptscript-go-tool createDataset + +--- +Name: Add Element +Description: Adds a new element to an existing dataset +Param: elementID: the ID of the dataset +Param: elementName: the name of the new element +Param: elementDescription: an optional description of the new element +Param: elementContent: the data to store in the element + +#!${GPTSCRIPT_TOOL_DIR}/bin/gptscript-go-tool addElement