mirror of
https://github.com/coder/coder.git
synced 2026-09-21 12:44:32 +08:00
feat(coderd): allow specifying a name for a task (#19745)
Relates to https://github.com/coder/internal/issues/955 Add the ability to the tasks endpoint to give a task a custom name.
This commit is contained in:
+22
-8
@@ -113,15 +113,29 @@ func (api *API) tasksCreate(rw http.ResponseWriter, r *http.Request) {
|
||||
return
|
||||
}
|
||||
|
||||
taskName := taskname.GenerateFallback()
|
||||
if anthropicAPIKey := taskname.GetAnthropicAPIKeyFromEnv(); anthropicAPIKey != "" {
|
||||
anthropicModel := taskname.GetAnthropicModelFromEnv()
|
||||
taskName := req.Name
|
||||
if taskName != "" {
|
||||
if err := codersdk.NameValid(taskName); err != nil {
|
||||
httpapi.Write(ctx, rw, http.StatusBadRequest, codersdk.Response{
|
||||
Message: "Unable to create a Task with the provided name.",
|
||||
Detail: err.Error(),
|
||||
})
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
generatedName, err := taskname.Generate(ctx, req.Prompt, taskname.WithAPIKey(anthropicAPIKey), taskname.WithModel(anthropicModel))
|
||||
if err != nil {
|
||||
api.Logger.Error(ctx, "unable to generate task name", slog.Error(err))
|
||||
} else {
|
||||
taskName = generatedName
|
||||
if taskName == "" {
|
||||
taskName = taskname.GenerateFallback()
|
||||
|
||||
if anthropicAPIKey := taskname.GetAnthropicAPIKeyFromEnv(); anthropicAPIKey != "" {
|
||||
anthropicModel := taskname.GetAnthropicModelFromEnv()
|
||||
|
||||
generatedName, err := taskname.Generate(ctx, req.Prompt, taskname.WithAPIKey(anthropicAPIKey), taskname.WithModel(anthropicModel))
|
||||
if err != nil {
|
||||
api.Logger.Error(ctx, "unable to generate task name", slog.Error(err))
|
||||
} else {
|
||||
taskName = generatedName
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -441,6 +441,80 @@ func TestTasksCreate(t *testing.T) {
|
||||
assert.Equal(t, taskPrompt, parameters[0].Value)
|
||||
})
|
||||
|
||||
t.Run("CustomNames", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
taskName string
|
||||
expectFallbackName bool
|
||||
expectError string
|
||||
}{
|
||||
{
|
||||
name: "ValidName",
|
||||
taskName: "a-valid-task-name",
|
||||
},
|
||||
{
|
||||
name: "NotValidName",
|
||||
taskName: "this is not a valid task name",
|
||||
expectError: "Unable to create a Task with the provided name.",
|
||||
},
|
||||
{
|
||||
name: "NoNameProvided",
|
||||
taskName: "",
|
||||
expectFallbackName: true,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
var (
|
||||
ctx = testutil.Context(t, testutil.WaitShort)
|
||||
client = coderdtest.New(t, &coderdtest.Options{IncludeProvisionerDaemon: true})
|
||||
expClient = codersdk.NewExperimentalClient(client)
|
||||
user = coderdtest.CreateFirstUser(t, client)
|
||||
version = coderdtest.CreateTemplateVersion(t, client, user.OrganizationID, &echo.Responses{
|
||||
Parse: echo.ParseComplete,
|
||||
ProvisionApply: echo.ApplyComplete,
|
||||
ProvisionPlan: []*proto.Response{
|
||||
{Type: &proto.Response_Plan{Plan: &proto.PlanComplete{
|
||||
Parameters: []*proto.RichParameter{{Name: "AI Prompt", Type: "string"}},
|
||||
HasAiTasks: true,
|
||||
}}},
|
||||
},
|
||||
})
|
||||
template = coderdtest.CreateTemplate(t, client, user.OrganizationID, version.ID)
|
||||
)
|
||||
|
||||
coderdtest.AwaitTemplateVersionJobCompleted(t, client, version.ID)
|
||||
|
||||
// When: We attempt to create a Task.
|
||||
task, err := expClient.CreateTask(ctx, "me", codersdk.CreateTaskRequest{
|
||||
TemplateVersionID: template.ActiveVersionID,
|
||||
Prompt: "Some prompt",
|
||||
Name: tt.taskName,
|
||||
})
|
||||
if tt.expectError == "" {
|
||||
require.NoError(t, err)
|
||||
require.True(t, task.WorkspaceID.Valid)
|
||||
|
||||
// Then: We expect the correct name to have been picked.
|
||||
err = codersdk.NameValid(task.Name)
|
||||
require.NoError(t, err, "Generated task name should be valid")
|
||||
|
||||
require.NotEmpty(t, task.Name)
|
||||
if !tt.expectFallbackName {
|
||||
require.Equal(t, tt.taskName, task.Name)
|
||||
}
|
||||
} else {
|
||||
require.ErrorContains(t, err, tt.expectError)
|
||||
}
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("FailsOnNonTaskTemplate", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
|
||||
@@ -51,6 +51,7 @@ type CreateTaskRequest struct {
|
||||
TemplateVersionID uuid.UUID `json:"template_version_id" format:"uuid"`
|
||||
TemplateVersionPresetID uuid.UUID `json:"template_version_preset_id,omitempty" format:"uuid"`
|
||||
Prompt string `json:"prompt"`
|
||||
Name string `json:"name,omitempty"`
|
||||
}
|
||||
|
||||
func (c *ExperimentalClient) CreateTask(ctx context.Context, user string, request CreateTaskRequest) (Task, error) {
|
||||
|
||||
Generated
+1
@@ -481,6 +481,7 @@ export interface CreateTaskRequest {
|
||||
readonly template_version_id: string;
|
||||
readonly template_version_preset_id?: string;
|
||||
readonly prompt: string;
|
||||
readonly name?: string;
|
||||
}
|
||||
|
||||
// From codersdk/organizations.go
|
||||
|
||||
Reference in New Issue
Block a user