Skip to content

Commit

Permalink
chore: workspace files updates
Browse files Browse the repository at this point in the history
  • Loading branch information
ibuildthecloud committed Oct 21, 2024
1 parent 4234b4e commit 37b20d4
Show file tree
Hide file tree
Showing 15 changed files with 112 additions and 60 deletions.
2 changes: 1 addition & 1 deletion .dockerignore
Original file line number Diff line number Diff line change
@@ -1 +1 @@
node_modules
node_modules/
17 changes: 14 additions & 3 deletions Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -21,11 +21,22 @@ FROM ubuntu:22.04

RUN apt-get update && apt install -y git tini openssh-server

RUN sed -E 's/^#(PermitRootLogin)no/\1yes/' /etc/ssh/sshd_config -i
RUN ssh-keygen -A
RUN mkdir /run/sshd && /usr/sbin/sshd


# Copy the compiled application from the builder stage
COPY --link --from=builder /app/otto8 /bin/

COPY --link <<EOF /bin/run.sh
#!/bin/bash
mkdir -p /run/sshd
/usr/sbin/sshd -D &
exec tini -- otto8 server
EOF

EXPOSE 22
ENV HOME=/data
WORKDIR /data
VOLUME /data
# Command to run the application
CMD ["tini", "--", "otto8", "server"]
CMD ["run.sh"]
4 changes: 2 additions & 2 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ replace (
require (
github.com/MicahParks/keyfunc/v3 v3.3.5
github.com/acorn-io/baaah v0.0.0-20241017212231-096ce8555b5f
github.com/acorn-io/mink v0.0.0-20241019043522-c80c1a36ee59
github.com/acorn-io/mink v0.0.0-20241021151948-78ca55fc5887
github.com/adrg/xdg v0.5.1
github.com/dustin/go-humanize v1.0.1
github.com/fatih/color v1.17.0
Expand All @@ -20,7 +20,7 @@ require (
github.com/google/uuid v1.6.0
github.com/gptscript-ai/cmd v0.0.0-20240907001148-ffd49061124a
github.com/gptscript-ai/go-gptscript v0.9.6-0.20241020145205-d72fa0553119
github.com/gptscript-ai/gptscript v0.9.6-0.20241020145319-366104477158
github.com/gptscript-ai/gptscript v0.9.6-0.20241021152632-87863a7ef640
github.com/liggitt/tabwriter v0.0.0-20181228230101-89fcab3d43de
github.com/oauth2-proxy/oauth2-proxy/v7 v7.0.0-00010101000000-000000000000
github.com/otto8-ai/otto8/apiclient v0.0.0-00010101000000-000000000000
Expand Down
8 changes: 4 additions & 4 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -73,8 +73,8 @@ github.com/acorn-io/baaah v0.0.0-20241017212231-096ce8555b5f h1:T6XYeXfrspHSwOCJ
github.com/acorn-io/baaah v0.0.0-20241017212231-096ce8555b5f/go.mod h1:FTdfwOSepj32acbdMwoqe5pwFTYhNG0UQhCXxgHuEzM=
github.com/acorn-io/broadcaster v0.0.0-20240105011354-bfadd4a7b45d h1:hfpNQkJ4I2b8+DbMr8m97gG67ku0uPsMzUfskVu3cHU=
github.com/acorn-io/broadcaster v0.0.0-20240105011354-bfadd4a7b45d/go.mod h1:WF6FYrEqW0+ZtY5OKb21JhSL0aeL5VJoVrm+u0d4gOE=
github.com/acorn-io/mink v0.0.0-20241019043522-c80c1a36ee59 h1:bPs5d5p4eqhHsM651VKDnyuT96RpMm+lUHX2IzCgC2I=
github.com/acorn-io/mink v0.0.0-20241019043522-c80c1a36ee59/go.mod h1:8oQd22FJQbhQLTI+i4llI34+sGQC/ZS6reayoALk8eo=
github.com/acorn-io/mink v0.0.0-20241021151948-78ca55fc5887 h1:ZnYt+Pf27f4w6u35tbLcDmwYiM5Ksy/LUhjTGpDvKk4=
github.com/acorn-io/mink v0.0.0-20241021151948-78ca55fc5887/go.mod h1:8oQd22FJQbhQLTI+i4llI34+sGQC/ZS6reayoALk8eo=
github.com/adrg/xdg v0.5.1 h1:Im8iDbEFARltY09yOJlSGu4Asjk2vF85+3Dyru8uJ0U=
github.com/adrg/xdg v0.5.1/go.mod h1:nlTsY+NNiCBGCK2tpm09vRqfVzrc2fLmXGpBLF0zlTQ=
github.com/alecthomas/assert/v2 v2.7.0 h1:QtqSACNS3tF7oasA8CU6A6sXZSBDqnm7RfpLl9bZqbE=
Expand Down Expand Up @@ -361,8 +361,8 @@ github.com/gptscript-ai/cmd v0.0.0-20240907001148-ffd49061124a h1:LX7AOcbBoTnUk/
github.com/gptscript-ai/cmd v0.0.0-20240907001148-ffd49061124a/go.mod h1:DJAo1xTht1LDkNYFNydVjTHd576TC7MlpsVRl3oloVw=
github.com/gptscript-ai/go-gptscript v0.9.6-0.20241020145205-d72fa0553119 h1:7ettKtKssIPzfCsgUuyw8CYLRLMNmdavnCFPwn2scbU=
github.com/gptscript-ai/go-gptscript v0.9.6-0.20241020145205-d72fa0553119/go.mod h1:/FVuLwhz+sIfsWUgUHWKi32qT0i6+IXlUlzs70KKt/Q=
github.com/gptscript-ai/gptscript v0.9.6-0.20241020145319-366104477158 h1:RjTaHjx9HK3gu+YwT1abnr7TjK/9/PsVR+mIyBl6ODU=
github.com/gptscript-ai/gptscript v0.9.6-0.20241020145319-366104477158/go.mod h1:+CC86EbcmMMBgvv5NBqHzep63SobtB5vhNOq4i6aPrA=
github.com/gptscript-ai/gptscript v0.9.6-0.20241021152632-87863a7ef640 h1:FiSgEOk/p1vTgeWp4G1S7Fn8BWCY4o2F5gKmOkxmowg=
github.com/gptscript-ai/gptscript v0.9.6-0.20241021152632-87863a7ef640/go.mod h1:+CC86EbcmMMBgvv5NBqHzep63SobtB5vhNOq4i6aPrA=
github.com/gptscript-ai/tui v0.0.0-20240923192013-172e51ccf1d6 h1:vkgNZVWQgbE33VD3z9WKDwuu7B/eJVVMMPM62ixfCR8=
github.com/gptscript-ai/tui v0.0.0-20240923192013-172e51ccf1d6/go.mod h1:frrl/B+ZH3VSs3Tqk2qxEIIWTONExX3tuUa4JsVnqx4=
github.com/gregjones/httpcache v0.0.0-20180305231024-9cad4c3443a7 h1:pdN6V1QBWetyv/0+wjACpqVH+eVULgEjkurDLq3goeM=
Expand Down
5 changes: 5 additions & 0 deletions pkg/api/authz/authz.go
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,11 @@ var staticRules = map[string][]string{
AuthenticatedGroup: {
"POST /api/invoke/otto/threads/user",
"GET /api/threads/user/events",
"GET /api/threads/user/files",
"DELETE /api/threads/user/files/{file...}",
"GET /api/threads/user/knowledge",
"POST /api/threads/user/knowledge/{file}",
"DELETE /api/threads/user/knowledge/{file}",
"GET /api/me",
},
}
Expand Down
2 changes: 1 addition & 1 deletion pkg/api/handlers/agent.go
Original file line number Diff line number Diff line change
Expand Up @@ -165,7 +165,7 @@ func (a *AgentHandler) DeleteFile(req api.Context) error {
return fmt.Errorf("failed to get agent with id %s: %w", id, err)
}

return deleteFile(req.Context(), req, a.gptscript, agent.Status.WorkspaceName)
return deleteFile(req.Context(), req, a.gptscript, agent.Status.WorkspaceName, "files/")
}

func (a *AgentHandler) Knowledge(req api.Context) error {
Expand Down
36 changes: 19 additions & 17 deletions pkg/api/handlers/files.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,16 +27,18 @@ func listFiles(ctx context.Context, req api.Context, gClient *gptscript.GPTScrip
return err
}

return listFileFromWorkspace(ctx, req, gClient, ws)
return listFileFromWorkspace(ctx, req, gClient, ws, gptscript.ListFilesInWorkspaceOptions{
Prefix: "files/",
})
}

func listFileFromWorkspace(ctx context.Context, req api.Context, gClient *gptscript.GPTScript, ws v1.Workspace) error {
files, err := gClient.ListFilesInWorkspace(ctx, ws.Status.WorkspaceID)
func listFileFromWorkspace(ctx context.Context, req api.Context, gClient *gptscript.GPTScript, ws v1.Workspace, opts gptscript.ListFilesInWorkspaceOptions) error {
files, err := gClient.ListFilesInWorkspace(ctx, ws.Status.WorkspaceID, opts)
if err != nil {
return fmt.Errorf("failed to list files in workspace %q: %w", ws.Status.WorkspaceID, err)
}

return req.Write(types.FileList{Items: compileFileNames(files)})
return req.Write(types.FileList{Items: compileFileNames(files, opts)})
}

func getWorkspaceFromKnowledgeSet(req api.Context, knowledgeSetNames ...string) (ws v1.Workspace, ok bool, err error) {
Expand Down Expand Up @@ -97,7 +99,7 @@ func uploadKnowledge(req api.Context, gClient *gptscript.GPTScript, knowledgeSet
func uploadKnowledgeToWorkspace(req api.Context, gClient *gptscript.GPTScript, ws v1.Workspace) error {
filename := req.PathValue("file")

if err := uploadFileToWorkspace(req.Context(), req, gClient, ws); err != nil {
if err := uploadFileToWorkspace(req.Context(), req, gClient, ws, ""); err != nil {
return err
}

Expand All @@ -115,7 +117,7 @@ func uploadKnowledgeToWorkspace(req api.Context, gClient *gptscript.GPTScript, w
}

if err := req.Storage.Create(req.Context(), &file); err != nil && !apierrors.IsAlreadyExists(err) {
_ = deleteFile(req.Context(), req, gClient, ws.Status.WorkspaceID)
_ = deleteFile(req.Context(), req, gClient, ws.Status.WorkspaceID, "")
return err
}

Expand All @@ -137,18 +139,18 @@ func convertKnowledgeFile(file v1.KnowledgeFile, ws v1.Workspace) types.Knowledg
}
}

func compileFileNames(files []string) []types.File {
func compileFileNames(files []string, opts gptscript.ListFilesInWorkspaceOptions) []types.File {
resp := make([]types.File, 0, len(files))
for _, file := range files {
resp = append(resp, convertFile(file))
resp = append(resp, convertFile(file, opts.Prefix))
}

return resp
}

func convertFile(file string) types.File {
func convertFile(file, prefix string) types.File {
return types.File{
Name: file,
Name: strings.TrimPrefix(file, prefix),
}
}

Expand All @@ -158,10 +160,10 @@ func uploadFile(ctx context.Context, req api.Context, gClient *gptscript.GPTScri
return fmt.Errorf("failed to get workspace with id %s: %w", workspaceName, err)
}

return uploadFileToWorkspace(ctx, req, gClient, ws)
return uploadFileToWorkspace(ctx, req, gClient, ws, "files/")
}

func uploadFileToWorkspace(ctx context.Context, req api.Context, gClient *gptscript.GPTScript, ws v1.Workspace) error {
func uploadFileToWorkspace(ctx context.Context, req api.Context, gClient *gptscript.GPTScript, ws v1.Workspace, prefix string) error {
file := req.PathValue("file")
if file == "" {
return fmt.Errorf("file path parameter is required")
Expand All @@ -172,7 +174,7 @@ func uploadFileToWorkspace(ctx context.Context, req api.Context, gClient *gptscr
return fmt.Errorf("failed to read request body: %w", err)
}

if err = gClient.WriteFileInWorkspace(ctx, ws.Status.WorkspaceID, file, contents); err != nil {
if err = gClient.WriteFileInWorkspace(ctx, ws.Status.WorkspaceID, prefix+file, contents); err != nil {
return fmt.Errorf("failed to upload file %q to workspace %q: %w", file, ws.Status.WorkspaceID, err)
}

Expand Down Expand Up @@ -211,19 +213,19 @@ func deleteKnowledgeFromWorkspace(req api.Context, filename string, ws v1.Worksp
return nil
}

func deleteFile(ctx context.Context, req api.Context, gClient *gptscript.GPTScript, workspaceName string) error {
func deleteFile(ctx context.Context, req api.Context, gClient *gptscript.GPTScript, workspaceName, prefix string) error {
var ws v1.Workspace
if err := req.Get(&ws, workspaceName); err != nil {
return err
}

return deleteFileFromWorkspaceID(ctx, req, gClient, ws.Status.WorkspaceID)
return deleteFileFromWorkspaceID(ctx, req, gClient, ws.Status.WorkspaceID, prefix)
}

func deleteFileFromWorkspaceID(ctx context.Context, req api.Context, gClient *gptscript.GPTScript, workspaceID string) error {
func deleteFileFromWorkspaceID(ctx context.Context, req api.Context, gClient *gptscript.GPTScript, workspaceID, prefix string) error {
filename := req.PathValue("file")

if err := gClient.DeleteFileInWorkspace(ctx, workspaceID, filename); err != nil {
if err := gClient.DeleteFileInWorkspace(ctx, workspaceID, prefix+filename); err != nil {
return fmt.Errorf("failed to delete file %q from workspace %q: %w", filename, workspaceID, err)
}

Expand Down
1 change: 1 addition & 0 deletions pkg/api/handlers/invoke.go
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,7 @@ func (i *InvokeHandler) Invoke(req api.Context) error {

if async {
req.WriteHeader(http.StatusCreated)
req.ResponseWriter.Header().Set("Content-Type", "application/json")
return req.Write(map[string]string{
"threadID": resp.Thread.Name,
})
Expand Down
77 changes: 55 additions & 22 deletions pkg/api/handlers/threads.go
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,6 @@ func (a *ThreadHandler) Events(req api.Context) error {
maxRunString = req.URL.Query().Get("maxRuns")
maxRuns int
err error
thread v1.Thread
waitForThread = req.URL.Query().Get("waitForThread") == "true"
)

Expand All @@ -79,17 +78,13 @@ func (a *ThreadHandler) Events(req api.Context) error {
maxRuns = 20
}

if err := req.Get(&thread, id); err != nil {
return err
}

_, events, err := a.events.Watch(req.Context(), req.Namespace(), events.WatchOptions{
Follow: follow,
History: runID == "",
LastRunName: strings.TrimSuffix(runID, ":after"),
MaxRuns: maxRuns,
After: strings.HasSuffix(runID, ":after"),
ThreadName: thread.Name,
ThreadName: id,
WaitForThread: waitForThread,
})
if err != nil {
Expand Down Expand Up @@ -187,23 +182,32 @@ func (a *ThreadHandler) List(req api.Context) error {
}

func (a *ThreadHandler) Files(req api.Context) error {
var workspaces v1.WorkspaceList
var (
workspaces v1.WorkspaceList
threadID = req.PathValue("id")
)
if threadID == "user" {
threadID = system.ThreadPrefix + req.User.GetUID()
}

if err := req.Storage.List(req.Context(), &workspaces, &client.ListOptions{
Namespace: req.Namespace(),
FieldSelector: fields.SelectorFromSet(map[string]string{
"spec.threadName": req.PathValue("id"),
"spec.threadName": threadID,
}),
}); err != nil {
return err
}

for _, workspace := range workspaces.Items {
if !workspace.Spec.IsKnowledge {
return listFileFromWorkspace(req.Context(), req, a.gptscript, workspace)
return listFileFromWorkspace(req.Context(), req, a.gptscript, workspace, gptscript.ListFilesInWorkspaceOptions{
Prefix: "files/",
})
}
}

return fmt.Errorf("no workspace found for thread %s", req.PathValue("id"))
return req.Write(types.FileList{})
}

func (a *ThreadHandler) UploadFile(req api.Context) error {
Expand All @@ -219,7 +223,7 @@ func (a *ThreadHandler) UploadFile(req api.Context) error {

for _, workspace := range workspaces.Items {
if !workspace.Spec.IsKnowledge {
if err := uploadFileToWorkspace(req.Context(), req, a.gptscript, workspace); err != nil {
if err := uploadFileToWorkspace(req.Context(), req, a.gptscript, workspace, "files/"); err != nil {
return err
}

Expand All @@ -232,31 +236,46 @@ func (a *ThreadHandler) UploadFile(req api.Context) error {
}

func (a *ThreadHandler) DeleteFile(req api.Context) error {
var workspaces v1.WorkspaceList
var (
workspaces v1.WorkspaceList
threadID = req.PathValue("id")
)
if threadID == "user" {
threadID = system.ThreadPrefix + req.User.GetUID()
}

if err := req.Storage.List(req.Context(), &workspaces, &client.ListOptions{
Namespace: req.Namespace(),
FieldSelector: fields.SelectorFromSet(map[string]string{
"spec.threadName": req.PathValue("id"),
"spec.threadName": threadID,
}),
}); err != nil {
return err
}

for _, workspace := range workspaces.Items {
if !workspace.Spec.IsKnowledge {
return deleteFileFromWorkspaceID(req.Context(), req, a.gptscript, workspace.Status.WorkspaceID)
return deleteFileFromWorkspaceID(req.Context(), req, a.gptscript, workspace.Status.WorkspaceID, "files/")
}
}

return fmt.Errorf("no workspace found for thread %s", req.PathValue("id"))
req.WriteHeader(http.StatusNoContent)
return nil
}

func (a *ThreadHandler) Knowledge(req api.Context) error {
var workspaces v1.WorkspaceList
var (
workspaces v1.WorkspaceList
threadID = req.PathValue("id")
)
if threadID == "user" {
threadID = system.ThreadPrefix + req.User.GetUID()
}

if err := req.Storage.List(req.Context(), &workspaces, &client.ListOptions{
Namespace: req.Namespace(),
FieldSelector: fields.SelectorFromSet(map[string]string{
"spec.threadName": req.PathValue("id"),
"spec.threadName": threadID,
}),
}); err != nil {
return err
Expand All @@ -268,15 +287,23 @@ func (a *ThreadHandler) Knowledge(req api.Context) error {
}
}

return fmt.Errorf("no knowledge workspace found for thread %s", req.PathValue("id"))
return req.Write(types.KnowledgeFileList{})
}

func (a *ThreadHandler) UploadKnowledge(req api.Context) error {
var workspaces v1.WorkspaceList
var (
workspaces v1.WorkspaceList
threadID = req.PathValue("id")
)

if threadID == "user" {
threadID = system.ThreadPrefix + req.User.GetUID()
}

if err := req.Storage.List(req.Context(), &workspaces, &client.ListOptions{
Namespace: req.Namespace(),
FieldSelector: fields.SelectorFromSet(map[string]string{
"spec.threadName": req.PathValue("id"),
"spec.threadName": threadID,
}),
}); err != nil {
return err
Expand All @@ -292,11 +319,17 @@ func (a *ThreadHandler) UploadKnowledge(req api.Context) error {
}

func (a *ThreadHandler) DeleteKnowledge(req api.Context) error {
var workspaces v1.WorkspaceList
var (
workspaces v1.WorkspaceList
threadID = req.PathValue("id")
)
if threadID == "user" {
threadID = system.ThreadPrefix + req.User.GetUID()
}
if err := req.Storage.List(req.Context(), &workspaces, &client.ListOptions{
Namespace: req.Namespace(),
FieldSelector: fields.SelectorFromSet(map[string]string{
"spec.threadName": req.PathValue("id"),
"spec.threadName": threadID,
}),
}); err != nil {
return err
Expand Down
2 changes: 1 addition & 1 deletion pkg/api/handlers/workflows.go
Original file line number Diff line number Diff line change
Expand Up @@ -168,7 +168,7 @@ func (a *WorkflowHandler) DeleteFile(req api.Context) error {
return fmt.Errorf("failed to get workflow with id %s: %w", id, err)
}

return deleteFile(req.Context(), req, a.gptscript, workflow.Status.WorkspaceName)
return deleteFile(req.Context(), req, a.gptscript, workflow.Status.WorkspaceName, "files/")
}

func (a *WorkflowHandler) Script(req api.Context) error {
Expand Down
2 changes: 1 addition & 1 deletion pkg/api/router/router.go
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@ func Router(services *services.Services) (http.Handler, error) {
// Thread files
mux.HandleFunc("GET /api/threads/{id}/files", threads.Files)
mux.HandleFunc("POST /api/threads/{id}/files/{file}", threads.UploadFile)
mux.HandleFunc("DELETE /api/threads/{id}/files/{file}", threads.DeleteFile)
mux.HandleFunc("DELETE /api/threads/{id}/files/{file...}", threads.DeleteFile)

// Thread knowledge files
mux.HandleFunc("GET /api/threads/{id}/knowledge", threads.Knowledge)
Expand Down
Loading

0 comments on commit 37b20d4

Please sign in to comment.