From 65f9f2ef15e2e864764e2aa18a2cf76d96046ae4 Mon Sep 17 00:00:00 2001 From: William Petit Date: Sun, 23 Feb 2025 14:26:40 +0100 Subject: [PATCH] feat: add suggestions to issue output --- go.mod | 1 + go.sum | 2 + internal/core/service/issue_manager.go | 22 +++++++--- .../webui/issue/component/issue_page.templ | 32 +++++++++++++++ .../webui/issue/component/issue_page_templ.go | 41 ++++++++++++++++++- .../http/handler/webui/issue/issue_page.go | 3 +- 6 files changed, 92 insertions(+), 9 deletions(-) diff --git a/go.mod b/go.mod index c9f3493..5e973d7 100644 --- a/go.mod +++ b/go.mod @@ -38,6 +38,7 @@ require ( github.com/tidwall/match v1.1.1 // indirect github.com/tidwall/pretty v1.2.1 // indirect github.com/tidwall/sjson v1.2.5 // indirect + github.com/yuin/goldmark v1.7.8 // indirect go.opentelemetry.io/otel v1.29.0 // indirect go.opentelemetry.io/otel/trace v1.29.0 // indirect golang.org/x/crypto v0.33.0 // indirect diff --git a/go.sum b/go.sum index 9f86101..cbce944 100644 --- a/go.sum +++ b/go.sum @@ -72,6 +72,8 @@ github.com/tidwall/pretty v1.2.1/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhso github.com/tidwall/sjson v1.2.5 h1:kLy8mja+1c9jlljvWTlSazM7cKDRfJuR/bOJhcY5NcY= github.com/tidwall/sjson v1.2.5/go.mod h1:Fvgq9kS/6ociJEDnK0Fk1cpYF4FIW6ZF7LAe+6jwd28= github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= +github.com/yuin/goldmark v1.7.8 h1:iERMLn0/QJeHFhxSt3p6PeN9mGnvIKSpG9YYorDMnic= +github.com/yuin/goldmark v1.7.8/go.mod h1:uzxRWxtg69N339t3louHJ7+O03ezfj6PlliRlaOzY1E= go.opentelemetry.io/otel v1.29.0 h1:PdomN/Al4q/lN6iBJEN3AwPvUiHPMlt93c8bqTG5Llw= go.opentelemetry.io/otel v1.29.0/go.mod h1:N/WtXPs1CNCUEx+Agz5uouwCba+i+bJGFicT8SR4NP8= go.opentelemetry.io/otel/trace v1.29.0 h1:J/8ZNK4XgR7a21DZUAsbF8pZ5Jcw1VhACmnYt39JTi4= diff --git a/internal/core/service/issue_manager.go b/internal/core/service/issue_manager.go index 667a59f..11ead2c 100644 --- a/internal/core/service/issue_manager.go +++ b/internal/core/service/issue_manager.go @@ -80,17 +80,17 @@ func (m *IssueManager) GetUserProjects(ctx context.Context, user *model.User) ([ return projects, nil } -func (m *IssueManager) GenerateIssue(ctx context.Context, user *model.User, projectID string, issueSummary string) (string, string, error) { +func (m *IssueManager) GenerateIssue(ctx context.Context, user *model.User, projectID string, issueSummary string) (string, string, string, error) { systemPrompt, err := m.getIssueSystemPrompt(ctx, user, projectID) if err != nil { - return "", "", errors.WithStack(err) + return "", "", "", errors.WithStack(err) } slog.DebugContext(ctx, "using system prompt", slog.String("systemPrompt", systemPrompt)) userPrompt, err := m.getIssueUserPrompt(ctx, user, projectID, issueSummary) if err != nil { - return "", "", errors.WithStack(err) + return "", "", "", errors.WithStack(err) } slog.DebugContext(ctx, "using user prompt", slog.String("userPrompt", userPrompt)) @@ -102,7 +102,7 @@ func (m *IssueManager) GenerateIssue(ctx context.Context, user *model.User, proj res, err := m.llmClient.ChatCompletion(ctx, llm.WithMessages(messages...)) if err != nil { - return "", "", errors.WithStack(err) + return "", "", "", errors.WithStack(err) } body := res.Message().Content() @@ -112,12 +112,22 @@ func (m *IssueManager) GenerateIssue(ctx context.Context, user *model.User, proj res, err = m.llmClient.ChatCompletion(ctx, llm.WithMessages(messages...)) if err != nil { - return "", "", errors.WithStack(err) + return "", "", "", errors.WithStack(err) } title := toTitle(res.Message().Content()) - return title, body, nil + messages = append(messages, res.Message()) + messages = append(messages, llm.NewMessage(llm.RoleUser, "Give me a list of questions as Markdown that could help clarify the request. Only write the list without additional headings.")) + + res, err = m.llmClient.ChatCompletion(ctx, llm.WithMessages(messages...)) + if err != nil { + return "", "", "", errors.WithStack(err) + } + + tips := res.Message().Content() + + return title, body, tips, nil } func (m *IssueManager) getIssueSystemPrompt(ctx context.Context, user *model.User, projectID string) (string, error) { diff --git a/internal/http/handler/webui/issue/component/issue_page.templ b/internal/http/handler/webui/issue/component/issue_page.templ index 380f330..c39c27a 100644 --- a/internal/http/handler/webui/issue/component/issue_page.templ +++ b/internal/http/handler/webui/issue/component/issue_page.templ @@ -1,15 +1,21 @@ package component import ( + "bytes" + "context" "forge.cadoles.com/wpetit/clearcase/internal/core/model" "forge.cadoles.com/wpetit/clearcase/internal/http/form" common "forge.cadoles.com/wpetit/clearcase/internal/http/handler/webui/common/component" + "github.com/pkg/errors" + "github.com/yuin/goldmark" + "log/slog" ) type IssuePageVModel struct { IssueURL string SummaryForm *form.Form IssueForm *form.Form + IssueTips string Projects []*model.Project SelectedProjectID string } @@ -128,6 +134,22 @@ templ IssuePage(vmodel IssuePageVModel) { + if vmodel.IssueTips != "" { + {{ html := markdownToHTML(ctx, vmodel.IssueTips) }} + if html != "" { +
+
+

Suggestions

+ +
+
+
+ @templ.Raw(html) +
+
+
+ } + } ") + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 11, "
") + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + if vmodel.IssueTips != "" { + html := markdownToHTML(ctx, vmodel.IssueTips) + if html != "" { + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 12, "

