Skip to content

Commit f431f6f

Browse files
committed
add FunASR model
1 parent 9be1fa2 commit f431f6f

14 files changed

Lines changed: 3179 additions & 138 deletions

.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,9 @@ bld/
3434
[Ll]ogs/
3535

3636
# Visual Studio 2015/2017 cache/options directory
37+
.cursor/
3738
.vs/
39+
.funasr_bundle_build/
3840
# Uncomment if you have tasks that create the project's static files in wwwroot
3941
#wwwroot/
4042

AddTaskWindow.xaml.cs

Lines changed: 63 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ public partial class AddTaskWindow : Window
1414
private bool _isClarificationRound = false; // State for clarification
1515
private string _originalTaskDescription = string.Empty; // To store original task if clarification is needed
1616
private bool _isLlmConfigErrorNotified = false; // Flag to track if user has been notified of LLM config error
17+
private readonly IntentRecognizer _intentRecognizer = new IntentRecognizer();
1718

1819
public string TaskDescription { get; private set; }
1920
public int SelectedListIndex { get; private set; } // 0-indexed
@@ -128,7 +129,7 @@ private void ResetClarificationButton_Click(object sender, RoutedEventArgs e)
128129

129130
private async void AddTaskButton_Click(object sender, RoutedEventArgs e)
130131
{
131-
string currentTaskDescription = TaskDescriptionTextBox.Text.Trim();
132+
string currentTaskDescription = NormalizeTaskText(TaskDescriptionTextBox.Text);
132133
SelectedListIndex = ListSelectorComboBox.SelectedIndex;
133134
string configErrorSubstring = "LLM dummy response (Configuration Error: API key missing or placeholder)";
134135

@@ -182,6 +183,7 @@ private async void AddTaskButton_Click(object sender, RoutedEventArgs e)
182183
// Prioritization & Task Creation (either directly or after clarification)
183184
TaskDescription = currentTaskDescription; // Final task description
184185
var (llmImportance, llmUrgency) = await _llmService.GetTaskPriorityAsync(TaskDescription);
186+
var (ruleImportance, ruleUrgency) = _intentRecognizer.EstimatePriority(TaskDescription);
185187

186188
// Check for LLM configuration error after priority analysis
187189
// string configErrorSubstring has been defined above
@@ -195,18 +197,20 @@ private async void AddTaskButton_Click(object sender, RoutedEventArgs e)
195197
}
196198

197199
// --- LLM Suggestion Logic ---
198-
int suggestedIndex = GetIndexFromPriority(llmImportance, llmUrgency);
200+
var (finalImportanceByAi, finalUrgencyByAi, sourceTag) = MergePriority(llmImportance, llmUrgency, ruleImportance, ruleUrgency);
201+
int suggestedIndex = GetIndexFromPriority(finalImportanceByAi, finalUrgencyByAi);
199202
ListSelectorComboBox.SelectedIndex = suggestedIndex;
200203

201204
if (suggestedIndex != -1 && ListSelectorComboBox.SelectedItem != null)
202205
{
203-
LlmSuggestionText.Text = $"LLM Suggests: {ListSelectorComboBox.SelectedItem as string}";
206+
string label = ListSelectorComboBox.SelectedItem as string;
207+
LlmSuggestionText.Text = $"AI建议象限({sourceTag}): {label}";
204208
LlmSuggestionText.Visibility = Visibility.Visible;
205209
}
206210
else
207211
{
208212
// Handle cases where suggestion is ambiguous or mapping fails
209-
LlmSuggestionText.Text = "LLM suggestion unavailable.";
213+
LlmSuggestionText.Text = "AI建议暂不可用,请手动选择象限。";
210214
LlmSuggestionText.Visibility = Visibility.Collapsed; // Or Visible with a different message
211215
}
212216
// --- End LLM Suggestion Logic ---
@@ -283,7 +287,7 @@ public static int GetIndexFromPriority(string importance, string urgency)
283287

