-
Notifications
You must be signed in to change notification settings - Fork 1.5k
feat(codersdk/toolsdk): expose template version presets to agents #24695
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -30,6 +30,7 @@ const ( | |
| ToolNameListWorkspaces = "coder_list_workspaces" | ||
| ToolNameListTemplates = "coder_list_templates" | ||
| ToolNameListTemplateVersionParams = "coder_template_version_parameters" | ||
| ToolNameListTemplateVersionPresets = "coder_template_version_presets" | ||
| ToolNameGetAuthenticatedUser = "coder_get_authenticated_user" | ||
| ToolNameCreateWorkspaceBuild = "coder_create_workspace_build" | ||
| ToolNameCreateTemplateVersion = "coder_create_template_version" | ||
|
|
@@ -310,6 +311,7 @@ var All = []GenericTool{ | |
| DeleteTemplate.Generic(), | ||
| ListTemplates.Generic(), | ||
| ListTemplateVersionParameters.Generic(), | ||
| ListTemplateVersionPresets.Generic(), | ||
| ListWorkspaces.Generic(), | ||
| GetAuthenticatedUser.Generic(), | ||
| GetTemplateVersionLogs.Generic(), | ||
|
|
@@ -441,10 +443,11 @@ This returns more data than list_workspaces to reduce token usage.`, | |
| } | ||
|
|
||
| type CreateWorkspaceArgs struct { | ||
| Name string `json:"name"` | ||
| RichParameters map[string]string `json:"rich_parameters"` | ||
| TemplateVersionID string `json:"template_version_id"` | ||
| User string `json:"user"` | ||
| Name string `json:"name"` | ||
| RichParameters map[string]string `json:"rich_parameters"` | ||
| TemplateVersionID string `json:"template_version_id"` | ||
| TemplateVersionPresetID string `json:"template_version_preset_id"` | ||
| User string `json:"user"` | ||
| } | ||
|
|
||
| var CreateWorkspace = Tool[CreateWorkspaceArgs, codersdk.Workspace]{ | ||
|
|
@@ -478,6 +481,10 @@ be ready before trying to use or connect to the workspace. | |
| "type": "string", | ||
| "description": "ID of the template version to create the workspace from.", | ||
| }, | ||
| "template_version_preset_id": map[string]any{ | ||
| "type": "string", | ||
| "description": "Optional ID of the template version preset to create the workspace from.", | ||
| }, | ||
| "name": map[string]any{ | ||
| "type": "string", | ||
| "description": "Name of the workspace to create.", | ||
|
|
@@ -496,6 +503,14 @@ be ready before trying to use or connect to the workspace. | |
| if err != nil { | ||
| return codersdk.Workspace{}, xerrors.New("template_version_id must be a valid UUID") | ||
| } | ||
|
|
||
| var tvPresetID uuid.UUID | ||
| if args.TemplateVersionPresetID != "" { | ||
| tvPresetID, err = uuid.Parse(args.TemplateVersionPresetID) | ||
| if err != nil { | ||
| return codersdk.Workspace{}, xerrors.New("template_version_preset_id must be a valid UUID") | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. P3 [DEREM-4] Error discards the UUID parse failure. Every other UUID parse error in this file wraps with
The pre-existing Fix: (Leorio)
|
||
| } | ||
| } | ||
| if args.User == "" { | ||
| args.User = codersdk.Me | ||
| } | ||
|
|
@@ -506,11 +521,15 @@ be ready before trying to use or connect to the workspace. | |
| Value: v, | ||
| }) | ||
| } | ||
| workspace, err := deps.coderClient.CreateUserWorkspace(ctx, args.User, codersdk.CreateWorkspaceRequest{ | ||
| req := codersdk.CreateWorkspaceRequest{ | ||
| TemplateVersionID: tvID, | ||
| Name: args.Name, | ||
| RichParameterValues: buildParams, | ||
| }) | ||
| } | ||
| if tvPresetID != uuid.Nil { | ||
| req.TemplateVersionPresetID = tvPresetID | ||
| } | ||
| workspace, err := deps.coderClient.CreateUserWorkspace(ctx, args.User, req) | ||
| if err != nil { | ||
| return codersdk.Workspace{}, err | ||
| } | ||
|
|
@@ -626,6 +645,33 @@ var ListTemplateVersionParameters = Tool[ListTemplateVersionParametersArgs, []co | |
| }, | ||
| } | ||
|
|
||
| var ListTemplateVersionPresets = Tool[ListTemplateVersionParametersArgs, []codersdk.Preset]{ | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. P2 [DEREM-2]
This is pre-existing in (Melody)
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Nit [DEREM-6] Both tools take only (Netero, Bisky)
|
||
| Tool: aisdk.Tool{ | ||
| Name: ToolNameListTemplateVersionPresets, | ||
| Description: "Get the presets for a template version. Use these to choose a template_version_preset_id for task or workspace creation.", | ||
| Schema: aisdk.Schema{ | ||
| Properties: map[string]any{ | ||
| "template_version_id": map[string]any{ | ||
| "type": "string", | ||
| }, | ||
| }, | ||
| Required: []string{"template_version_id"}, | ||
| }, | ||
| }, | ||
| MCPAnnotations: mcpReadOnlyAnnotations, | ||
| Handler: func(ctx context.Context, deps Deps, args ListTemplateVersionParametersArgs) ([]codersdk.Preset, error) { | ||
| templateVersionID, err := uuid.Parse(args.TemplateVersionID) | ||
| if err != nil { | ||
| return nil, xerrors.Errorf("template_version_id must be a valid UUID: %w", err) | ||
| } | ||
| presets, err := deps.coderClient.TemplateVersionPresets(ctx, templateVersionID) | ||
| if err != nil { | ||
| return nil, err | ||
| } | ||
| return presets, nil | ||
| }, | ||
| } | ||
|
|
||
| var GetAuthenticatedUser = Tool[NoArgs, codersdk.User]{ | ||
| Tool: aisdk.Tool{ | ||
| Name: ToolNameGetAuthenticatedUser, | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -170,6 +170,12 @@ func TestTools(t *testing.T) { | |
| } | ||
| return agents | ||
| }).Do() | ||
| preset := dbgen.Preset(t, store, database.InsertPresetParams{ | ||
| TemplateVersionID: r.TemplateVersion.ID, | ||
| Name: testutil.GetRandomNameHyphenated(t), | ||
| CreatedAt: r.TemplateVersion.CreatedAt, | ||
| Description: "Preset for agent tool tests.", | ||
| }) | ||
|
|
||
| // Given: a client configured with the agent token. | ||
| agentClient := agentsdk.New(client.URL, agentsdk.WithFixedToken(r.AgentToken)) | ||
|
|
@@ -384,6 +390,20 @@ func TestTools(t *testing.T) { | |
| require.Empty(t, params) | ||
| }) | ||
|
|
||
| t.Run("ListTemplateVersionPresets", func(t *testing.T) { | ||
| tb, err := toolsdk.NewDeps(memberClient) | ||
| require.NoError(t, err) | ||
| presets, err := testTool(t, toolsdk.ListTemplateVersionPresets, tb, toolsdk.ListTemplateVersionParametersArgs{ | ||
| TemplateVersionID: r.TemplateVersion.ID.String(), | ||
| }) | ||
|
|
||
| require.NoError(t, err) | ||
| require.Len(t, presets, 1) | ||
| require.Equal(t, preset.ID, presets[0].ID) | ||
| require.Equal(t, preset.Name, presets[0].Name) | ||
| require.Equal(t, preset.Description, presets[0].Description) | ||
| }) | ||
|
|
||
| t.Run("GetWorkspaceAgentLogs", func(t *testing.T) { | ||
| tb, err := toolsdk.NewDeps(memberClient) | ||
| require.NoError(t, err) | ||
|
|
@@ -500,18 +520,34 @@ func TestTools(t *testing.T) { | |
| t.Run("CreateWorkspace", func(t *testing.T) { | ||
| tb, err := toolsdk.NewDeps(client) | ||
| require.NoError(t, err) | ||
| // We need a template version ID to create a workspace | ||
| res, err := testTool(t, toolsdk.CreateWorkspace, tb, toolsdk.CreateWorkspaceArgs{ | ||
| User: "me", | ||
| TemplateVersionID: r.TemplateVersion.ID.String(), | ||
| Name: testutil.GetRandomNameHyphenated(t), | ||
| RichParameters: map[string]string{}, | ||
| t.Run("WithoutPreset", func(t *testing.T) { | ||
| res, err := testTool(t, toolsdk.CreateWorkspace, tb, toolsdk.CreateWorkspaceArgs{ | ||
| User: "me", | ||
| TemplateVersionID: r.TemplateVersion.ID.String(), | ||
| Name: testutil.GetRandomNameHyphenated(t), | ||
| RichParameters: map[string]string{}, | ||
| }) | ||
|
|
||
| require.NoError(t, err) | ||
| require.NotEmpty(t, res.ID, "expected a workspace ID") | ||
| }) | ||
|
|
||
| // The creation might fail for various reasons, but the important thing is | ||
| // to mark it as tested | ||
| require.NoError(t, err) | ||
| require.NotEmpty(t, res.ID, "expected a workspace ID") | ||
| t.Run("WithPreset", func(t *testing.T) { | ||
| res, err := testTool(t, toolsdk.CreateWorkspace, tb, toolsdk.CreateWorkspaceArgs{ | ||
| User: "me", | ||
| TemplateVersionID: r.TemplateVersion.ID.String(), | ||
| TemplateVersionPresetID: preset.ID.String(), | ||
| Name: testutil.GetRandomNameHyphenated(t), | ||
| RichParameters: map[string]string{}, | ||
| }) | ||
|
|
||
| require.NoError(t, err) | ||
| require.NotEmpty(t, res.ID, "expected a workspace ID") | ||
|
|
||
| build := coderdtest.AwaitWorkspaceBuildJobCompleted(t, client, res.LatestBuild.ID) | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. P1 [DEREM-1] The test server is created with The require.NotNil(t, res.LatestBuild.TemplateVersionPresetID)
require.Equal(t, preset.ID, *res.LatestBuild.TemplateVersionPresetID)(Bisky P0, Hisoka P0, Ryosuke P0, Mafu-san P1, Mafuuu P1, Pariston P1, Chopper P1, Kite P1)
|
||
| require.NotNil(t, build.TemplateVersionPresetID) | ||
| require.Equal(t, preset.ID, *build.TemplateVersionPresetID) | ||
| }) | ||
| }) | ||
|
|
||
| t.Run("WorkspaceSSHExec", func(t *testing.T) { | ||
|
|
@@ -1072,11 +1108,10 @@ func TestTools(t *testing.T) { | |
| { | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. P3 [DEREM-3] The fix to use The table-driven structure here only checks error vs no-error, so adding a preset assertion would require minor restructuring. Not a correctness bug, but the test name promises preset coverage that the assertion does not deliver. (Bisky P2, Hisoka P3, Mafuuu P3)
|
||
| name: "WithPreset", | ||
| args: toolsdk.CreateTaskArgs{ | ||
| TemplateVersionID: r.TemplateVersion.ID.String(), | ||
| TemplateVersionID: aiTV.TemplateVersion.ID.String(), | ||
| TemplateVersionPresetID: presetID.String(), | ||
| Input: "not enough barrel rolls", | ||
| }, | ||
| error: "Template does not have a valid \"coder_ai_task\" resource.", | ||
| }, | ||
| } | ||
|
|
||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nit [DEREM-5] "to create the workspace from" misrepresents what a preset does.
The
template_version_iddescription correctly says "to create the workspace from" because the workspace IS created from a template version. The preset description copies the same phrasing, but a preset pre-fills parameter values; it is not the thing you create from. An LLM reading both descriptions side by side could confuse presets with template versions.Suggestion:
"Optional preset to apply when creating the workspace."(Gon, Leorio)