Explorar o código

修正錯誤: 修正Actor.OnAccept中session在兩次正確情形下該清除而未清除狀態的錯誤

0x0001F36D %!s(int64=5) %!d(string=hai) anos
pai
achega
49a6739aa3

+ 2 - 0
src/Yuuna.Contracts/Interaction/Response.cs

@@ -5,10 +5,12 @@ namespace Yuuna.Contracts.Interaction
 {
     using System;
     using System.ComponentModel;
+    using System.Reflection;
 
     /// <summary>
     /// 回應物件,表示插件回覆的資訊。
     /// </summary>
+    [DefaultMember("Message")]
     public struct Response
     {
         /// <summary>

+ 60 - 38
src/Yuuna/Actor.cs

@@ -81,58 +81,75 @@ namespace Yuuna
 
     public class Actor
     {
-        private readonly IEnumerable<Response> _canResponses;
-        private readonly Antonym[] _antonyms;
-        private readonly ITextSegmenter _segmenter;
-        private readonly IStrategy _strategy;
-        private readonly IReadOnlyList<ModuleProxy> _allPlugins;
+        private IEnumerable<Response> _canResponses;
+        private Antonym[] _antonyms;
+        private ITextSegmenter _segmenter;
+        private IStrategy _strategy;
+        private IReadOnlyList<ModuleProxy> _moduleProxies;
         private readonly Stack<Match> _session;
         private readonly object _lock = new object();
+        private volatile bool _initialized;
 
-        public Actor(ITextSegmenter segmenter, IEnumerable<Response> canResponses, IStrategy strategy = default)
+        public Actor()
         {
-            this._segmenter = segmenter ?? throw new ArgumentNullException(nameof(segmenter));
-            this._canResponses = canResponses ?? throw new ArgumentNullException(nameof(canResponses));
-            this._strategy = strategy ?? new Strategy();
-
-            this._antonyms = new[]
-            {
-                new Antonym("是", "不是"),
-                new Antonym("對", "不對"),
-                new Antonym("是", "否"),
-            };
-
-
+            this._session = new Stack<Match>();
+        }
+        public Actor(ITextSegmenter segmenter, IEnumerable<Response> canResponses, IStrategy strategy, IReadOnlyList<ModuleProxy> moduleProxies) : this()
+        {
+            this.Initialize(segmenter, canResponses, strategy, moduleProxies);
+        }
+        public void Initialize(ITextSegmenter segmenter, IEnumerable<Response> canResponses, IStrategy strategy, IReadOnlyList<ModuleProxy> moduleProxies)
+        {
+            if (!this._initialized)
+                lock (this._lock)
+                {
+                    if (this._initialized)
+                        return;
+                    this._segmenter = segmenter ?? throw new ArgumentNullException(nameof(segmenter));
+                    this._canResponses = canResponses ?? throw new ArgumentNullException(nameof(canResponses));
+                    this._strategy = strategy ?? new Strategy();
 
-            this._allPlugins = ModuleManager.Instance.Modules;
-            foreach (var plugin in this._allPlugins)
-            {
-                plugin.Initialize(this._segmenter, new GroupManager());
-                Debug.WriteLine("已載入模組: " + plugin.Type.AssemblyQualifiedName);
-            }
+                    this._antonyms = new[]
+                    {
+                        new Antonym("是", "不是"),
+                        new Antonym("對", "不對"),
+                        new Antonym("是", "否"),
+                    };
 
-            this._session = new Stack<Match>();
+                    this._moduleProxies = moduleProxies;
+                    foreach (var plugin in this._moduleProxies)
+                    {
+                        plugin.Initialize(this._segmenter, new GroupManager());
+                        Debug.WriteLine("已載入模組: " + plugin.Type.AssemblyQualifiedName);
+                    }
+                    this._initialized = true;
+                }
         }
 
         public bool Accept(string text, out Response response)
         {
-            lock (this._lock)
+            if (!this._initialized)
             {
-
-                if (string.IsNullOrWhiteSpace(text))
-                    return false;
-
-                response = this.OnAccept(text);
-                return true;
+                lock (this._lock)
+                {
+                    if (!this._initialized)
+                    {
+                        var t = this.GetType();
+                        throw new TypeInitializationException(t.FullName, null);
+                    }
+                }
             }
+            if (string.IsNullOrWhiteSpace(text))
+                return false;
+
+            response = this.OnAccept(text);
+            return true;
         }
 
         protected virtual Response OnAccept(string text)
         {
             lock (this._lock)
             {
-
-
                 var cutted = this._segmenter.Cut(text);
                 //Debug.WriteLine($"來自分詞器 {this._segmenter.Name} 的分詞結果: [ {string.Join(", ", cutted)} ]");
 
@@ -144,14 +161,18 @@ namespace Yuuna
                     {
                         if (antonym.Judge(text, out var type))
                         {
+
                             if (type.Equals(Antonym.TypeKinds.Positive))
                             {
                                 single.Pattern.Owner.Patterns.TryGet(single.Pattern, out var bag);
                                 var r = bag.Invoke(single);
+                                this._session.Clear();
                                 return r;
                             }
-                            else
-                                continue;
+                            else if (type.Equals(Antonym.TypeKinds.Negative))
+                            {
+                                break;
+                            }
                         }
                     }
 
@@ -162,7 +183,8 @@ namespace Yuuna
                     else
                     {
                         var sb = new StringBuilder("還是你是想");
-                        foreach (var g in this._session.Peek().Pattern.ToImmutable())
+                        var peek = this._session.Peek();
+                        foreach (var g in peek.Pattern.ToImmutable())
                             sb.Append(g.RandomTakeOne().RandomTakeOne());
                         sb.Append("?");
                         return sb.ToString();
@@ -171,7 +193,7 @@ namespace Yuuna
 
 
 
-                var alternative = this._strategy.FindBest(this._allPlugins.Select(x => x.PatternSet).ToArray(), cutted);
+                var alternative = this._strategy.FindBest(this._moduleProxies.Select(x => x.PatternSet).ToArray(), cutted);
                 //Debug.WriteLine(alternative.Status);
                 switch (alternative.Status)
                 {

+ 36 - 228
src/Yuuna/EntryPoint.cs

@@ -8,136 +8,53 @@ namespace Yuuna
     using Yuuna.Recognition.Speech;
     using System.Threading.Tasks;
     using System.Reflection;
-    using Yuuna.Contracts.Interaction;
-    using Yuuna.TextSegmention;
-    using System.Diagnostics;
-    using Microsoft.AspNetCore;
-    using Microsoft.AspNetCore.Hosting;
-    using Microsoft.Extensions.DependencyInjection;
-    using Microsoft.AspNetCore.Builder;
+    using System.Diagnostics; 
     using Microsoft.AspNetCore.Routing;
-    using Microsoft.Extensions.Hosting;
     using Microsoft.Extensions.Configuration;
     using Microsoft.AspNetCore.Mvc;
     using Microsoft.Extensions.Logging;
+    using Yuuna.Contracts.Optimization;
+    using Yuuna.Interaction.AspNetCore;
+    using RestSharp;
+    using Yuuna.TextSegmention;
+    using Yuuna.Contracts.Interaction;
 
-    namespace Controllers
-    {
-        public class Yuuna
+    internal class EntryPoint
+    { 
+        public static async Task Main(string[] args)
         {
-            public class Input
-            {
-                public string Text { get; set; }
-            }
-
-            public class Output
-            {
-                public Output(bool success, string message, Mood mood, string text)
-                {
-                    this.Success = success;
-                    this.Message = message;
-                    this.Mood = mood.Name;
-                    this.Text = text;
-                }
-                public bool Success { get; }
-                public string Message { get; }
-                public string Mood { get; }
-                public string Text { get; }
-            }
-        }
-
-        [ApiController]
-        [Route("/")]
-        //[Route("[controller]")]
-        public class YuunaController : ControllerBase
-        { 
-
-            public YuunaController(  Actor actor)
-            { 
-                this._actor = actor;
-
-            }
-
-            private readonly Actor _actor;
-
-            [HttpGet, HttpPost]
-            public Yuuna.Output Post([FromBody]Yuuna.Input text)
+            //await WebHost.RunAsync();
+            var actor = new Actor(new JiebaTextSegmenter(), new Response[]
             {
-                return new Yuuna.Output(this._actor.Accept(text.Text, out var r), r.Message, r.Mood, text.Text); 
-            }
-        }
-    }
-    public class Startup
-    {
-        public Startup()
-        {
-        }
-
-
-        // This method gets called by the runtime. Use this method to add services to the container.
-        public void ConfigureServices(IServiceCollection services)
-        {
-            services.AddSingleton(new Actor(new JiebaTextSegmenter(), new Response[]
-           {
                 (Moods.Sad, "我不清楚你想做什麼 OvQ"),
                 (Moods.Sad, "我不懂你想幹嘛 QAQ"),
                 (Moods.Sad, "我不知道你想幹嘛 OHQ"),
-           }));
-            services.AddControllers();
-        }
+            }, new Strategy(), ModuleManager.Instance.Modules);
 
-        // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
-        public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
-        {
-            if (env.IsDevelopment())
+            while (true)
             {
-                app.UseDeveloperExceptionPage();
-            }
-
-            //app.UseHttpsRedirection();
-             
-            app.UseRouting(); 
-
-            app.UseEndpoints(endpoints =>
-            {
-                endpoints.MapControllers();
-            });
-        }
-    }
-    internal class EntryPoint
-    { 
-        public static void Main(string[] args)
-        {
-            Host.CreateDefaultBuilder(args)
-                .ConfigureWebHostDefaults(webBuilder =>
+                Console.Write("Me:  ");
+                if (actor.Accept(Console.ReadLine(), out var response))
                 {
-                    webBuilder.UseUrls("http://0.0.0.0:5000");
-                    webBuilder.UseStartup<Startup>();
-                })
-                .Build()
-                .Run();
+                    Console.WriteLine("Bot: " + response.Message);
+                }
+                else
+                {
+                    Console.CursorTop--;
+                }
+            }
 
-            //var actor = new Actor(new JiebaTextSegmenter(), new Response[]
+            //while (true)
             //{
-            //    (Moods.Sad, "我不清楚你想做什麼 OvQ"),
-            //    (Moods.Sad, "我不懂你想幹嘛 QAQ"),
-            //    (Moods.Sad, "我不知道你想幹嘛 OHQ"),
-            //});
+            //    var client = new RestClient("http://api.yuuna-project.com:5000/");
+            //    client.Timeout = -1;
+            //    var request = new RestRequest(Method.POST);
+            //    request.AddHeader("Content-Type", "application/json");
+            //    request.AddParameter("application/json", "{\n    \"text\": \""+Console.ReadLine()+"\"\n}", ParameterType.RequestBody);
+            //    var response = client.Execute(request);
+            //    Console.WriteLine("Bot: " + response.Content);
+            //}
 
-            //while (true) 
-            //{
-            //    Console.Write("Me:  "); 
-            //    if (actor.Accept(Console.ReadLine(), out var response))
-            //    {
-            //        Console.WriteLine("Bot: " + response.Message);
-            //    }
-            //    else
-            //    {
-            //        Console.CursorTop --; 
-            //    }
-            //} 
-             
-             
             //var canResponses = new Response[]
             //{
             //    (Moods.Sad, "我不清楚你想做什麼 OvQ"),
@@ -190,7 +107,7 @@ namespace Yuuna
             //    case AlternativeStatus.Condition:
             //        {
             //            var sb = new StringBuilder();
-                        
+
             //            sb.Append("你是想要"); 
 
             //            foreach (var g in alternative.Matches[0].Pattern.ToImmutable())
@@ -203,6 +120,8 @@ namespace Yuuna
 
             //            text = interactor.Accept();
 
+
+
             //            foreach (var antonym in antonyms)
             //            {
             //                if (antonym.Judge(text, out var type))
@@ -290,7 +209,7 @@ namespace Yuuna
             //                        break;
             //                }
             //            }
-                         
+
             //            interactor.OnReceive(canResponses.RandomTakeOne()); 
 
             //            Leave:
@@ -311,6 +230,7 @@ namespace Yuuna
             //}
             //goto LOOP;
 
+
             //try
             //{Send("開燈");
             //    var stt = SpeechRecognizer.Create("secret.json");
@@ -344,117 +264,5 @@ namespace Yuuna
             //    Console.ReadKey();
             //} 
         }
-
-        public static void Send(string text)
-        {
-            
-
-            //Console.WriteLine("我: " + text);
-            //var cutted = segmenter.Cut(text);
-            //Console.WriteLine($"來自分詞器 {segmenter.Name} 的分詞結果: [ {string.Join(", ", cutted)} ]");
-
-            //var alternative = plan.FindBest(allPlugins.Select(x=>x.PatternSet).ToArray(), cutted);
-            //switch (alternative.Status)
-            //{
-            //    // 罐頭回應
-            //    case Status.Invalid:
-            //        view.OnResponsed(s_canResponses.RandomTakeOne());
-            //        break;
-
-            //    case Status.Optimal:
-            //        {
-            //            var single = alternative.Matches[0];
-            //            single.Pattern.Owner.Patterns.TryGet(single.Pattern, out var bag);
-            //            var r = bag.Invoke(single);
-            //            view.OnResponsed(r);
-            //        }
-            //        break;
-
-            //    case Status.Condition:
-            //        {
-            //            Console.WriteLine("proposition");
-            //            Console.Write("你是想要");
-            //            foreach (var matched in alternative.Matches[0].SequentialMatches)
-            //            {
-            //                Console.Write(matched.ToImmutable().RandomTakeOne());
-            //            }
-            //            foreach (var missing in alternative.Matches[0].Missing)
-            //            {
-            //                foreach (var unmatch in missing.ToImmutable())
-            //                {
-            //                    Console.Write(unmatch.ToImmutable().RandomTakeOne());
-            //                }
-            //            }
-            //            Console.WriteLine("嗎?");
-            //            Console.ReadLine()
-            //        }
-            //        break;
-
-            //    case Status.Proposition:
-            //        Console.WriteLine("proposition");
-            //        break;
-
-            //    case Status.Paradox:
-            //        Console.WriteLine("paradox");
-            //        break;
-
-            //    case Status.NoModuleInstalled:
-            //        Console.WriteLine("noModuleInstalled");
-            //        Console.WriteLine("無效的模組");
-            //        break;
-
-            //    default:
-            //        throw new InvalidOperationException();
-            //}
-
-
-
-            //var list = new List<IScore>();
-            //foreach (var p in allPlugins)
-            //    list.AddRange(p.Evaluate(cutted));
-            //list.Sort(ScoreComparer.Default);
-
-            //var sb = new StringBuilder("Bot: ");
-            //var best = list.LastOrDefault();
-            //if (best is null)
-            //    sb.Append("我不懂你的意思");
-            //else if (best.IsCompacted)
-            //{
-            //    var resp = best.Owner.SelectBest(best).Invoke(best);
-            //    Console.WriteLine(resp.Mood.ToString());
-            //    sb.Append(resp.Message);
-            //}
-            //else
-            //{
-            //    var choices = list.Where(s => s.Missing.Count <= 1).ToImmutableArray();
-            //    switch (choices.Length)
-            //    {
-            //        case 1:
-            //            {
-            //                Console.WriteLine("#1");
-            //                sb.Append("你" + new[] { "是想", "想要" }.RandomTakeOne() + " ");
-            //                sb.Append(choices.Select(x => x.Pattern.ToImmutable().Aggregate(string.Empty, (s, g) => s += g.ToImmutable().RandomTakeOne().ToImmutable().RandomTakeOne())));
-            //                sb.Append(" 嗎?");
-            //            }
-            //            break;
-
-            //        case 2:
-            //            {
-            //                Console.WriteLine("#2");
-            //                sb.Append("你" + new[] { "是想", "想要" }.RandomTakeOne() + " ");
-            //                sb.AppendJoin(" 還是 ", choices.Select(x => x.Pattern.ToImmutable().Aggregate(string.Empty, (s, g) => s += g.ToImmutable().RandomTakeOne().ToImmutable().RandomTakeOne())));
-            //                sb.Append(" ?");
-            //            }
-            //            break;
-
-            //        default:
-            //            Console.WriteLine("#n");
-            //            sb.Append("我不太清楚你想做什麼");
-            //            break;
-            //    }
-            //}
-
-            //Console.WriteLine(sb);
-        }
     }
 }

+ 27 - 0
src/Yuuna/Interaction/AspNetCore/Mvc/Controllers/ActorController.cs

@@ -0,0 +1,27 @@
+
+namespace Yuuna.Interaction.AspNetCore.Mvc.Controllers
+{
+    using Microsoft.AspNetCore.Mvc;
+    using System;
+    using System.Collections.Generic;
+    using System.Text;
+    using Yuuna.Interaction.AspNetCore.Mvc.Models;
+
+    [ApiController]
+    [Route("/")]
+    public class ActorController : ControllerBase
+    {
+        public ActorController(Actor actor)
+        {
+            this._actor = actor;
+        }
+
+        private readonly Actor _actor;
+
+        [HttpGet, HttpPost]
+        public Output Post([FromBody]Input text)
+        {
+            return new Output(this._actor.Accept(text.Text, out var r), r.Message, r.Mood, text.Text);
+        }
+    }
+}

+ 12 - 0
src/Yuuna/Interaction/AspNetCore/Mvc/Models/Input.cs

@@ -0,0 +1,12 @@
+
+namespace Yuuna.Interaction.AspNetCore.Mvc.Models
+{
+    using System;
+    using System.Collections.Generic;
+    using System.Text;
+     
+    public class Input
+    {
+        public string Text { get; set; }
+    }
+}

+ 23 - 0
src/Yuuna/Interaction/AspNetCore/Mvc/Models/Output.cs

@@ -0,0 +1,23 @@
+
+namespace Yuuna.Interaction.AspNetCore.Mvc.Models
+{
+    using System;
+    using System.Collections.Generic;
+    using System.Text;
+    using Yuuna.Contracts.Interaction;
+
+    public class Output
+    {
+        public Output(bool success, string message, Mood mood, string text)
+        {
+            this.Success = success;
+            this.Message = message;
+            this.Mood = mood.Name;
+            this.Text = text;
+        }
+        public bool Success { get; }
+        public string Message { get; }
+        public string Mood { get; }
+        public string Text { get; }
+    }
+}

+ 69 - 0
src/Yuuna/Interaction/AspNetCore/WebHost.cs

@@ -0,0 +1,69 @@
+// Author: Orlys
+// Github: https://github.com/Orlys
+
+namespace Yuuna.Interaction.AspNetCore
+{
+    using System.Threading.Tasks; 
+    using Yuuna.Contracts.Interaction;
+    using Yuuna.TextSegmention;
+    using Microsoft.AspNetCore.Hosting;
+    using Microsoft.Extensions.DependencyInjection;
+    using Microsoft.AspNetCore.Builder;
+    using Microsoft.Extensions.Hosting;
+    using Microsoft.AspNetCore.Http;
+    using System;
+
+    public sealed class WebHost 
+    {
+        public static async Task RunAsync()
+        {
+            await Host.CreateDefaultBuilder()
+                .ConfigureWebHostDefaults(webBuilder =>
+                {
+                    webBuilder
+                        .UseUrls("http://0.0.0.0:5000")
+                        .UseStartup<Startup>();
+                })
+                .Build()
+                .RunAsync();
+        }
+         
+
+       private class Startup
+        {
+
+            // This method gets called by the runtime. Use this method to add services to the container.
+            public void ConfigureServices(IServiceCollection services)
+            {
+                var canResponses = new Response[]
+                {
+                    (Moods.Sad, "我不清楚你想做什麼 OvQ"),
+                    (Moods.Sad, "我不懂你想幹嘛 QAQ"),
+                    (Moods.Sad, "我不知道你想幹嘛 OHQ"),
+                };
+                services.AddSingleton(new Actor(new JiebaTextSegmenter(), canResponses, default, ModuleManager.Instance.Modules));
+
+                services.AddControllers();
+            }
+
+            // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
+            public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
+            {
+                if (env.IsDevelopment())
+                {
+                    app.UseDeveloperExceptionPage();
+                }
+
+                //app.UseHttpsRedirection();
+
+                app.UseRouting();
+
+                app.UseEndpoints(endpoints =>
+                {
+                    endpoints.MapControllers();
+                });
+            }
+        }
+
+    }
+}

+ 2 - 0
src/Yuuna/Yuuna.csproj

@@ -10,6 +10,7 @@
   </ItemGroup>
 
   <ItemGroup>
+    <Folder Include="Interaction\AspNetCore\Mvc\Views\" />
     <Folder Include="Properties\" />
   </ItemGroup>
 
@@ -17,6 +18,7 @@
     <PackageReference Include="Microsoft.AspNetCore" Version="2.2.0" />
     <PackageReference Include="Microsoft.AspNetCore.App" Version="2.2.8" />
     <PackageReference Include="Microsoft.NETCore.App" Version="2.2.8" />
+    <PackageReference Include="RestSharp" Version="106.10.0" />
   </ItemGroup>
 
   <ItemGroup>