From 5da724ba4f2e316283dcc0793b2784c125681dd5 Mon Sep 17 00:00:00 2001 From: zggsong Date: Tue, 12 Mar 2024 18:14:49 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E6=B7=BB=E5=8A=A0prompt=E7=AE=A1?= =?UTF-8?q?=E7=90=86?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/Attachments/iconfont/demo_index.html | 52 +++++++- src/Attachments/iconfont/iconfont.css | 14 ++- src/Attachments/iconfont/iconfont.js | 2 +- src/Attachments/iconfont/iconfont.json | 14 +++ src/Attachments/iconfont/iconfont.ttf | Bin 17340 -> 17980 bytes src/Attachments/iconfont/iconfont.woff | Bin 10140 -> 10584 bytes src/Attachments/iconfont/iconfont.woff2 | Bin 8712 -> 9080 bytes src/STranslate.Model/ITranslatorAI.cs | 2 + src/STranslate.Model/Prompt.cs | 25 ++++ src/STranslate.Style/Fonts/iconfont.ttf | Bin 17340 -> 17980 bytes src/STranslate.Style/Styles/ButtonStyle.xaml | 118 ++++++++++++++++++ src/STranslate/Helper/ConfigHelper.cs | 1 + src/STranslate/STranslate.csproj | 3 + .../ViewModels/Preference/ServiceViewModel.cs | 3 +- .../Preference/Services/PromptViewModel.cs | 63 ++++++++++ .../Preference/Services/TranslatorChatglm.cs | 4 + .../Preference/Services/TranslatorGemini.cs | 4 + .../Preference/Services/TranslatorOpenAI.cs | 76 ++++++++++- .../ViewModels/Preference/TTSViewModel.cs | 1 - .../Preference/Service/PromptDialog.xaml | 118 ++++++++++++++++++ .../Preference/Service/PromptDialog.xaml.cs | 64 ++++++++++ .../Service/TextOpenAIServicesPage.xaml | 67 +++++++++- 22 files changed, 616 insertions(+), 15 deletions(-) create mode 100644 src/STranslate/ViewModels/Preference/Services/PromptViewModel.cs create mode 100644 src/STranslate/Views/Preference/Service/PromptDialog.xaml create mode 100644 src/STranslate/Views/Preference/Service/PromptDialog.xaml.cs diff --git a/src/Attachments/iconfont/demo_index.html b/src/Attachments/iconfont/demo_index.html index 0fb201c0..69cc3395 100644 --- a/src/Attachments/iconfont/demo_index.html +++ b/src/Attachments/iconfont/demo_index.html @@ -54,6 +54,18 @@

    +
  • + +
    编辑
    +
    
    +
  • + +
  • + +
    编辑
    +
    
    +
  • +
  • 24gl-clipboardEmpty
    @@ -414,9 +426,9 @@

    第一步:拷贝项目下面生成的 @font-face@font-face { font-family: 'iconfont'; - src: url('iconfont.woff2?t=1709607636773') format('woff2'), - url('iconfont.woff?t=1709607636773') format('woff'), - url('iconfont.ttf?t=1709607636773') format('truetype'); + src: url('iconfont.woff2?t=1710297275859') format('woff2'), + url('iconfont.woff?t=1710297275859') format('woff'), + url('iconfont.ttf?t=1710297275859') format('truetype'); }

    第二步:定义使用 iconfont 的样式

    @@ -442,6 +454,24 @@

    第三步:挑选相应图标并获取字体编码,应用于页面
      +
    • + +
      + 编辑 +
      +
      .icon-bianji +
      +
    • + +
    • + +
      + 编辑 +
      +
      .icon-bianji1 +
      +
    • +
    • @@ -982,6 +1012,22 @@

      第二步:挑选相应图标并获取类名,应用于页面:
        +
      • + +
        编辑
        +
        #icon-bianji
        +
      • + +
      • + +
        编辑
        +
        #icon-bianji1
        +
      • +
      • CCA~g%-jlpV!8g!&icjNz{j{b( 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 literal 8712 zcmV+jBKO^QPew8T0RR9103rwg3jhEB07JY003o*k0RR9100000000000000000000 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 diff --git a/src/STranslate.Model/ITranslatorAI.cs b/src/STranslate.Model/ITranslatorAI.cs index 4845e3fc..4312e473 100644 --- a/src/STranslate.Model/ITranslatorAI.cs +++ b/src/STranslate.Model/ITranslatorAI.cs @@ -5,5 +5,7 @@ namespace STranslate.Model public interface ITranslatorAI : ITranslator { BindingList Prompts { get; set; } + + BindingList UserDefinePrompts { get; set; } } } diff --git a/src/STranslate.Model/Prompt.cs b/src/STranslate.Model/Prompt.cs index 03c7d534..f83fec69 100644 --- a/src/STranslate.Model/Prompt.cs +++ b/src/STranslate.Model/Prompt.cs @@ -1,9 +1,34 @@ using CommunityToolkit.Mvvm.ComponentModel; using Newtonsoft.Json; using System; +using System.ComponentModel; namespace STranslate.Model { + public partial class UserDefinePrompt : ObservableObject, ICloneable + { + [JsonIgnore] + [ObservableProperty] + [property: JsonProperty("name")] + private string _name; + + [JsonIgnore] + [ObservableProperty] + [property: JsonProperty("prompts")] + private BindingList _prompts; + + public UserDefinePrompt(string name, BindingList prompts) + { + Name = name; + Prompts = prompts; + } + + public object Clone() + { + return new UserDefinePrompt(Name, Prompts); + } + } + /// /// Prompt Definition /// diff --git a/src/STranslate.Style/Fonts/iconfont.ttf b/src/STranslate.Style/Fonts/iconfont.ttf index e8a1d80adeb53cc54e7a8ce0ed0cb04adef97594..f21e0df328550f2c9770e8fd44fe85ad731ae0ee 100644 GIT binary patch delta 1970 zcmZ9MZ){Ul7{;G-+upXf|J$v%Yqzo$Y}c(TvOn$E#x{mAIFKM3f+*mSKLcS~!mIj{ zU$P6^o1D9#S*!e6&vl(kikz4z3)e)y_F>!1<3)&jusy!YaWoiu26l0tx35Mo9PmoF z^vIx$$VzF-#6-32m(pfz#w-7{ZBT7DthNV3|7)XAZ4CcyYp1_l+L>NmDM;()_K`ok*MjPsp;;EyEAp$p= zki{Y_MiH&Z@OXCUScnAW&?he0|C*S#c}iZU0qv&M^G6jW2MZ<#N!=`%CM31AV8W0# z1yhG)2`qRBNQZ*Qfz%Z|7^G9dBSN|qJS?QUWOaPl3S=!1DgbFcM5qO1Q1Ljq2(JnC zfvi+eDM%|3Ld_tpJwo*$;|l5s$%9*1rjRuXlnT5kDkw9gr2wJekUa`Y5810=5g?Z< zEW5})1q%bYQUNRBeg$g<`K*FfgIuFv{UFx@C97j8A)iyQsF3RwEHC6s3Kkl2lY%9O z+^%5pAzxFVmgAU$hJf6!pfw=hQ_v)k6AId;1bLhjLgzr9P|!n=UnuA%$g>Li3i1a9 z9ftcnodvyy`wU`1*Wvyu1^tJa0R^3inQs*IBxV_N_BiWLdt&xm1&xZ?pA@t#9$Zw= zym-J5iv?|r2W15fjX(Gz3tAfwKT=pG=kJtAi4U~(+7+AKcEG;Ye!*clHtOq~9nO=k z4X#`6gGMQBOnY3OLC;a|TJIU(3;qlKUjq$+lR;OoFL)-@5E=^I3?B(Uio_%1k((9k zD!#1jsyrLr7`gNMRT-5Zxp4JUsQ%FlA@)qxmzp~t%c@jji~SokyvRr>oYiFD=-`bRzyT*=@ZurAt{|)YmTK8ciZiqvM z%fk>YEezp4Wre6=45Ku6#jS^2lP<5r!4RRKPs@vU`of~Y7PKJEqXfzhf zb@VJX%cEX#BaqD|J>Ecmxcs?TFkHTSP@LxD?1uSGs#RclVp;~ToSd$HZ@+xm|oJPX|SUO5shL+ieS_Xnl#j;4LX$8 zg{Qg^N*AqTyKAUgH@cBzQ{$-Mq98(7?n+fk`7*g|6&K?8{`nQe^YNVjaxVY(zu$XK z?Oag%AUMA(%+7rWk)NRH%F(&gGaK)39*4vx43}o7%Wn*ftb7LiD)!B4BK}KsMg5xS zpPjFqs?%sdp!=!Mop`126vDHX4mE7DV6EI6=hC z>Tu7EEpH2oLG^{m<>5P969hjK-Rc$9wrbfs#DXQ=IzfphQkJl!r7dGw%Uf@?KYBBq zWRh3^i79-{;9{0z|BD5Dyv2W`f(uE;2%}`Vn-1EvdJ9Fm*iW8b?jfc1_p*xzc#wy* zktFTxU~rraX&$A(y-N8mwlPFMd$^xIn&~D-CvhHTfbBfOFuU2Q4MvD@A3e+8#@d3= zmB$_X!PGdE3nqyi$_kTg4&{eMJ<1f5+79K6HF=ah*6h(fu(-$RX$g<^h9y0rwu!li zgf@(&JzEK7)E5!~a|;T|fw{4eFj$XA5@GI|kXV?zCL|l?HX$S+cEBSkv4e!}gjB^! z9_foc?vc{i6CP=fJ>`-5*wY@>0ejZNpxWm=Dh>9$M@7O8dsHs$h=)R`&dVOv5PQX= zYGTtK)fIcwqY7guJ*qW!+M}vtA9_@Otm=^yFnur%c>=rWkvp(2M5vRDg30F&SqA&j zBlEDj(<2+Ps+T!rC{~~G$Xe9P9+`~#S034oH7)9p@mSNZz#$8=cEckx60Uy@4)@`$ z%Zv{9ov#1okyqK!OB`}7n~NS)cOCBpdxKAc+mXrWVDwV79vf{MYA!UNiSLVFOH8%I zS{7TZ^~u&_$-(5=)b6%RZMF79`HGGtu*V?-f{nDfz=lFjn1us1mUJ`JoGuzX3t* B8)X0h diff --git a/src/STranslate.Style/Styles/ButtonStyle.xaml b/src/STranslate.Style/Styles/ButtonStyle.xaml index f8e2d312..ea51d07e 100644 --- a/src/STranslate.Style/Styles/ButtonStyle.xaml +++ b/src/STranslate.Style/Styles/ButtonStyle.xaml @@ -269,4 +269,122 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/STranslate/Helper/ConfigHelper.cs b/src/STranslate/Helper/ConfigHelper.cs index e8c0ef8a..08d4746e 100644 --- a/src/STranslate/Helper/ConfigHelper.cs +++ b/src/STranslate/Helper/ConfigHelper.cs @@ -451,6 +451,7 @@ public override ITranslator ReadJson(JsonReader reader, Type objectType, ITransl if (translator is ITranslatorAI ai) { ai.Prompts.Clear(); + ai.UserDefinePrompts.Clear(); } serializer.Populate(jsonObject.CreateReader(), translator); diff --git a/src/STranslate/STranslate.csproj b/src/STranslate/STranslate.csproj index 516413c9..b04de7c0 100644 --- a/src/STranslate/STranslate.csproj +++ b/src/STranslate/STranslate.csproj @@ -58,6 +58,9 @@ Code + + Code + Code diff --git a/src/STranslate/ViewModels/Preference/ServiceViewModel.cs b/src/STranslate/ViewModels/Preference/ServiceViewModel.cs index 94837343..024546eb 100644 --- a/src/STranslate/ViewModels/Preference/ServiceViewModel.cs +++ b/src/STranslate/ViewModels/Preference/ServiceViewModel.cs @@ -202,8 +202,7 @@ public void NavigationPage(string name, ITranslator translator) if (string.IsNullOrWhiteSpace(name)) throw new ArgumentException("param name is null or empty", nameof(name)); - if (translator == null) - throw new ArgumentNullException(nameof(translator)); + ArgumentNullException.ThrowIfNull(translator); Type? type = Type.GetType(name) ?? throw new Exception($"{nameof(NavigationPage)} get {name} exception"); diff --git a/src/STranslate/ViewModels/Preference/Services/PromptViewModel.cs b/src/STranslate/ViewModels/Preference/Services/PromptViewModel.cs new file mode 100644 index 00000000..7a793b79 --- /dev/null +++ b/src/STranslate/ViewModels/Preference/Services/PromptViewModel.cs @@ -0,0 +1,63 @@ +using CommunityToolkit.Mvvm.ComponentModel; +using CommunityToolkit.Mvvm.Input; +using STranslate.Model; +using System.Linq; +using System.Windows; + +namespace STranslate.ViewModels.Preference.Services +{ + public partial class PromptViewModel : ObservableObject + { + [ObservableProperty] + private UserDefinePrompt _userDefinePrompt; + + private readonly ServiceType _serviceType; + + public PromptViewModel(ServiceType type, UserDefinePrompt definePrompt) + { + _serviceType = type; + UserDefinePrompt = definePrompt; + } + + [RelayCommand] + private void Add() + { + var last = UserDefinePrompt.Prompts.LastOrDefault()?.Role ?? ""; + var newOne = _serviceType switch + { + ServiceType.GeminiService + => last switch + { + "user" => new Prompt("model"), + _ => new Prompt("user") + }, + ServiceType.ChatglmService + => last switch + { + "user" => new Prompt("assistant"), + _ => new Prompt("user") + }, + _ + => last switch + { + "" => new Prompt("system"), + "user" => new Prompt("assistant"), + _ => new Prompt("user") + }, + }; + UserDefinePrompt.Prompts.Add(newOne); + } + + [RelayCommand] + private void Del(Prompt prompt) + { + UserDefinePrompt.Prompts.Remove(prompt); + } + + [RelayCommand] + private void Save(Window window) + { + window.DialogResult = true; + } + } +} diff --git a/src/STranslate/ViewModels/Preference/Services/TranslatorChatglm.cs b/src/STranslate/ViewModels/Preference/Services/TranslatorChatglm.cs index bfe50fde..ab1c22a1 100644 --- a/src/STranslate/ViewModels/Preference/Services/TranslatorChatglm.cs +++ b/src/STranslate/ViewModels/Preference/Services/TranslatorChatglm.cs @@ -98,6 +98,10 @@ public TranslatorChatglm(Guid guid, string url, string name = "", IconType icon #endregion Show/Hide Encrypt Info + [JsonIgnore] + [ObservableProperty] + private BindingList userDefinePrompts = []; + [JsonIgnore] [ObservableProperty] private BindingList prompts = diff --git a/src/STranslate/ViewModels/Preference/Services/TranslatorGemini.cs b/src/STranslate/ViewModels/Preference/Services/TranslatorGemini.cs index 41873968..5b57a7bf 100644 --- a/src/STranslate/ViewModels/Preference/Services/TranslatorGemini.cs +++ b/src/STranslate/ViewModels/Preference/Services/TranslatorGemini.cs @@ -104,6 +104,10 @@ public TranslatorGemini( #endregion Show/Hide Encrypt Info + [JsonIgnore] + [ObservableProperty] + private BindingList userDefinePrompts = []; + [JsonIgnore] [ObservableProperty] private BindingList prompts = diff --git a/src/STranslate/ViewModels/Preference/Services/TranslatorOpenAI.cs b/src/STranslate/ViewModels/Preference/Services/TranslatorOpenAI.cs index d608bc32..79722e37 100644 --- a/src/STranslate/ViewModels/Preference/Services/TranslatorOpenAI.cs +++ b/src/STranslate/ViewModels/Preference/Services/TranslatorOpenAI.cs @@ -15,6 +15,8 @@ namespace STranslate.ViewModels.Preference.Services { public partial class TranslatorOpenAI : ObservableObject, ITranslatorAI { + #region Constructor + public TranslatorOpenAI() : this(Guid.NewGuid(), "https://api.openai.com", "OpenAI") { } @@ -29,8 +31,14 @@ public TranslatorOpenAI(Guid guid, string url, string name = "", IconType icon = AppKey = appKey; IsEnabled = isEnabled; Type = type; + + PromptCounter = Prompts.Count; } + #endregion Constructor + + #region Properties + [ObservableProperty] private Guid _identify = Guid.Empty; @@ -98,19 +106,66 @@ public TranslatorOpenAI(Guid guid, string url, string name = "", IconType icon = #endregion Show/Hide Encrypt Info + #region Prompt + + [JsonIgnore] + [ObservableProperty] + private BindingList _userDefinePrompts = + [ + new UserDefinePrompt("翻译", [new Prompt("system", "You are a professional translation engine, please translate the text into a colloquial, professional, elegant and fluent content, without the style of machine translation. You must only translate the text content, never interpret it."), new Prompt("user", "Translate the following text from $source to $target: $content")]), + new UserDefinePrompt("润色", [new Prompt("system", "You are a text embellisher, you can only embellish the text, never interpret it."), new Prompt("user", "Embellish the following text in $source: $content")]), + new UserDefinePrompt("总结", [new Prompt("system", "You are a text summarizer, you can only summarize the text, never interpret it."), new Prompt("user", "Summarize the following text in $source: $content")]), + ]; + + [RelayCommand] + [property: JsonIgnore] + private void PresetPrompt(BindingList prompts) + { + Prompts = prompts.Clone(); + PromptCounter = Prompts.Count; + } + + [RelayCommand] + [property: JsonIgnore] + private void EditPresetPrompt(UserDefinePrompt userDefinePrompt) + { + var dialog = new Views.Preference.Service.PromptDialog(ServiceType.OpenAIService, (UserDefinePrompt)userDefinePrompt.Clone()); + if (dialog.ShowDialog() ?? false) + { + var tmp = ((PromptViewModel)dialog.DataContext).UserDefinePrompt; + userDefinePrompt.Name = tmp.Name; + userDefinePrompt.Prompts = tmp.Prompts; + } + } + + [RelayCommand] + [property: JsonIgnore] + private void DelPresetPrompt(UserDefinePrompt userDefinePrompt) + { + UserDefinePrompts.Remove(userDefinePrompt); + } + [JsonIgnore] [ObservableProperty] + [NotifyCanExecuteChangedFor(nameof(SavePromptCommand))] private BindingList prompts = [ new Prompt("system", "You are a professional translation engine, please translate the text into a colloquial, professional, elegant and fluent content, without the style of machine translation. You must only translate the text content, never interpret it."), new Prompt("user", "Translate the following text from $source to $target: $content") ]; + [JsonIgnore] + [ObservableProperty] + [NotifyCanExecuteChangedFor(nameof(SavePromptCommand))] + [property: JsonIgnore] + private int promptCounter; + [RelayCommand] [property: JsonIgnore] - private void DeletePrompt(Prompt msg) + private void DelPrompt(Prompt msg) { Prompts.Remove(msg); + PromptCounter--; } [RelayCommand] @@ -125,8 +180,24 @@ private void AddPrompt() _ => new Prompt("user") }; Prompts.Add(newOne); + PromptCounter++; + } + + [property: JsonIgnore] + [RelayCommand(CanExecute = nameof(CanSavePrompt))] + private void SavePrompt(BindingList prompts) + { + UserDefinePrompts.Add(new UserDefinePrompt("UnDefined", prompts)); } + private bool CanSavePrompt => PromptCounter > 0; + + #endregion Prompt + + #endregion Properties + + #region Interface Implementation + public async Task TranslateAsync(object request, Action OnDataReceived, CancellationToken token) { if (string.IsNullOrEmpty(Url) || string.IsNullOrEmpty(AppKey)) @@ -224,7 +295,10 @@ public ITranslator Clone() Icons = this.Icons, KeyHide = this.KeyHide, Prompts = this.Prompts, + UserDefinePrompts = this.UserDefinePrompts, }; } + + #endregion Interface Implementation } } \ No newline at end of file diff --git a/src/STranslate/ViewModels/Preference/TTSViewModel.cs b/src/STranslate/ViewModels/Preference/TTSViewModel.cs index d392febf..1035cf9f 100644 --- a/src/STranslate/ViewModels/Preference/TTSViewModel.cs +++ b/src/STranslate/ViewModels/Preference/TTSViewModel.cs @@ -232,7 +232,6 @@ public void NavigationPage(string name, ITTS tts) private UIElement? _ttsServiceContent; [ObservableProperty] - [NotifyCanExecuteChangedFor(nameof(DeleteCommand))] private int _ttsCounter; [ObservableProperty] diff --git a/src/STranslate/Views/Preference/Service/PromptDialog.xaml b/src/STranslate/Views/Preference/Service/PromptDialog.xaml new file mode 100644 index 00000000..8282466b --- /dev/null +++ b/src/STranslate/Views/Preference/Service/PromptDialog.xaml @@ -0,0 +1,118 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +