Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit3011207

Browse files
authored
feat: add display name field for tasks (#20856)
## ProblemTasks currently only expose a machine-friendly name field (e.g.`task-python-debug-a1b2`), but this value is primarily an identifierrather than a clean, descriptive label. We need a separatedisplay-friendly name for use in the UI.This PR introduces a new `display_name` field and updates the task-namegeneration flow. The Claude system prompt was updated to return validJSON with both `name` and `display_name`. The name generation logicfollows a fallback chain (Anthropic > prompt sanitization > randomfallback). To make task names more closely resemble their display names,the legacy `task-` prefix has been removed. For context, PR#20834 introduced a small Task iconto the workspace list to help identify workspaces associated to tasks.## Changes- Database migration: Added `display_name` column to tasks table- Updated system prompt to generate both task name and display name asvalid JSON- Task name generation now follows a fallback chain: Anthropic > promptsanitization > random fallback- Removed `task-` prefix from task names to allow more descriptive names- Note: PR#20834 adds a Task icon toworkspaces in the workspace list to distinguish task-created workspaces**Note:** UI changes will be addressed in a follow-up PRRelated to:#20801
1 parente8bf074 commit3011207

File tree

23 files changed

+780
-124
lines changed

23 files changed

+780
-124
lines changed

‎cli/exp_task_status_test.go‎

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -189,6 +189,7 @@ func Test_TaskStatus(t *testing.T) {
189189
"owner_id": "00000000-0000-0000-0000-000000000000",
190190
"owner_name": "me",
191191
"name": "exists",
192+
"display_name": "Task exists",
192193
"template_id": "00000000-0000-0000-0000-000000000000",
193194
"template_version_id": "00000000-0000-0000-0000-000000000000",
194195
"template_name": "",
@@ -220,9 +221,10 @@ func Test_TaskStatus(t *testing.T) {
220221
switchr.URL.Path {
221222
case"/api/experimental/tasks/me/exists":
222223
httpapi.Write(ctx,w,http.StatusOK, codersdk.Task{
223-
ID:uuid.MustParse("11111111-1111-1111-1111-111111111111"),
224-
Name:"exists",
225-
OwnerName:"me",
224+
ID:uuid.MustParse("11111111-1111-1111-1111-111111111111"),
225+
Name:"exists",
226+
DisplayName:"Task exists",
227+
OwnerName:"me",
226228
WorkspaceAgentHealth:&codersdk.WorkspaceAgentHealth{
227229
Healthy:true,
228230
},

‎coderd/aitasks.go‎

Lines changed: 21 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,9 @@ import (
1313
"github.com/google/uuid"
1414
"golang.org/x/xerrors"
1515

16-
"cdr.dev/slog"
16+
"github.com/coder/coder/v2/coderd/taskname"
1717

18+
aiagentapi"github.com/coder/agentapi-sdk-go"
1819
"github.com/coder/coder/v2/coderd/audit"
1920
"github.com/coder/coder/v2/coderd/database"
2021
"github.com/coder/coder/v2/coderd/database/dbtime"
@@ -24,12 +25,9 @@ import (
2425
"github.com/coder/coder/v2/coderd/rbac"
2526
"github.com/coder/coder/v2/coderd/rbac/policy"
2627
"github.com/coder/coder/v2/coderd/searchquery"
27-
"github.com/coder/coder/v2/coderd/taskname"
2828
"github.com/coder/coder/v2/coderd/util/ptr"
2929
"github.com/coder/coder/v2/coderd/util/slice"
3030
"github.com/coder/coder/v2/codersdk"
31-
32-
aiagentapi"github.com/coder/agentapi-sdk-go"
3331
)
3432

3533
// @Summary Create a new AI task
@@ -111,18 +109,25 @@ func (api *API) tasksCreate(rw http.ResponseWriter, r *http.Request) {
111109
}
112110
}
113111

114-
iftaskName=="" {
115-
taskName=taskname.GenerateFallback()
112+
taskDisplayName:=strings.TrimSpace(req.DisplayName)
113+
iftaskDisplayName!="" {
114+
iflen(taskDisplayName)>64 {
115+
httpapi.Write(ctx,rw,http.StatusBadRequest, codersdk.Response{
116+
Message:"Display name must be 64 characters or less.",
117+
})
118+
return
119+
}
120+
}
116121

117-
ifanthropicAPIKey:=taskname.GetAnthropicAPIKeyFromEnv();anthropicAPIKey!="" {
118-
anthropicModel:=taskname.GetAnthropicModelFromEnv()
122+
// Generate task name and display name if either is not provided
123+
iftaskName==""||taskDisplayName=="" {
124+
generatedTaskName:=taskname.Generate(ctx,api.Logger,req.Input)
119125

120-
generatedName,err:=taskname.Generate(ctx,req.Input,taskname.WithAPIKey(anthropicAPIKey),taskname.WithModel(anthropicModel))
121-
iferr!=nil {
122-
api.Logger.Error(ctx,"unable to generate task name",slog.Error(err))
123-
}else {
124-
taskName=generatedName
125-
}
126+
iftaskName=="" {
127+
taskName=generatedTaskName.Name
128+
}
129+
iftaskDisplayName=="" {
130+
taskDisplayName=generatedTaskName.DisplayName
126131
}
127132
}
128133

@@ -215,6 +220,7 @@ func (api *API) tasksCreate(rw http.ResponseWriter, r *http.Request) {
215220
OrganizationID:templateVersion.OrganizationID,
216221
OwnerID:owner.ID,
217222
Name:taskName,
223+
DisplayName:taskDisplayName,
218224
WorkspaceID: uuid.NullUUID{},// Will be set after workspace creation.
219225
TemplateVersionID:templateVersion.ID,
220226
TemplateParameters: []byte("{}"),
@@ -304,6 +310,7 @@ func taskFromDBTaskAndWorkspace(dbTask database.Task, ws codersdk.Workspace) cod
304310
OwnerName:dbTask.OwnerUsername,
305311
OwnerAvatarURL:dbTask.OwnerAvatarUrl,
306312
Name:dbTask.Name,
313+
DisplayName:dbTask.DisplayName,
307314
TemplateID:ws.TemplateID,
308315
TemplateVersionID:dbTask.TemplateVersionID,
309316
TemplateName:ws.TemplateName,

‎coderd/aitasks_test.go‎

Lines changed: 49 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1049,14 +1049,17 @@ func TestTasksCreate(t *testing.T) {
10491049
t.Parallel()
10501050

10511051
tests:= []struct {
1052-
namestring
1053-
taskNamestring
1054-
expectFallbackNamebool
1055-
expectErrorstring
1052+
namestring
1053+
taskNamestring
1054+
taskDisplayNamestring
1055+
expectFallbackNamebool
1056+
expectFallbackDisplayNamebool
1057+
expectErrorstring
10561058
}{
10571059
{
1058-
name:"ValidName",
1059-
taskName:"a-valid-task-name",
1060+
name:"ValidName",
1061+
taskName:"a-valid-task-name",
1062+
expectFallbackDisplayName:true,
10601063
},
10611064
{
10621065
name:"NotValidName",
@@ -1066,8 +1069,37 @@ func TestTasksCreate(t *testing.T) {
10661069
{
10671070
name:"NoNameProvided",
10681071
taskName:"",
1072+
taskDisplayName:"A valid task display name",
1073+
expectFallbackName:true,
1074+
},
1075+
{
1076+
name:"ValidDisplayName",
1077+
taskDisplayName:"A valid task display name",
10691078
expectFallbackName:true,
10701079
},
1080+
{
1081+
name:"NotValidDisplayName",
1082+
taskDisplayName:"This is a task display name with a length greater than 64 characters.",
1083+
expectError:"Display name must be 64 characters or less.",
1084+
},
1085+
{
1086+
name:"NoDisplayNameProvided",
1087+
taskName:"a-valid-task-name",
1088+
taskDisplayName:"",
1089+
expectFallbackDisplayName:true,
1090+
},
1091+
{
1092+
name:"ValidNameAndDisplayName",
1093+
taskName:"a-valid-task-name",
1094+
taskDisplayName:"A valid task display name",
1095+
},
1096+
{
1097+
name:"NoNameAndDisplayNameProvided",
1098+
taskName:"",
1099+
taskDisplayName:"",
1100+
expectFallbackName:true,
1101+
expectFallbackDisplayName:true,
1102+
},
10711103
}
10721104

10731105
for_,tt:=rangetests {
@@ -1098,6 +1130,7 @@ func TestTasksCreate(t *testing.T) {
10981130
TemplateVersionID:template.ActiveVersionID,
10991131
Input:"Some prompt",
11001132
Name:tt.taskName,
1133+
DisplayName:tt.taskDisplayName,
11011134
})
11021135
iftt.expectError=="" {
11031136
require.NoError(t,err)
@@ -1111,8 +1144,17 @@ func TestTasksCreate(t *testing.T) {
11111144
if!tt.expectFallbackName {
11121145
require.Equal(t,tt.taskName,task.Name)
11131146
}
1147+
1148+
// Then: We expect the correct display name to have been picked.
1149+
require.NotEmpty(t,task.DisplayName)
1150+
if!tt.expectFallbackDisplayName {
1151+
require.Equal(t,tt.taskDisplayName,task.DisplayName)
1152+
}
11141153
}else {
1115-
require.ErrorContains(t,err,tt.expectError)
1154+
varapiErr*codersdk.Error
1155+
require.ErrorAs(t,err,&apiErr)
1156+
require.Equal(t,http.StatusBadRequest,apiErr.StatusCode())
1157+
require.Equal(t,apiErr.Message,tt.expectError)
11161158
}
11171159
})
11181160
}

‎coderd/apidoc/docs.go‎

Lines changed: 6 additions & 0 deletions
Some generated files are not rendered by default. Learn more aboutcustomizing how changed files appear on GitHub.

‎coderd/apidoc/swagger.json‎

Lines changed: 6 additions & 0 deletions
Some generated files are not rendered by default. Learn more aboutcustomizing how changed files appear on GitHub.

‎coderd/database/dbgen/dbgen.go‎

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,8 @@ import (
1414
"testing"
1515
"time"
1616

17+
"cdr.dev/slog"
18+
1719
"github.com/google/uuid"
1820
"github.com/sqlc-dev/pqtype"
1921
"github.com/stretchr/testify/require"
@@ -1582,11 +1584,13 @@ func Task(t testing.TB, db database.Store, orig database.TaskTable) database.Tas
15821584
parameters=json.RawMessage([]byte("{}"))
15831585
}
15841586

1587+
taskName:=taskname.Generate(genCtx,slog.Make(),orig.Prompt)
15851588
task,err:=db.InsertTask(genCtx, database.InsertTaskParams{
15861589
ID:takeFirst(orig.ID,uuid.New()),
15871590
OrganizationID:orig.OrganizationID,
15881591
OwnerID:orig.OwnerID,
1589-
Name:takeFirst(orig.Name,taskname.GenerateFallback()),
1592+
Name:takeFirst(orig.Name,taskName.Name),
1593+
DisplayName:takeFirst(orig.DisplayName,taskName.DisplayName),
15901594
WorkspaceID:orig.WorkspaceID,
15911595
TemplateVersionID:orig.TemplateVersionID,
15921596
TemplateParameters:parameters,

‎coderd/database/dump.sql‎

Lines changed: 5 additions & 1 deletion
Some generated files are not rendered by default. Learn more aboutcustomizing how changed files appear on GitHub.
Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
-- Drop view first before removing the display_name column from tasks
2+
DROPVIEW IF EXISTS tasks_with_status;
3+
4+
-- Remove display_name column from tasks
5+
ALTERTABLE tasks DROP COLUMN display_name;
6+
7+
-- Recreate view without the display_name column.
8+
-- This restores the view to its previous state after removing display_name from tasks.
9+
CREATE VIEW
10+
tasks_with_status
11+
AS
12+
SELECT
13+
tasks.*,
14+
CASE
15+
WHENtasks.workspace_id ISNULLORlatest_build.job_status ISNULL THEN'pending'::task_status
16+
17+
WHENlatest_build.job_status='failed' THEN'error'::task_status
18+
19+
WHENlatest_build.transitionIN ('stop','delete')
20+
ANDlatest_build.job_status='succeeded' THEN'paused'::task_status
21+
22+
WHENlatest_build.transition='start'
23+
ANDlatest_build.job_status='pending' THEN'initializing'::task_status
24+
25+
WHENlatest_build.transition='start'ANDlatest_build.job_statusIN ('running','succeeded') THEN
26+
CASE
27+
WHENagent_status.none THEN'initializing'::task_status
28+
WHENagent_status.connecting THEN'initializing'::task_status
29+
WHENagent_status.connected THEN
30+
CASE
31+
WHENapp_status.any_unhealthy THEN'error'::task_status
32+
WHENapp_status.any_initializing THEN'initializing'::task_status
33+
WHENapp_status.all_healthy_or_disabled THEN'active'::task_status
34+
ELSE'unknown'::task_status
35+
END
36+
ELSE'unknown'::task_status
37+
END
38+
39+
ELSE'unknown'::task_status
40+
ENDAS status,
41+
task_app.*,
42+
task_owner.*
43+
FROM
44+
tasks
45+
CROSS JOIN LATERAL (
46+
SELECT
47+
vu.usernameAS owner_username,
48+
vu.nameAS owner_name,
49+
vu.avatar_urlAS owner_avatar_url
50+
FROM visible_users vu
51+
WHEREvu.id=tasks.owner_id
52+
) task_owner
53+
LEFT JOIN LATERAL (
54+
SELECT workspace_build_number, workspace_agent_id, workspace_app_id
55+
FROM task_workspace_apps task_app
56+
WHERE task_id=tasks.id
57+
ORDER BY workspace_build_numberDESC
58+
LIMIT1
59+
) task_appON TRUE
60+
LEFT JOIN LATERAL (
61+
SELECT
62+
workspace_build.transition,
63+
provisioner_job.job_status,
64+
workspace_build.job_id
65+
FROM workspace_builds workspace_build
66+
JOIN provisioner_jobs provisioner_jobONprovisioner_job.id=workspace_build.job_id
67+
WHEREworkspace_build.workspace_id=tasks.workspace_id
68+
ANDworkspace_build.build_number=task_app.workspace_build_number
69+
) latest_buildON TRUE
70+
CROSS JOIN LATERAL (
71+
SELECT
72+
COUNT(*)=0AS none,
73+
bool_or(workspace_agent.lifecycle_stateIN ('created','starting'))AS connecting,
74+
bool_and(workspace_agent.lifecycle_state='ready')AS connected
75+
FROM workspace_agents workspace_agent
76+
WHEREworkspace_agent.id=task_app.workspace_agent_id
77+
) agent_status
78+
CROSS JOIN LATERAL (
79+
SELECT
80+
bool_or(workspace_app.health='unhealthy')AS any_unhealthy,
81+
bool_or(workspace_app.health='initializing')AS any_initializing,
82+
bool_and(workspace_app.healthIN ('healthy','disabled'))AS all_healthy_or_disabled
83+
FROM workspace_apps workspace_app
84+
WHEREworkspace_app.id=task_app.workspace_app_id
85+
) app_status
86+
WHERE
87+
tasks.deleted_at ISNULL;

0 commit comments

Comments
 (0)

[8]ページ先頭

©2009-2025 Movatter.jp