Suggestions

") + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + templ_7745c5c3_Err = templ.Raw(html).Render(ctx, templ_7745c5c3_Buffer) + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 13, "
") + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + } + } + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 14, "") if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } @@ -224,4 +251,14 @@ func projectsToOptions(projects []*model.Project) []string { return options } +func markdownToHTML(ctx context.Context, text string) string { + var buff bytes.Buffer + if err := goldmark.Convert([]byte(text), &buff); err != nil { + slog.ErrorContext(ctx, "could not convert markdown to html", slog.Any("error", errors.WithStack(err))) + return "" + } + + return buff.String() +} + var _ = templruntime.GeneratedTemplate diff --git a/internal/http/handler/webui/issue/issue_page.go b/internal/http/handler/webui/issue/issue_page.go index a94e949..c80c05a 100644 --- a/internal/http/handler/webui/issue/issue_page.go +++ b/internal/http/handler/webui/issue/issue_page.go @@ -105,12 +105,13 @@ func (h *Handler) handleIssueSummaryForm(w http.ResponseWriter, r *http.Request) return } - issueTitle, issueBody, err := h.issueManager.GenerateIssue(ctx, httpCtx.User(ctx), projectID, summary) + issueTitle, issueBody, issueTips, err := h.issueManager.GenerateIssue(ctx, httpCtx.User(ctx), projectID, summary) if err != nil { h.handleError(w, r, errors.WithStack(err)) return } + vmodel.IssueTips = issueTips vmodel.IssueForm.Field("title").Set("value", issueTitle) vmodel.IssueForm.Field("body").Set("value", issueBody)