From a933efeb8acaafe20ccdeb375b6c593d250d2ac5 Mon Sep 17 00:00:00 2001 From: Ettore Di Giacinto Date: Wed, 4 Nov 2026 10:20:31 +0000 Subject: [PATCH] test(studio): cover commands_host_exec in the add-on list and refresh swagger Pin the field on the chat and the draft endpoint for off, ask, on, no setting and an unknown value. Regenerate the swagger files for the new field. Assisted-by: Claude:claude-sonnet-5-5 --- .../endpoints/studio/addonhostexec_test.go | 62 +++++++++++++++++++ swagger/docs.go | 16 ++++- swagger/swagger.json | 16 ++++- swagger/swagger.yaml | 19 +++++- 4 files changed, 109 insertions(+), 4 deletions(-) create mode 100644 core/http/endpoints/studio/addonhostexec_test.go diff --git a/core/http/endpoints/studio/addonhostexec_test.go b/core/http/endpoints/studio/addonhostexec_test.go new file mode 100644 index 0000000000..53bd634b5f --- /dev/null +++ b/core/http/endpoints/studio/addonhostexec_test.go @@ -0,0 +1,62 @@ +package studio + +import ( + "context" + "encoding/json" + "net/http" + "net/http/httptest" + + "github.com/labstack/echo/v4" + . "github.com/onsi/ginkgo/v2" + . "github.com/onsi/gomega" + + "github.com/mudler/LocalAI/core/schema" + studiosvc "github.com/mudler/LocalAI/core/services/studio" + "github.com/mudler/LocalAI/core/services/studio/provider" + "github.com/mudler/LocalAI/core/services/studio/store" + "github.com/mudler/LocalAI/core/services/studio/studiotest" + "github.com/mudler/LocalAI/core/services/studio/workspace" +) + +// The add-on list carries commands_host_exec, a read-only copy of the +// chats_host_exec mode, on the chat endpoint and on the draft endpoint. +var _ = Describe("commands_host_exec in the add-on list", func() { + rigWith := func(mode func() string) (*studiosvc.Service, *echo.Echo) { + h := &studiotest.ToolHarness{Script: func(workspace.Actor, string) []studiotest.ToolStep { + return []studiotest.ToolStep{{Say: "ok"}} + }} + st, err := store.NewJSON("") + Expect(err).NotTo(HaveOccurred()) + svc, err := studiosvc.New(studiosvc.Deps{Store: st, Provider: provider.New(), StateDir: GinkgoT().TempDir(), + Harness: h, HostExecMode: mode}) + Expect(err).NotTo(HaveOccurred()) + DeferCleanup(func() { _ = svc.Close(context.Background()) }) + hs := New(svc, nil) + e := echo.New() + e.GET("/api/chats/:id/addons", hs.ListChatAddons) + e.GET("/api/chats/draft/addons", hs.DraftAddons) + return svc, e + } + get := func(e *echo.Echo, path string) schema.ChatsAddonList { + rec := httptest.NewRecorder() + e.ServeHTTP(rec, httptest.NewRequest(http.MethodGet, path, nil)) + Expect(rec.Code).To(Equal(http.StatusOK), rec.Body.String()) + var out schema.ChatsAddonList + Expect(json.Unmarshal(rec.Body.Bytes(), &out)).To(Succeed()) + return out + } + DescribeTable("reports the mode on both endpoints", + func(mode func() string, want string) { + svc, e := rigWith(mode) + rec, err := svc.Create(context.Background(), "", studiosvc.CreateInput{Model: "m"}) + Expect(err).NotTo(HaveOccurred()) + Expect(get(e, "/api/chats/"+rec.ID+"/addons").CommandsHostExec).To(Equal(want)) + Expect(get(e, "/api/chats/draft/addons?model=m").CommandsHostExec).To(Equal(want)) + }, + Entry("off", func() string { return "off" }, "off"), + Entry("ask", func() string { return "ask" }, "ask"), + Entry("on", func() string { return "on" }, "on"), + Entry("no setting reads as off", nil, "off"), + Entry("an unknown value reads as off", func() string { return "maybe" }, "off"), + ) +}) diff --git a/swagger/docs.go b/swagger/docs.go index 75df19ee01..f4eae1d247 100644 --- a/swagger/docs.go +++ b/swagger/docs.go @@ -9040,7 +9040,7 @@ const docTemplate = `{ }, "/api/chats/{id}/addons": { "get": { - "description": "The add-ons whose tools the signed-in user may use now, each with the chat's switch, and whether the chat can run commands at its next turn (commands: available, off, unavailable, none, or switched_off when the chat switched the built-in group shell off). When commands is available, commands_provider says who serves bash (builtin: directly on this machine with no isolation and with its network, or the add-on's ID) and commands_isolation says isolated or host; commands_consent is ask when each command waits for a yes in the chat, and commands_fallback names an add-on that would serve but does not (commands_fallback_reason: off, or why it cannot run). Without the add-on host the list is empty.", + "description": "The add-ons whose tools the signed-in user may use now, each with the chat's switch, and whether the chat can run commands at its next turn (commands: available, off, unavailable, none, or switched_off when the chat switched the built-in group shell off). When commands is available, commands_provider says who serves bash (builtin: directly on this machine with no isolation and with its network, or the add-on's ID) and commands_isolation says isolated or host; commands_consent is ask when each command waits for a yes in the chat, and commands_fallback names an add-on that would serve but does not (commands_fallback_reason: off, or why it cannot run). commands_host_exec is the mode of chats_host_exec now (off, ask or on), so a client can say that the built-in bash is off when commands is none. Without the add-on host the list is empty.", "tags": [ "chats" ], @@ -18234,6 +18234,12 @@ const docTemplate = `{ "installed": { "type": "boolean" }, + "locked": { + "type": "boolean" + }, + "lockedReason": { + "type": "string" + }, "nonce": { "type": "string" }, @@ -18295,6 +18301,10 @@ const docTemplate = `{ "locked": { "type": "boolean" }, + "lockedReason": { + "description": "LockedReason is LockedByDeployment or LockedNoAuth when Locked.", + "type": "string" + }, "revision": { "type": "integer" } @@ -20120,6 +20130,10 @@ const docTemplate = `{ "commands_fallback_reason": { "type": "string" }, + "commands_host_exec": { + "description": "CommandsHostExec is the mode of the setting chats_host_exec as it applies\nnow (off, ask or on), so a client can say that the built-in bash is off\nwhen no add-on runs commands. Every signed-in user may read it.", + "type": "string" + }, "commands_isolation": { "description": "CommandsIsolation is isolated (a sandbox) or host (directly on this\nmachine, with its files and network), when Commands is available.", "type": "string" diff --git a/swagger/swagger.json b/swagger/swagger.json index 7292eddb1c..ee533e2341 100644 --- a/swagger/swagger.json +++ b/swagger/swagger.json @@ -9037,7 +9037,7 @@ }, "/api/chats/{id}/addons": { "get": { - "description": "The add-ons whose tools the signed-in user may use now, each with the chat's switch, and whether the chat can run commands at its next turn (commands: available, off, unavailable, none, or switched_off when the chat switched the built-in group shell off). When commands is available, commands_provider says who serves bash (builtin: directly on this machine with no isolation and with its network, or the add-on's ID) and commands_isolation says isolated or host; commands_consent is ask when each command waits for a yes in the chat, and commands_fallback names an add-on that would serve but does not (commands_fallback_reason: off, or why it cannot run). Without the add-on host the list is empty.", + "description": "The add-ons whose tools the signed-in user may use now, each with the chat's switch, and whether the chat can run commands at its next turn (commands: available, off, unavailable, none, or switched_off when the chat switched the built-in group shell off). When commands is available, commands_provider says who serves bash (builtin: directly on this machine with no isolation and with its network, or the add-on's ID) and commands_isolation says isolated or host; commands_consent is ask when each command waits for a yes in the chat, and commands_fallback names an add-on that would serve but does not (commands_fallback_reason: off, or why it cannot run). commands_host_exec is the mode of chats_host_exec now (off, ask or on), so a client can say that the built-in bash is off when commands is none. Without the add-on host the list is empty.", "tags": [ "chats" ], @@ -18231,6 +18231,12 @@ "installed": { "type": "boolean" }, + "locked": { + "type": "boolean" + }, + "lockedReason": { + "type": "string" + }, "nonce": { "type": "string" }, @@ -18292,6 +18298,10 @@ "locked": { "type": "boolean" }, + "lockedReason": { + "description": "LockedReason is LockedByDeployment or LockedNoAuth when Locked.", + "type": "string" + }, "revision": { "type": "integer" } @@ -20117,6 +20127,10 @@ "commands_fallback_reason": { "type": "string" }, + "commands_host_exec": { + "description": "CommandsHostExec is the mode of the setting chats_host_exec as it applies\nnow (off, ask or on), so a client can say that the built-in bash is off\nwhen no add-on runs commands. Every signed-in user may read it.", + "type": "string" + }, "commands_isolation": { "description": "CommandsIsolation is isolated (a sandbox) or host (directly on this\nmachine, with its files and network), when Commands is available.", "type": "string" diff --git a/swagger/swagger.yaml b/swagger/swagger.yaml index 4f122b6179..78a9748ec2 100644 --- a/swagger/swagger.yaml +++ b/swagger/swagger.yaml @@ -2106,6 +2106,10 @@ definitions: type: string installed: type: boolean + locked: + type: boolean + lockedReason: + type: string nonce: type: string nonceExpiresAt: @@ -2146,6 +2150,9 @@ definitions: $ref: '#/definitions/gateway.HostGrantsFloor' locked: type: boolean + lockedReason: + description: LockedReason is LockedByDeployment or LockedNoAuth when Locked. + type: string revision: type: integer type: object @@ -3510,6 +3517,12 @@ definitions: type: string commands_fallback_reason: type: string + commands_host_exec: + description: |- + CommandsHostExec is the mode of the setting chats_host_exec as it applies + now (off, ask or on), so a client can say that the built-in bash is off + when no add-on runs commands. Every signed-in user may read it. + type: string commands_isolation: description: |- CommandsIsolation is isolated (a sandbox) or host (directly on this @@ -14463,8 +14476,10 @@ paths: and with its network, or the add-on''s ID) and commands_isolation says isolated or host; commands_consent is ask when each command waits for a yes in the chat, and commands_fallback names an add-on that would serve but does not - (commands_fallback_reason: off, or why it cannot run). Without the add-on - host the list is empty.' + (commands_fallback_reason: off, or why it cannot run). commands_host_exec + is the mode of chats_host_exec now (off, ask or on), so a client can say that + the built-in bash is off when commands is none. Without the add-on host the + list is empty.' parameters: - description: conversation id in: path