284288
// Default or fallback for Medium/Unknown - could be -1 to indicate no selection
285289
// Or a specific category like "Important & Urgent"
286-
return 0; // Defaulting to "Important & Urgent" for now
290+
return 1; // 对未知结果偏向“重要不紧急”,降低默认紧急打扰
287291
}
288292

289293
// Helper method to map ComboBox index back to Importance/Urgency strings
@@ -312,5 +316,59 @@ private void CloseButton_Click(object sender, RoutedEventArgs e)
312316
this.DialogResult = false;
313317
this.Close();
314318
}
319+
320+
private static bool IsKnownPriority(string value)
321+
{
322+
if (string.IsNullOrWhiteSpace(value))
323+
return false;
324+
325+
string v = value.Trim().ToLowerInvariant();
326+
return v == "high" || v == "medium" || v == "low";
327+
}
328+
329+
private (string importance, string urgency, string sourceTag) MergePriority(
330+
string llmImportance,
331+
string llmUrgency,
332+
string ruleImportance,
333+
string ruleUrgency)
334+
{
335+
bool llmValid = IsKnownPriority(llmImportance) && IsKnownPriority(llmUrgency)
336+
&& !ContainsDummy(llmImportance)
337+
&& !ContainsDummy(llmUrgency);
338+
339+
if (llmValid)
340+
{
341+
return (llmImportance, llmUrgency, "LLM");
342+
}
343+
344+
bool ruleValid = IsKnownPriority(ruleImportance) && IsKnownPriority(ruleUrgency);
345+
if (ruleValid)
346+
{
347+
return (ruleImportance, ruleUrgency, "规则");
348+
}
349+
350+
return ("Medium", "Low", "默认");
351+
}
352+
353+
private static bool ContainsDummy(string value)
354+
{
355+
return !string.IsNullOrWhiteSpace(value) &&
356+
value.IndexOf("dummy response", StringComparison.OrdinalIgnoreCase) >= 0;
357+
}
358+
359+
private string NormalizeTaskText(string raw)
360+
{
361+
if (string.IsNullOrWhiteSpace(raw))
362+
return string.Empty;
363+
364+
string trimmed = raw.Trim();
365+
string extracted = _intentRecognizer.ExtractTaskDescription(trimmed);
366+
if (!string.IsNullOrWhiteSpace(extracted))
367+
{
368+
return extracted.Trim();
369+
}
370+
371+
return trimmed;
372+
}
315373
}
316374
} // Closing brace for namespace TimeTask

