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

feat(coderd): generate task names based on their prompt#19335

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 ourterms of service andprivacy statement. We’ll occasionally send you account related emails.

Already on GitHub?Sign in to your account

Merged
Merged
Show file tree
Hide file tree
Changes fromall commits
Commits
Show all changes
13 commits
Select commitHold shift + click to select a range
afd1c70
feat(coderd): generate task name based on prompt using llm
DanielleMaywoodAug 12, 2025
1ed234e
refactor: slightly
DanielleMaywoodAug 12, 2025
706c789
refactor: slightly again
DanielleMaywoodAug 13, 2025
29f446a
refactor: remove space from prompt
DanielleMaywoodAug 13, 2025
7bd118e
chore: appease linter and formatter
DanielleMaywoodAug 13, 2025
8f51a4c
chore: remove excesss configuration
DanielleMaywoodAug 13, 2025
76b494a
test: add
DanielleMaywoodAug 14, 2025
8bdea7e
chore: appease linter and formatter
DanielleMaywoodAug 14, 2025
38bc49f
chore: some feedback
DanielleMaywoodAug 19, 2025
72595d6
chore: slightly logic oopsie
DanielleMaywoodAug 19, 2025
9562664
chore: replace `errors` with `xerrors`
DanielleMaywoodAug 19, 2025
f5b43a9
Merge branch 'main' into danielle/tasks/generate-task-name-on-coderd
DanielleMaywoodAug 19, 2025
0eed2ae
chore: export option type
DanielleMaywoodAug 19, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 16 additions & 1 deletioncoderd/aitasks.go
View file
Open in desktop
Original file line numberDiff line numberDiff line change
Expand Up@@ -10,11 +10,14 @@ import (

"github.com/google/uuid"

"cdr.dev/slog"

"github.com/coder/coder/v2/coderd/audit"
"github.com/coder/coder/v2/coderd/database"
"github.com/coder/coder/v2/coderd/httpapi"
"github.com/coder/coder/v2/coderd/httpmw"
"github.com/coder/coder/v2/coderd/rbac"
"github.com/coder/coder/v2/coderd/taskname"
"github.com/coder/coder/v2/codersdk"
)

Expand DownExpand Up@@ -104,8 +107,20 @@ func (api *API) tasksCreate(rw http.ResponseWriter, r *http.Request) {
return
}

taskName := req.Name
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
}
}

