From 406aa46a5aa54fcb9790aab8e9790cb29d8e1f3f Mon Sep 17 00:00:00 2001 From: William Petit Date: Thu, 27 Feb 2025 22:20:40 +0100 Subject: [PATCH] feat: overwrite project issue template --- go.mod | 2 +- go.sum | 2 + internal/core/service/issue_manager.go | 29 +++++++++----- .../webui/issue/component/issue_page.templ | 40 +++++++++++++++++++ .../webui/issue/component/issue_page_templ.go | 39 ++++++++++++++---- .../http/handler/webui/issue/issue_page.go | 13 +++++- internal/setup/issue_manager.go | 14 +++---- 7 files changed, 112 insertions(+), 27 deletions(-) diff --git a/go.mod b/go.mod index dc3b7f9..ef64a46 100644 --- a/go.mod +++ b/go.mod @@ -7,7 +7,7 @@ toolchain go1.23.6 require ( code.gitea.io/sdk/gitea v0.20.0 github.com/a-h/templ v0.3.833 - github.com/bornholm/genai v0.0.0-20250222092500-1076426da67c + github.com/bornholm/genai v0.0.0-20250227201654-4c93b20ee628 github.com/caarlos0/env/v11 v11.2.2 github.com/gabriel-vasile/mimetype v1.4.7 github.com/google/go-github/v69 v69.2.0 diff --git a/go.sum b/go.sum index 74724fe..70f124a 100644 --- a/go.sum +++ b/go.sum @@ -14,6 +14,8 @@ github.com/a-h/templ v0.3.833 h1:L/KOk/0VvVTBegtE0fp2RJQiBm7/52Zxv5fqlEHiQUU= github.com/a-h/templ v0.3.833/go.mod h1:cAu4AiZhtJfBjMY0HASlyzvkrtjnHWPeEsyGK2YYmfk= github.com/bornholm/genai v0.0.0-20250222092500-1076426da67c h1:bI0ebsgO1/7Jx6+ZQdDF/I6tTZxyB5hODYz7x/XxwK8= github.com/bornholm/genai v0.0.0-20250222092500-1076426da67c/go.mod h1:MnuvwSsBEWv/joeK/WgUyfZfOLcLTpd81NJdWoRpRfI= +github.com/bornholm/genai v0.0.0-20250227201654-4c93b20ee628 h1:YsrF9+NUdwYPLfpJUUfD0h/yH0jvpnaMxtM/sPsFsPg= +github.com/bornholm/genai v0.0.0-20250227201654-4c93b20ee628/go.mod h1:kgZb50LiE3cLjyGdUzNwDtpxL5QRllZIsWT2Ub24fIM= github.com/caarlos0/env/v11 v11.2.2 h1:95fApNrUyueipoZN/EhA8mMxiNxrBwDa+oAZrMWl3Kg= github.com/caarlos0/env/v11 v11.2.2/go.mod h1:JBfcdeQiBoI3Zh1QRAWfe+tpiNTmDtcCj/hHHHMx0vc= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= diff --git a/internal/core/service/issue_manager.go b/internal/core/service/issue_manager.go index 7b8199b..a9380e3 100644 --- a/internal/core/service/issue_manager.go +++ b/internal/core/service/issue_manager.go @@ -85,8 +85,13 @@ 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, string, error) { - systemPrompt, err := m.getIssueSystemPrompt(ctx, user, projectID) +type GeneratIssueOptions struct { + IssueSummary string + IssueTemplate string +} + +func (m *IssueManager) GenerateIssue(ctx context.Context, user *model.User, projectID string, issueSummary string, overwrittenIssueTemplate string) (string, string, string, error) { + systemPrompt, err := m.getIssueSystemPrompt(ctx, user, projectID, overwrittenIssueTemplate) if err != nil { return "", "", "", errors.WithStack(err) } @@ -135,15 +140,19 @@ func (m *IssueManager) GenerateIssue(ctx context.Context, user *model.User, proj return title, body, tips, nil } -func (m *IssueManager) getIssueSystemPrompt(ctx context.Context, user *model.User, projectID string) (string, error) { - forge, err := m.getUserForge(ctx, user) - if err != nil { - return "", errors.WithStack(err) - } +func (m *IssueManager) getIssueSystemPrompt(ctx context.Context, user *model.User, projectID string, issueTemplate string) (string, error) { + if issueTemplate == "" { + forge, err := m.getUserForge(ctx, user) + if err != nil { + return "", errors.WithStack(err) + } - issueTemplate, err := forge.GetIssueTemplate(ctx, projectID) - if err != nil && !errors.Is(err, port.ErrFileNotFound) { - return "", errors.WithStack(err) + repoIssueTemplate, err := forge.GetIssueTemplate(ctx, projectID) + if err != nil && !errors.Is(err, port.ErrFileNotFound) { + return "", errors.WithStack(err) + } + + issueTemplate = repoIssueTemplate } if issueTemplate == "" { diff --git a/internal/http/handler/webui/issue/component/issue_page.templ b/internal/http/handler/webui/issue/component/issue_page.templ index 6a63f60..54c2df1 100644 --- a/internal/http/handler/webui/issue/component/issue_page.templ +++ b/internal/http/handler/webui/issue/component/issue_page.templ @@ -31,6 +31,10 @@ const bodyPlaceholder = ` Une fois votre demande générée, vous pourrez l'éditer puis la créer directement en cliquant sur le bouton 'Créer' ci-dessous. ` +const issueTemplatePlaceholder = ` +Vous pouvez surcharger le modèle de demande fourni par le projet en remplissant ce champ. +` + func NewIssueSummaryForm() *form.Form { return form.New( form.NewField( @@ -47,6 +51,14 @@ func NewIssueSummaryForm() *form.Form { }, form.NonEmpty("Ce champ ne doit pas être vide."), ), + form.NewField( + "template", + form.Attrs{ + "type": "textarea", + "rows": "20", + "placeholder": strings.TrimSpace(issueTemplatePlaceholder), + }, + ), ) } @@ -127,6 +139,15 @@ templ IssuePage(vmodel IssuePageVModel) { "hx-on:change", "onSummaryChange(event)", ), ) +
+ Paramètres avancés + @common.FormTextarea( + vmodel.SummaryForm, "issue-template", "template", "Surcharger le modèle de demande", + common.WithTextareaAttrs( + "hx-on:change", "onIssueTemplateChange(event)", + ), + ) +

Votre demande

Paramètres avancés") + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + templ_7745c5c3_Err = common.FormTextarea( + vmodel.SummaryForm, "issue-template", "template", "Surcharger le modèle de demande", + common.WithTextareaAttrs( + "hx-on:change", "onIssueTemplateChange(event)", + ), + ).Render(ctx, templ_7745c5c3_Buffer) + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 10, "

Votre demande

") + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 11, "\" method=\"post\" hx-disabled-elt=\"textarea, input, select, button\" hx-indicator=\"#generation-progress\">") if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } @@ -221,14 +246,14 @@ func IssuePage(vmodel IssuePageVModel) templ.Component { if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } - templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 11, "
") + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 12, "
") 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, "

Questionnements

Utilisez ces quelques questions pour réfléchir aux éléments d'informations nécessaire à la bonne rédaction de votre demande:

") + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 13, "

Questionnements

Utilisez ces quelques questions pour réfléchir aux éléments d'informations nécessaire à la bonne rédaction de votre demande:

") if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } @@ -236,13 +261,13 @@ func IssuePage(vmodel IssuePageVModel) templ.Component { if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } - templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 13, "
") + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 14, "
") if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } } } - templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 14, "") + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 15, "") if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } diff --git a/internal/http/handler/webui/issue/issue_page.go b/internal/http/handler/webui/issue/issue_page.go index 263e1c3..f60792c 100644 --- a/internal/http/handler/webui/issue/issue_page.go +++ b/internal/http/handler/webui/issue/issue_page.go @@ -3,6 +3,7 @@ package issue import ( "context" "net/http" + "strings" "forge.cadoles.com/wpetit/clearcase/internal/core/service" httpCtx "forge.cadoles.com/wpetit/clearcase/internal/http/context" @@ -99,13 +100,21 @@ func (h *Handler) handleIssueSummaryForm(w http.ResponseWriter, r *http.Request) return } - summary, err := form.FormFieldAttr[string](issueSummaryForm, "summary", "value") + issueSummary, err := form.FormFieldAttr[string](issueSummaryForm, "summary", "value") if err != nil { h.handleError(w, r, errors.WithStack(err)) return } - issueTitle, issueBody, issueTips, err := h.issueManager.GenerateIssue(ctx, httpCtx.User(ctx), projectID, summary) + issueTemplate, err := form.FormFieldAttr[string](issueSummaryForm, "template", "value") + if err != nil { + h.handleError(w, r, errors.WithStack(err)) + return + } + + issueTemplate = strings.TrimSpace(issueTemplate) + + issueTitle, issueBody, issueTips, err := h.issueManager.GenerateIssue(ctx, httpCtx.User(ctx), projectID, issueSummary, issueTemplate) if err != nil { h.handleError(w, r, errors.WithStack(err)) return diff --git a/internal/setup/issue_manager.go b/internal/setup/issue_manager.go index 0a215b6..febecdd 100644 --- a/internal/setup/issue_manager.go +++ b/internal/setup/issue_manager.go @@ -13,13 +13,13 @@ import ( ) func NewIssueManagerFromConfig(ctx context.Context, conf *config.Config) (*service.IssueManager, error) { - llmCtx := provider.FromMap(ctx, "", map[string]string{ - string(provider.ContextKeyAPIBaseURL): conf.LLM.Provider.BaseURL, - string(provider.ContextKeyAPIKey): conf.LLM.Provider.Key, - string(provider.ContextKeyModel): conf.LLM.Provider.Model, - }) - - client, err := provider.Create(llmCtx, provider.Name(conf.LLM.Provider.Name)) + client, err := provider.Create(ctx, + provider.WithConfig(&provider.Config{ + Provider: provider.Name(conf.LLM.Provider.Name), + BaseURL: conf.LLM.Provider.BaseURL, + Key: conf.LLM.Provider.Key, + Model: conf.LLM.Provider.Model, + })) if err != nil { return nil, errors.Wrapf(err, "could not create llm client '%s'", conf.LLM.Provider.Name) }