App.config

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,40 @@
2020
<add key="SpeechModelName" value="vosk-model-cn-0.22" />
2121
<!-- 可留空使用内置默认下载地址;或配置为你自己的模型 zip 地址 -->
2222
<add key="SpeechModelUrl" value="" />
23+
<!-- 默认 false:热词仅用于重排序,不强约束识别词表(可显著减少漏识别) -->
24+
<add key="VoiceUseStrictVoskGrammar" value="false" />
25+
<!-- System.Speech 回退时是否加载 hints grammar;默认关闭可避免部分中文引擎异常 -->
26+
<add key="VoiceSystemSpeechUseHints" value="false" />
27+
<!-- 语音识别提供方: funasr / hybrid -->
28+
<add key="VoiceAsrProvider" value="funasr" />
29+
<add key="FunAsrPythonExe" value="python" />
30+
<add key="FunAsrScriptPath" value="scripts\funasr_asr.py" />
31+
<add key="FunAsrModel" value="iic/SenseVoiceSmall" />
32+
<add key="FunAsrDevice" value="cpu" />
33+
<add key="FunAsrAutoBootstrap" value="true" />
34+
<add key="FunAsrBootstrapTimeoutSeconds" value="900" />
35+
<!-- FunASR 建议 Python 3.10/3.11/3.12;默认不使用 3.13+ -->
36+
<add key="FunAsrMaxPythonMinor" value="12" />
37+
<!-- 若当前 Python 不兼容,自动用 Conda 创建兼容 Python 环境 -->
38+
<add key="FunAsrAutoProvisionCondaPython" value="true" />
39+
<add key="FunAsrCondaPythonVersion" value="3.11" />
40+
<!-- 可选:手动指定 conda.exe 全路径 -->
41+
<add key="FunAsrCondaExe" value="" />
42+
<add key="FunAsrPipPackages" value="funasr modelscope torch torchaudio" />
43+
<!-- 预置运行时优先:推荐将可用 Python 环境打包为 zip,运行时解压即用 -->
44+
<add key="FunAsrPreferPrebuiltRuntime" value="true" />
45+
<add key="FunAsrRuntimeBundlePath" value="funasr-runtime-bundle.zip" />
46+
<!-- 默认关闭在线安装回退,避免复杂自动步骤 -->
47+
<add key="FunAsrAllowOnlineInstallFallback" value="false" />
48+
<add key="FunAsrInstallUseNoDepsStrategy" value="true" />
49+
<add key="FunAsrInstallRetryCooldownMinutes" value="30" />
50+
<add key="FunAsrRetryPollSeconds" value="60" />
51+
<add key="FunAsrHealthCacheHours" value="72" />
52+
<add key="FunAsrBaseDependencies" value="modelscope torch torchaudio" />
53+
<add key="FunAsrTimeoutSeconds" value="60" />
54+
<add key="FunAsrUsePersistentWorker" value="true" />
55+
<add key="FunAsrWorkerStartupTimeoutSeconds" value="600" />
56+
<add key="FunAsrMinSegmentSeconds" value="0.5" />
2357
<!-- 语音草稿自动加入四象限 -->
2458
<add key="VoiceAutoAddToQuadrant" value="false" />
2559
<add key="VoiceAutoAddMinConfidence" value="0.65" />

App.xaml.cs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,9 @@ protected override void OnStartup(StartupEventArgs e)
3030
VoiceRuntimeLog.Info("App startup.");
3131
VoiceRuntimeLog.Info($"ProcessBitness: {(Environment.Is64BitProcess ? "x64" : "x86")}, OS: {(Environment.Is64BitOperatingSystem ? "x64" : "x86")}");
3232
VoiceRuntimeLog.Info($"BaseDirectory: {AppDomain.CurrentDomain.BaseDirectory}");
33+
VoiceRuntimeLog.Info($"Config: VoiceAsrProvider={ConfigurationManager.AppSettings["VoiceAsrProvider"]}, FunAsrAutoBootstrap={ConfigurationManager.AppSettings["FunAsrAutoBootstrap"]}");
34+
VoiceListenerStatusCenter.Publish(VoiceListenerState.Unavailable, "语音监听不可用(启动初始化中)");
35+
FunAsrRuntimeManager.KickoffIfNeeded();
3336

3437
// Check for API Key configuration
3538
string apiKey = System.Configuration.ConfigurationManager.AppSettings["OpenAIApiKey"];
@@ -90,11 +93,13 @@ protected override void OnStartup(StartupEventArgs e)
9093
_legacyAudioService = new AudioCaptureService();
9194
_legacyAudioService.Start();
9295
VoiceRuntimeLog.Info("Legacy AudioCaptureService started as fallback.");
96+
VoiceListenerStatusCenter.Publish(VoiceListenerState.Ready, "已回退到系统语音引擎,监听可用");
9397
}
9498
catch (Exception ex2)
9599
{
96100
Console.WriteLine($"[App] Legacy AudioCaptureService also failed: {ex2.Message}");
97101
VoiceRuntimeLog.Error("Legacy AudioCaptureService start failed.", ex2);
102+
VoiceListenerStatusCenter.Publish(VoiceListenerState.Unavailable, "语音监听不可用");
98103
MessageBox.Show(
99104
"语音识别初始化失败,系统未检测到可用语音识别引擎或麦克风权限异常。\n" +
100105
$"请查看日志:{VoiceRuntimeLog.LogFilePath}",

0 commit comments

Comments
 (0)