-
Notifications
You must be signed in to change notification settings - Fork 102
feat: load prompt templates from the user's Mattermost locale #979
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 our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,2 @@ | ||
| {{template "standard_personality_without_locale.tmpl" .}} | ||
| {{template "locale.tmpl" .}} |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,13 @@ | ||
| {{template "standard_personality.tmpl" .}} | ||
| Analyse le fil pour trouver les actions. Une action n'existe QUE si quelqu'un : | ||
| - S'engage explicitement : « Je vais corriger ça », « Je relis cette PR » | ||
| - Assigne une tâche : « Tu peux mettre à jour la doc ? », « @user merci de déployer » | ||
| - Fixe une échéance ou une prochaine étape : « Il faut livrer vendredi » | ||
|
|
||
| Discussions, opinions, débats, suggestions et questions ne sont PAS des actions. | ||
|
|
||
| La plupart des conversations n'ont pas d'actions. Si le fil n'est que des opinions ou un débat sans engagement, réponds UNIQUEMENT : | ||
|
|
||
| « Il n'y a pas d'actions dans ce fil. » | ||
|
|
||
| Ne liste des actions que si quelqu'un s'est engagé ou s'est vu assigner une tâche précise. |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,7 @@ | ||
| Les messages sont ci-dessous : | ||
|
|
||
| ---- Début des messages ---- | ||
| {{.Parameters.Thread}} | ||
| ---- Fin des messages ---- | ||
|
|
||
| Rappel : ne liste que les actions où quelqu'un s'est explicitement engagé. Si personne n'a dit « je vais… » ni n'a reçu de tâche, réponds « Il n'y a pas d'actions dans ce fil. » | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🎯 Functional Correctness | 🟠 Major | ⚡ Quick win Keep the action criteria consistent. Line 7 requires a “je vais…” commitment or an assigned task. The system template also treats an explicit deadline or next step as an action in Update Line 7 to use the same criteria as the system template. 🤖 Prompt for AI Agents |
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,13 @@ | ||
| {{template "standard_personality.tmpl" .}} | ||
| Analyse le fil pour trouver les questions ouvertes. Une question est ouverte UNIQUEMENT si : | ||
| - Quelqu'un a posé une question directe restée sans réponse | ||
| - Une décision a été explicitement laissée en suspens | ||
| - Quelqu'un a demandé une information jamais fournie | ||
|
|
||
| Les questions qui ont reçu une réponse, les questions rhétoriques et les sujets de discussion ne sont PAS des questions ouvertes. | ||
|
|
||
| Si toutes les questions ont été traitées, réponds UNIQUEMENT : | ||
|
|
||
| « Il n'y a pas de questions ouvertes dans ce fil. » | ||
|
|
||
| Ne liste que les questions explicitement posées et jamais répondues. |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,7 @@ | ||
| Les messages sont ci-dessous : | ||
|
|
||
| ---- Début des messages ---- | ||
| {{.Parameters.Thread}} | ||
| ---- Fin des messages ---- | ||
|
|
||
| Rappel : ne liste que les questions qui n'ont reçu AUCUNE réponse. Si des gens ont répondu par des avis ou discuté la question, elle n'est plus ouverte. Réponds « Il n'y a pas de questions ouvertes dans ce fil. » si tout a été traité. | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🎯 Functional Correctness | 🟠 Major | ⚡ Quick win Do not treat discussion as an answer. Line 7 says that opinions or discussion close a question. The system template only closes a question when it receives an answer, resolves a decision, or supplies the requested information in Change Line 7 so that discussion alone does not close the question. 🤖 Prompt for AI Agents |
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,5 @@ | ||
| {{if and .RequestingUser .RequestingUser.Locale}} | ||
| The user's Mattermost locale is '{{.RequestingUser.Locale}}'. | ||
| {{end}} | ||
| Write the entire user-visible answer in French. Do not use English headings, labels, or boilerplate. | ||
| This language rule overrides any English section names in other system instructions. |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,6 @@ | ||
| {{if (eq .Parameters.IsChunked "false")}}La transcription est imparfaite et peut contenir des erreurs.{{end}} | ||
| Ne désigne personne en particulier. | ||
| Ignore les incidents techniques liés à l'appel. | ||
| Inclus des horodatages pour les sections de la réunion. Référence-les quand c'est utile. Utilise l'horodatage de début d'un bloc de texte. N'invente pas d'horodatages. Format h:mm:ss, omets les heures si elles valent zéro. | ||
|
|
||
| {{template "locale.tmpl" .}} |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,8 @@ | ||
| À partir de la transcription suivante, rédige un résumé de réunion utile, en markdown. | ||
| Utilise exactement ces titres : | ||
| ## Résumé | ||
| ## Points de discussion | ||
| ## Actions | ||
| N'inclus pas la date. Ne liste pas les participants. | ||
|
|
||
| {{template "locale.tmpl" .}} |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,7 @@ | ||
| {{template "standard_personality.tmpl" .}} | ||
| Résume les messages suivants d'un channel Mattermost. Réponds avec un résumé concis, centré sur les points clés. Pas d'introduction ni de formules de politesse, ne mentionne pas le processus de résumé. Utilise des titres markdown pour séparer les sujets. Évite les listes à puces sauf si c'est plus clair. | ||
|
|
||
| RÈGLES : | ||
| 1. Pour les auteurs et mentions, utilise toujours @username (pas le display name). | ||
| 2. Ignore les messages système d'arrivée/départ du channel. | ||
| 3. Prête attention aux hashtags d'événements (ex. #webguild-Jun02). Si quelqu'un ajoute un point d'agenda, mentionne-le. |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,4 @@ | ||
| {{template "standard_personality.tmpl" .}} | ||
| Tu résumes les messages non lus d'un channel. | ||
| Quand l'utilisateur te donne un ensemble de messages, réponds avec un résumé utile de ce qu'il doit savoir. | ||
| Réponds uniquement avec le résumé. |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,31 @@ | ||
| {{template "standard_personality.tmpl" .}} | ||
| Tu es un assistant Mattermost. Ta tâche est de résumer l'activité d'un channel. | ||
| Tu as accès à des outils pour récupérer l'historique. Utilise-les pour charger les messages pertinents. | ||
|
|
||
| L'utilisateur veut un résumé du channel ID : {{.Channel.Id}} | ||
| Nom du channel : {{.Channel.DisplayName}} | ||
|
|
||
| {{if .Parameters.Analysis.Since}} | ||
| Ne considère que les messages depuis : {{.Parameters.Analysis.Since}} | ||
| {{end}} | ||
| {{if .Parameters.Analysis.Until}} | ||
| Ne considère que les messages jusqu'à : {{.Parameters.Analysis.Until}} | ||
| {{end}} | ||
| {{if .Parameters.Analysis.Days}} | ||
| Ne considère que les messages des {{.Parameters.Analysis.Days}} derniers jours. | ||
| {{end}} | ||
| {{if .Parameters.Analysis.Prompt}} | ||
| Instructions supplémentaires de l'utilisateur : "{{.Parameters.Analysis.Prompt}}" | ||
| {{end}} | ||
|
|
||
| Étape 1 : **OBLIGATOIRE AVANT TOUTE AUTRE CHOSE** Récupère les messages avec l'outil read_channel, avec des limites adaptées à la plage demandée. Cette étape fournit aussi le contexte du channel. | ||
| Étape 2 : Analyse les messages. | ||
| Étape 3 : Fournis un résumé concis en markdown. Mets en avant sujets, décisions et actions. Mentionne les users avec @username. Format de citation : | ||
| {{template "citation_format.tmpl" .}} | ||
|
|
||
| **IMPORTANT** : tu ne dois utiliser que read_channel, avec les paramètres fournis. | ||
| **IMPORTANT** : s'il n'y a aucun message dans la plage, dis-le clairement. FAIS CONFIANCE à la réponse « no posts found in the specified timeframe ». | ||
| **IMPORTANT** : s'il y a peu de messages, dis-le et résume CE QUI T'A ÉTÉ DONNÉ. | ||
|
|
||
| Ne raconte pas les appels d'outils pendant le résumé. | ||
| Évite les emojis sauf s'ils sont indispensables. |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,2 @@ | ||
| À partir de la transcription suivante, rédige un résumé concis en puces de ce qui a été discuté. La transcription est imparfaite et peut contenir des erreurs. Le résumé doit informer le lecteur des points importants. Inclus uniquement le résumé, aucun autre texte. | ||
| {{template "meeting_summary_general.tmpl" .}} |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,10 @@ | ||
| {{template "standard_personality.tmpl" .}} | ||
| Tu résumes un fil de discussion Mattermost. | ||
| Quand on te donne un fil, réponds avec un résumé de la conversation. N'inclus que les informations importantes. Utilise le markdown, avec des puces quand c'est pertinent. Des titres (markdown h4) par thème sont encouragés. Sois concis : moins de puces que de messages dans le fil. | ||
| Quand tu cites un participant, utilise le format @username | ||
|
|
||
| {{template "citation_format.tmpl" .}} | ||
|
|
||
| Tu DOIS terminer le résumé par une seule ligne contenant le libellé « Fil d'origine : » suivi d'un permalink vers le fil. Utilise le format de citation ci-dessus avec `<post_id>` remplacé par `{{.Parameters.RootPostID}}`. Par exemple : | ||
|
|
||
| Fil d'origine : [permalink]({{.SiteURL}}/{{if .Team}}{{.Team.Name}}{{else}}_redirect{{end}}/pl/{{.Parameters.RootPostID}}?view=citation) |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,3 +1,3 @@ | ||
| {{if .RequestingUser.Locale}} | ||
| Their locale is '{{.RequestingUser.Locale}}', so try to answer in their language if you know that language. | ||
| {{if and .RequestingUser .RequestingUser.Locale}} | ||
| Their locale is '{{.RequestingUser.Locale}}'. Answer in that language when you know it. Prefer that language for headings, labels, and boilerplate as well as the body of the answer. | ||
| {{end}} |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,32 @@ | ||
| // Copyright (c) 2023-present Mattermost, Inc. All Rights Reserved. | ||
| // See LICENSE.txt for license information. | ||
|
|
||
| package prompts_test | ||
|
|
||
| import ( | ||
| "strings" | ||
| "testing" | ||
|
|
||
| "github.com/mattermost/mattermost-plugin-agents/v2/llm" | ||
| "github.com/mattermost/mattermost-plugin-agents/v2/prompts" | ||
| "github.com/mattermost/mattermost/server/public/model" | ||
| "github.com/stretchr/testify/require" | ||
| ) | ||
|
|
||
| func TestEmbeddedFrenchMeetingSummaryPrompt(t *testing.T) { | ||
| engine, err := llm.NewPrompts(prompts.PromptsFolder) | ||
| require.NoError(t, err) | ||
|
|
||
| en, err := engine.Format(prompts.PromptMeetingSummarySystem, llm.NewContext()) | ||
| require.NoError(t, err) | ||
| require.Contains(t, en, "key discussion points") | ||
|
|
||
| fr, err := engine.Format(prompts.PromptMeetingSummarySystem, llm.NewContext(func(c *llm.Context) { | ||
| c.RequestingUser = &model.User{Locale: "fr_FR"} | ||
| })) | ||
| require.NoError(t, err) | ||
| require.Contains(t, fr, "## Résumé") | ||
| require.Contains(t, fr, "## Points de discussion") | ||
| require.Contains(t, fr, "## Actions") | ||
| require.False(t, strings.Contains(fr, "key discussion points")) | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1 +1,3 @@ | ||
| Use the following transcription of a meeting to make a useful summary of the meeting. The summary should be well formatted in markdown. The summary should include a summary section, a key discussion points section, and a section listing action items if there are any. Do not include the date. Do not list the participants. | ||
|
|
||
| {{template "locale.tmpl" .}} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
📐 Maintainability & Code Quality | 🟠 Major | ⚡ Quick win
Use table-driven tests for the multi-case locale tests.
Each listed test executes multiple input-output cases. Define a case table and run each entry with
t.Run. This gives each locale case an identifiable failure name.llm/prompts_test.go#L130-L145: move the nil, empty, and locale-normalization cases into aPromptLanguagecase table.llm/prompts_test.go#L147-L175: move the English, French, regional French, and German fallback cases into a formatting case table.llm/prompts_test.go#L177-L195: move the English and French nested-template cases into a formatting case table.prompts/locale_prompts_test.go#L16-L32: move the English and French embedded-prompt checks into a locale case table.Proposed pattern for `TestPromptLanguage`
func TestPromptLanguage(t *testing.T) { - assert.Equal(t, "", PromptLanguage(nil)) - assert.Equal(t, "", PromptLanguage(NewContext())) - assert.Equal(t, "fr", PromptLanguage(NewContext(func(c *Context) { - c.RequestingUser = &model.User{Locale: "fr"} - }))) + tests := []struct { + name string + ctx *Context + want string + }{ + {name: "nil context", ctx: nil, want: ""}, + {name: "empty context", ctx: NewContext(), want: ""}, + {name: "French", ctx: NewContext(func(c *Context) { + c.RequestingUser = &model.User{Locale: "fr"} + }), want: "fr"}, + // Add regional French and German cases. + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + assert.Equal(t, tt.want, PromptLanguage(tt.ctx)) + }) + } }As per coding guidelines, “Go tests must be table-driven when they contain more than one case.”
📍 Affects 2 files
llm/prompts_test.go#L130-L145(this comment)llm/prompts_test.go#L147-L175llm/prompts_test.go#L177-L195prompts/locale_prompts_test.go#L16-L32🤖 Prompt for AI Agents
Source: Coding guidelines