createReq := codersdk.CreateWorkspaceRequest{
Name:req.Name,
Name:taskName,
TemplateVersionID: req.TemplateVersionID,
TemplateVersionPresetID: req.TemplateVersionPresetID,
RichParameterValues: []codersdk.WorkspaceBuildParameter{
Expand Down
145 changes: 145 additions & 0 deletionscoderd/taskname/taskname.go
View file
Open in desktop
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,145 @@
package taskname

import (
"context"
"io"
"os"

"github.com/anthropics/anthropic-sdk-go"
anthropicoption "github.com/anthropics/anthropic-sdk-go/option"
"golang.org/x/xerrors"

"github.com/coder/aisdk-go"
"github.com/coder/coder/v2/codersdk"
)

const (
defaultModel = anthropic.ModelClaude3_5HaikuLatest
systemPrompt = `Generate a short workspace name from this AI task prompt.

Requirements:
- Only lowercase letters, numbers, and hyphens
- Start with "task-"
- End with a random number between 0-99
- Maximum 32 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"

If you cannot create a suitable name:
- Respond with "task-unnamed"
- Do not end with a random number`
)

var (
ErrNoAPIKey = xerrors.New("no api key provided")
ErrNoNameGenerated = xerrors.New("no task name generated")
)

type options struct {
apiKey string
model anthropic.Model
}

type Option func(o *options)

func WithAPIKey(apiKey string) Option {
return func(o *options) {
o.apiKey = apiKey
}
}

func WithModel(model anthropic.Model) Option {
return func(o *options) {
o.model = model
}
}

func GetAnthropicAPIKeyFromEnv() string {
return os.Getenv("ANTHROPIC_API_KEY")
}

func GetAnthropicModelFromEnv() anthropic.Model {
return anthropic.Model(os.Getenv("ANTHROPIC_MODEL"))
}

func Generate(ctx context.Context, prompt string, opts ...Option) (string, error) {
o := options{}
for _, opt := range opts {
opt(&o)
}

if o.model == "" {
o.model = defaultModel
}
if o.apiKey == "" {
return "", ErrNoAPIKey
}

conversation := []aisdk.Message{
{
Role: "system",
Parts: []aisdk.Part{{
Type: aisdk.PartTypeText,
Text: systemPrompt,
}},
},
{
Role: "user",
Parts: []aisdk.Part{{
Type: aisdk.PartTypeText,
Text: prompt,
}},
},
}

anthropicOptions := anthropic.DefaultClientOptions()
anthropicOptions = append(anthropicOptions, anthropicoption.WithAPIKey(o.apiKey))
anthropicClient := anthropic.NewClient(anthropicOptions...)

stream, err := anthropicDataStream(ctx, anthropicClient, o.model, conversation)
if err != nil {
return "", xerrors.Errorf("create anthropic data stream: %w", err)
}

var acc aisdk.DataStreamAccumulator
stream = stream.WithAccumulator(&acc)

if err := stream.Pipe(io.Discard); err != nil {
return "", xerrors.Errorf("pipe data stream")
}

if len(acc.Messages()) == 0 {
return "", ErrNoNameGenerated
}

generatedName := acc.Messages()[0].Content

if err := codersdk.NameValid(generatedName); err != nil {
return "", xerrors.Errorf("generated name %v not valid: %w", generatedName, err)
}

if generatedName == "task-unnamed" {
return "", ErrNoNameGenerated
}

return generatedName, nil
}

func anthropicDataStream(ctx context.Context, client anthropic.Client, model anthropic.Model, input []aisdk.Message) (aisdk.DataStream, error) {
messages, system, err := aisdk.MessagesToAnthropic(input)
if err != nil {
return nil, xerrors.Errorf("convert messages to anthropic format: %w", err)
}

return aisdk.AnthropicToDataStream(client.Messages.NewStreaming(ctx, anthropic.MessageNewParams{
Model: model,
MaxTokens: 24,
System: system,
Messages: messages,
})), nil
}
48 changes: 48 additions & 0 deletionscoderd/taskname/taskname_test.go
View file
Open in desktop
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
package taskname_test

import (
"os"
"testing"

"github.com/stretchr/testify/require"

"github.com/coder/coder/v2/coderd/taskname"
"github.com/coder/coder/v2/codersdk"
"github.com/coder/coder/v2/testutil"
)

const (
anthropicEnvVar = "ANTHROPIC_API_KEY"
)

func TestGenerateTaskName(t *testing.T) {
t.Parallel()

t.Run("Fallback", func(t *testing.T) {
t.Parallel()

ctx := testutil.Context(t, testutil.WaitShort)

name, err := taskname.Generate(ctx, "Some random prompt")
require.ErrorIs(t, err, taskname.ErrNoAPIKey)
require.Equal(t, "", name)
})

t.Run("Anthropic", func(t *testing.T) {
t.Parallel()

apiKey := os.Getenv(anthropicEnvVar)
if apiKey == "" {
t.Skipf("Skipping test as %s not set", anthropicEnvVar)
}

ctx := testutil.Context(t, testutil.WaitShort)

name, err := taskname.Generate(ctx, "Create a finance planning app", taskname.WithAPIKey(apiKey))
require.NoError(t, err)
require.NotEqual(t, "", name)

err = codersdk.NameValid(name)
require.NoError(t, err, "name should be valid")
})
}
2 changes: 1 addition & 1 deletiongo.mod
View file
Open in desktop
Original file line numberDiff line numberDiff line change
Expand Up@@ -477,6 +477,7 @@ require (
)

require (
github.com/anthropics/anthropic-sdk-go v1.4.0
github.com/brianvoe/gofakeit/v7 v7.3.0
github.com/coder/agentapi-sdk-go v0.0.0-20250505131810-560d1d88d225
github.com/coder/aisdk-go v0.0.9
Expand All@@ -500,7 +501,6 @@ require (
github.com/GoogleCloudPlatform/opentelemetry-operations-go/exporter/metric v0.50.0 // indirect
github.com/GoogleCloudPlatform/opentelemetry-operations-go/internal/resourcemapping v0.50.0 // indirect
github.com/Masterminds/semver/v3 v3.3.1 // indirect
github.com/anthropics/anthropic-sdk-go v1.4.0 // indirect
github.com/aquasecurity/go-version v0.0.1 // indirect
github.com/aquasecurity/trivy v0.58.2 // indirect
github.com/aws/aws-sdk-go v1.55.7 // indirect
Expand Down
Loading

[8]ページ先頭

©2009-2025 Movatter.jp