mirror of
https://github.com/coder/coder.git
synced 2026-06-02 20:48:20 +00:00
refactor: generate task name fallback on coderd (#19447)
Instead of generating the fallback task name on the website, we instead generate it on coderd.
This commit is contained in:
+1
-1
@@ -107,7 +107,7 @@ func (api *API) tasksCreate(rw http.ResponseWriter, r *http.Request) {
|
||||
return
|
||||
}
|
||||
|
||||
taskName := req.Name
|
||||
taskName := taskname.GenerateFallback()
|
||||
if anthropicAPIKey := taskname.GetAnthropicAPIKeyFromEnv(); anthropicAPIKey != "" {
|
||||
anthropicModel := taskname.GetAnthropicModelFromEnv()
|
||||
|
||||
|
||||
@@ -151,7 +151,6 @@ func TestTaskCreate(t *testing.T) {
|
||||
var (
|
||||
ctx = testutil.Context(t, testutil.WaitShort)
|
||||
|
||||
taskName = "task-foo-bar-baz"
|
||||
taskPrompt = "Some task prompt"
|
||||
)
|
||||
|
||||
@@ -176,7 +175,6 @@ func TestTaskCreate(t *testing.T) {
|
||||
|
||||
// When: We attempt to create a Task.
|
||||
workspace, err := expClient.CreateTask(ctx, "me", codersdk.CreateTaskRequest{
|
||||
Name: taskName,
|
||||
TemplateVersionID: template.ActiveVersionID,
|
||||
Prompt: taskPrompt,
|
||||
})
|
||||
@@ -184,7 +182,7 @@ func TestTaskCreate(t *testing.T) {
|
||||
coderdtest.AwaitWorkspaceBuildJobCompleted(t, client, workspace.LatestBuild.ID)
|
||||
|
||||
// Then: We expect a workspace to have been created.
|
||||
assert.Equal(t, taskName, workspace.Name)
|
||||
assert.NotEmpty(t, workspace.Name)
|
||||
assert.Equal(t, template.ID, workspace.TemplateID)
|
||||
|
||||
// And: We expect it to have the "AI Prompt" parameter correctly set.
|
||||
@@ -201,7 +199,6 @@ func TestTaskCreate(t *testing.T) {
|
||||
var (
|
||||
ctx = testutil.Context(t, testutil.WaitShort)
|
||||
|
||||
taskName = "task-foo-bar-baz"
|
||||
taskPrompt = "Some task prompt"
|
||||
)
|
||||
|
||||
@@ -217,7 +214,6 @@ func TestTaskCreate(t *testing.T) {
|
||||
|
||||
// When: We attempt to create a Task.
|
||||
_, err := expClient.CreateTask(ctx, "me", codersdk.CreateTaskRequest{
|
||||
Name: taskName,
|
||||
TemplateVersionID: template.ActiveVersionID,
|
||||
Prompt: taskPrompt,
|
||||
})
|
||||
@@ -235,7 +231,6 @@ func TestTaskCreate(t *testing.T) {
|
||||
var (
|
||||
ctx = testutil.Context(t, testutil.WaitShort)
|
||||
|
||||
taskName = "task-foo-bar-baz"
|
||||
taskPrompt = "Some task prompt"
|
||||
)
|
||||
|
||||
@@ -251,7 +246,6 @@ func TestTaskCreate(t *testing.T) {
|
||||
|
||||
// When: We attempt to create a Task with an invalid template version ID.
|
||||
_, err := expClient.CreateTask(ctx, "me", codersdk.CreateTaskRequest{
|
||||
Name: taskName,
|
||||
TemplateVersionID: uuid.New(),
|
||||
Prompt: taskPrompt,
|
||||
})
|
||||
|
||||
@@ -2,11 +2,15 @@ package taskname
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"io"
|
||||
"math/rand/v2"
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"github.com/anthropics/anthropic-sdk-go"
|
||||
anthropicoption "github.com/anthropics/anthropic-sdk-go/option"
|
||||
"github.com/moby/moby/pkg/namesgenerator"
|
||||
"golang.org/x/xerrors"
|
||||
|
||||
"github.com/coder/aisdk-go"
|
||||
@@ -20,19 +24,17 @@ const (
|
||||
Requirements:
|
||||
- Only lowercase letters, numbers, and hyphens
|
||||
- Start with "task-"
|
||||
- End with a random number between 0-99
|
||||
- Maximum 32 characters total
|
||||
- Maximum 28 characters total
|
||||
- Descriptive of the main task
|
||||
|
||||
Examples:
|
||||
- "Help me debug a Python script" → "task-python-debug-12"
|
||||
- "Create a React dashboard component" → "task-react-dashboard-93"
|
||||
- "Analyze sales data from Q3" → "task-analyze-q3-sales-37"
|
||||
- "Set up CI/CD pipeline" → "task-setup-cicd-44"
|
||||
- "Help me debug a Python script" → "task-python-debug"
|
||||
- "Create a React dashboard component" → "task-react-dashboard"
|
||||
- "Analyze sales data from Q3" → "task-analyze-q3-sales"
|
||||
- "Set up CI/CD pipeline" → "task-setup-cicd"
|
||||
|
||||
If you cannot create a suitable name:
|
||||
- Respond with "task-unnamed"
|
||||
- Do not end with a random number`
|
||||
- Respond with "task-unnamed"`
|
||||
)
|
||||
|
||||
var (
|
||||
@@ -67,6 +69,32 @@ func GetAnthropicModelFromEnv() anthropic.Model {
|
||||
return anthropic.Model(os.Getenv("ANTHROPIC_MODEL"))
|
||||
}
|
||||
|
||||
// generateSuffix generates a random hex string between `0000` and `ffff`.
|
||||
func generateSuffix() string {
|
||||
numMin := 0x00000
|
||||
numMax := 0x10000
|
||||
//nolint:gosec // We don't need a cryptographically secure random number generator for generating a task name suffix.
|
||||
num := rand.IntN(numMax-numMin) + numMin
|
||||
|
||||
return fmt.Sprintf("%04x", num)
|
||||
}
|
||||
|
||||
func GenerateFallback() string {
|
||||
// We have a 32 character limit for the name.
|
||||
// We have a 5 character prefix `task-`.
|
||||
// We have a 5 character suffix `-ffff`.
|
||||
// This leaves us with 22 characters for the middle.
|
||||
//
|
||||
// Unfortunately, `namesgenerator.GetRandomName(0)` will
|
||||
// generate names that are longer than 22 characters, so
|
||||
// we just trim these down to length.
|
||||
name := strings.ReplaceAll(namesgenerator.GetRandomName(0), "_", "-")
|
||||
name = name[:min(len(name), 22)]
|
||||
name = strings.TrimSuffix(name, "-")
|
||||
|
||||
return fmt.Sprintf("task-%s-%s", name, generateSuffix())
|
||||
}
|
||||
|
||||
func Generate(ctx context.Context, prompt string, opts ...Option) (string, error) {
|
||||
o := options{}
|
||||
for _, opt := range opts {
|
||||
@@ -127,7 +155,7 @@ func Generate(ctx context.Context, prompt string, opts ...Option) (string, error
|
||||
return "", ErrNoNameGenerated
|
||||
}
|
||||
|
||||
return generatedName, nil
|
||||
return fmt.Sprintf("%s-%s", generatedName, generateSuffix()), nil
|
||||
}
|
||||
|
||||
func anthropicDataStream(ctx context.Context, client anthropic.Client, model anthropic.Model, input []aisdk.Message) (aisdk.DataStream, error) {
|
||||
|
||||
@@ -15,6 +15,14 @@ const (
|
||||
anthropicEnvVar = "ANTHROPIC_API_KEY"
|
||||
)
|
||||
|
||||
func TestGenerateFallback(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
name := taskname.GenerateFallback()
|
||||
err := codersdk.NameValid(name)
|
||||
require.NoErrorf(t, err, "expected fallback to be valid workspace name, instead found %s", name)
|
||||
}
|
||||
|
||||
func TestGenerateTaskName(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
|
||||
@@ -47,7 +47,6 @@ func (c *ExperimentalClient) AITaskPrompts(ctx context.Context, buildIDs []uuid.
|
||||
}
|
||||
|
||||
type CreateTaskRequest struct {
|
||||
Name string `json:"name"`
|
||||
TemplateVersionID uuid.UUID `json:"template_version_id" format:"uuid"`
|
||||
TemplateVersionPresetID uuid.UUID `json:"template_version_preset_id,omitempty" format:"uuid"`
|
||||
Prompt string `json:"prompt"`
|
||||
|
||||
Generated
-1
@@ -478,7 +478,6 @@ export interface CreateProvisionerKeyResponse {
|
||||
|
||||
// From codersdk/aitasks.go
|
||||
export interface CreateTaskRequest {
|
||||
readonly name: string;
|
||||
readonly template_version_id: string;
|
||||
readonly template_version_preset_id?: string;
|
||||
readonly prompt: string;
|
||||
|
||||
@@ -53,7 +53,6 @@ import { useAuthenticated } from "hooks";
|
||||
import { useExternalAuth } from "hooks/useExternalAuth";
|
||||
import { RedoIcon, RotateCcwIcon, SendIcon } from "lucide-react";
|
||||
import { AI_PROMPT_PARAMETER_NAME, type Task } from "modules/tasks/tasks";
|
||||
import { generateWorkspaceName } from "modules/workspaces/generateWorkspaceName";
|
||||
import { WorkspaceAppStatus } from "modules/workspaces/WorkspaceAppStatus/WorkspaceAppStatus";
|
||||
import { type FC, type ReactNode, useEffect, useState } from "react";
|
||||
import { Helmet } from "react-helmet-async";
|
||||
@@ -741,7 +740,6 @@ export const data = {
|
||||
}
|
||||
|
||||
const workspace = await API.experimental.createTask(userId, {
|
||||
name: `task-${generateWorkspaceName()}`,
|
||||
template_version_id: templateVersionId,
|
||||
template_version_preset_id: preset_id || undefined,
|
||||
prompt,
|
||||
|
||||
Reference in New Issue
Block a user