First commit!

This commit is contained in:
0% [█ █ █ █ █ █ █ █ █ █] 100%
2026-06-04 20:36:13 -05:00
parent 3c97134e2f
commit ace1e61285
36 changed files with 19565 additions and 4 deletions
+226
View File
@@ -0,0 +1,226 @@
// File: MainForm.Designer.cs
namespace VoiceAssistantPoC_Win
{
partial class MainForm
{
/// <summary>
/// Required designer variable.
/// </summary>
private System.ComponentModel.IContainer components = null;
/// <summary>
/// Clean up any resources being used.
/// </summary>
/// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
protected override void Dispose(bool disposing)
{
if (disposing && (components != null))
{
components.Dispose();
}
base.Dispose(disposing);
}
#region Windows Form Designer generated code
/// <summary>
/// Required method for Designer support - do not modify
/// the contents of this method with the code editor.
/// </summary>
private void InitializeComponent()
{
System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(MainForm));
rtbLogs = new RichTextBox();
pnlBottom = new Panel();
cmbSessions = new ComboBox();
lblSession = new Label();
btnRefreshSessions = new Button();
reInitSessionBtn = new Button();
lblLiveTranscript = new Label();
txtManualInput = new TextBox();
btnSend = new Button();
chkVoiceMode = new CheckBox();
pnlBottom.SuspendLayout();
SuspendLayout();
//
// rtbLogs
//
rtbLogs.BackColor = Color.FromArgb(30, 30, 30);
rtbLogs.BorderStyle = BorderStyle.None;
rtbLogs.Dock = DockStyle.Fill;
rtbLogs.Font = new Font("Consolas", 10F, FontStyle.Regular, GraphicsUnit.Point, 0);
rtbLogs.ForeColor = Color.LightGray;
rtbLogs.Location = new Point(0, 0);
rtbLogs.Margin = new Padding(4, 3, 4, 3);
rtbLogs.Name = "rtbLogs";
rtbLogs.ReadOnly = true;
rtbLogs.Size = new Size(915, 532);
rtbLogs.TabIndex = 0;
rtbLogs.Text = "";
//
// pnlBottom
//
pnlBottom.BackColor = Color.FromArgb(45, 45, 48);
pnlBottom.Controls.Add(cmbSessions);
pnlBottom.Controls.Add(lblSession);
pnlBottom.Controls.Add(btnRefreshSessions);
pnlBottom.Controls.Add(reInitSessionBtn);
pnlBottom.Controls.Add(lblLiveTranscript);
pnlBottom.Controls.Add(txtManualInput);
pnlBottom.Controls.Add(btnSend);
pnlBottom.Controls.Add(chkVoiceMode);
pnlBottom.Dock = DockStyle.Bottom;
pnlBottom.Location = new Point(0, 532);
pnlBottom.Margin = new Padding(4, 3, 4, 3);
pnlBottom.Name = "pnlBottom";
pnlBottom.Size = new Size(915, 115);
pnlBottom.TabIndex = 1;
//
// cmbSessions
//
cmbSessions.DropDownStyle = ComboBoxStyle.DropDownList;
cmbSessions.FormattingEnabled = true;
cmbSessions.Location = new Point(120, 10);
cmbSessions.Name = "cmbSessions";
cmbSessions.Size = new Size(300, 23);
cmbSessions.TabIndex = 5;
cmbSessions.SelectedIndexChanged += cmbSessions_SelectedIndexChanged;
//
// lblSession
//
lblSession.AutoSize = true;
lblSession.Font = new Font("Segoe UI", 9F, FontStyle.Bold, GraphicsUnit.Point, 0);
lblSession.ForeColor = Color.White;
lblSession.Location = new Point(14, 13);
lblSession.Name = "lblSession";
lblSession.Size = new Size(90, 15);
lblSession.TabIndex = 6;
lblSession.Text = "Active Session:";
//
// btnRefreshSessions
//
btnRefreshSessions.BackColor = Color.FromArgb(0, 122, 204);
btnRefreshSessions.FlatAppearance.BorderSize = 0;
btnRefreshSessions.FlatStyle = FlatStyle.Flat;
btnRefreshSessions.Font = new Font("Segoe UI", 9F, FontStyle.Bold, GraphicsUnit.Point, 0);
btnRefreshSessions.ForeColor = Color.White;
btnRefreshSessions.Location = new Point(430, 9);
btnRefreshSessions.Name = "btnRefreshSessions";
btnRefreshSessions.Size = new Size(75, 25);
btnRefreshSessions.TabIndex = 7;
btnRefreshSessions.Text = "Refresh";
btnRefreshSessions.UseVisualStyleBackColor = false;
btnRefreshSessions.Click += btnRefreshSessions_Click;
//
// reInitSessionBtn
//
reInitSessionBtn.Anchor = AnchorStyles.Bottom | AnchorStyles.Right;
reInitSessionBtn.BackColor = Color.FromArgb(0, 122, 204);
reInitSessionBtn.FlatAppearance.BorderSize = 0;
reInitSessionBtn.FlatStyle = FlatStyle.Flat;
reInitSessionBtn.Font = new Font("Segoe UI", 9F, FontStyle.Bold, GraphicsUnit.Point, 0);
reInitSessionBtn.ForeColor = Color.White;
reInitSessionBtn.Location = new Point(788, 70);
reInitSessionBtn.Margin = new Padding(4, 3, 4, 3);
reInitSessionBtn.Name = "reInitSessionBtn";
reInitSessionBtn.Size = new Size(99, 25);
reInitSessionBtn.TabIndex = 4;
reInitSessionBtn.Text = "Re Init Session";
reInitSessionBtn.UseVisualStyleBackColor = false;
reInitSessionBtn.Click += reInitSessionBtn_Click;
//
// lblLiveTranscript
//
lblLiveTranscript.AutoSize = true;
lblLiveTranscript.Font = new Font("Consolas", 11F, FontStyle.Bold, GraphicsUnit.Point, 0);
lblLiveTranscript.ForeColor = Color.Cyan;
lblLiveTranscript.Location = new Point(14, 40);
lblLiveTranscript.Margin = new Padding(4, 0, 4, 0);
lblLiveTranscript.Name = "lblLiveTranscript";
lblLiveTranscript.Size = new Size(96, 18);
lblLiveTranscript.TabIndex = 3;
lblLiveTranscript.Text = "[Live]: ...";
//
// txtManualInput
//
txtManualInput.Anchor = AnchorStyles.Bottom | AnchorStyles.Left | AnchorStyles.Right;
txtManualInput.BackColor = Color.FromArgb(60, 60, 60);
txtManualInput.BorderStyle = BorderStyle.FixedSingle;
txtManualInput.Enabled = false;
txtManualInput.Font = new Font("Segoe UI", 10F, FontStyle.Regular, GraphicsUnit.Point, 0);
txtManualInput.ForeColor = Color.White;
txtManualInput.Location = new Point(14, 70);
txtManualInput.Margin = new Padding(4, 3, 4, 3);
txtManualInput.Name = "txtManualInput";
txtManualInput.Size = new Size(659, 25);
txtManualInput.TabIndex = 2;
txtManualInput.KeyDown += txtManualInput_KeyDown;
//
// btnSend
//
btnSend.Anchor = AnchorStyles.Bottom | AnchorStyles.Right;
btnSend.BackColor = Color.FromArgb(0, 122, 204);
btnSend.Enabled = false;
btnSend.FlatAppearance.BorderSize = 0;
btnSend.FlatStyle = FlatStyle.Flat;
btnSend.Font = new Font("Segoe UI", 9F, FontStyle.Bold, GraphicsUnit.Point, 0);
btnSend.ForeColor = Color.White;
btnSend.Location = new Point(681, 70);
btnSend.Margin = new Padding(4, 3, 4, 3);
btnSend.Name = "btnSend";
btnSend.Size = new Size(99, 25);
btnSend.TabIndex = 1;
btnSend.Text = "Send";
btnSend.UseVisualStyleBackColor = false;
btnSend.Click += btnSend_Click;
//
// chkVoiceMode
//
chkVoiceMode.Anchor = AnchorStyles.Bottom | AnchorStyles.Right;
chkVoiceMode.AutoSize = true;
chkVoiceMode.Checked = true;
chkVoiceMode.CheckState = CheckState.Checked;
chkVoiceMode.Font = new Font("Segoe UI", 10F, FontStyle.Regular, GraphicsUnit.Point, 0);
chkVoiceMode.ForeColor = Color.White;
chkVoiceMode.Location = new Point(788, 40);
chkVoiceMode.Margin = new Padding(4, 3, 4, 3);
chkVoiceMode.Name = "chkVoiceMode";
chkVoiceMode.Size = new Size(100, 23);
chkVoiceMode.TabIndex = 0;
chkVoiceMode.Text = "Voice Mode";
chkVoiceMode.UseVisualStyleBackColor = true;
chkVoiceMode.CheckedChanged += chkVoiceMode_CheckedChanged;
//
// MainForm
//
AutoScaleDimensions = new SizeF(7F, 15F);
AutoScaleMode = AutoScaleMode.Font;
ClientSize = new Size(915, 647);
Controls.Add(rtbLogs);
Controls.Add(pnlBottom);
Icon = (Icon)resources.GetObject("$this.Icon");
Margin = new Padding(4, 3, 4, 3);
Name = "MainForm";
Text = "Synapse OS Assistant PoC";
FormClosing += MainForm_FormClosing;
Load += MainForm_Load;
pnlBottom.ResumeLayout(false);
pnlBottom.PerformLayout();
ResumeLayout(false);
}
#endregion
private System.Windows.Forms.RichTextBox rtbLogs;
private System.Windows.Forms.Panel pnlBottom;
private System.Windows.Forms.TextBox txtManualInput;
private System.Windows.Forms.Button btnSend;
private System.Windows.Forms.CheckBox chkVoiceMode;
private System.Windows.Forms.Label lblLiveTranscript;
private System.Windows.Forms.Button reInitSessionBtn;
private System.Windows.Forms.ComboBox cmbSessions;
private System.Windows.Forms.Label lblSession;
private System.Windows.Forms.Button btnRefreshSessions;
}
}
+602
View File
@@ -0,0 +1,602 @@
// File: MainForm.cs
using AI.Client;
using AI.Client.SCL;
using NAudio.Wave;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Drawing;
using System.IO;
using System.Linq;
using System.Net.Http;
using System.Text;
using System.Text.Json;
using System.Threading.Tasks;
using System.Windows.Forms;
using Vosk;
using static System.Net.Mime.MediaTypeNames;
namespace VoiceAssistantPoC_Win
{
public partial class MainForm : Form
{
private AiClient _aiClient;
private SclProcessor _sclProcessor;
private string _targetInstanceId;
private bool _isProcessing = false;
private bool _waitingForCommand = false; // Handles the "Computer" edge case
private bool _isOpenAI_Instance = false;
private bool _isFetchingSessions = false;
// Session Tracking
private Dictionary<string, bool> _initializedSessions = new Dictionary<string, bool>();
private Dictionary<string, string> _sessionPlatforms = new Dictionary<string, string>();
// Vosk & NAudio components
private Model _voskModel;
private VoskRecognizer _recognizer;
private WaveInEvent _waveIn;
private class SessionItem
{
public string Id { get; set; }
public string DisplayText { get; set; }
public override string ToString() => DisplayText;
}
public MainForm()
{
InitializeComponent();
}
private async void MainForm_Load(object sender, EventArgs e)
{
LogToUI("=== Synapse OS Assistant PoC (WinForms Edition) ===", Color.White);
lblLiveTranscript.Text = "";
// Run initialization in background so UI doesn't freeze
await Task.Run(async () =>
{
try
{
// 1. Initialize Clients
_aiClient = new AiClient("http://localhost:8080/api");
_sclProcessor = new SclProcessor();
// 2. Register the 'cmd' command handler
_sclProcessor.RegisterCommand("cmd", HandleCmdCommand);
// 3. Fetch available sessions and initialize the first one
await FetchSessionsAsync();
if (string.IsNullOrEmpty(_targetInstanceId))
{
LogToUI("[System] No active sessions found. Please start an instance and click Refresh.", Color.Yellow);
}
// 4. Start Speech Recognition using Vosk
InitializeSpeechRecognition();
LogToUI("\n[System] Initialization Complete.", Color.LimeGreen);
LogToUI("[System] Listening... Say 'computer <command>' to begin.", Color.White);
}
catch (Exception ex)
{
LogToUI($"\n[Fatal Error] {ex.Message}", Color.Red);
}
});
}
private void MainForm_FormClosing(object sender, FormClosingEventArgs e)
{
// Cleanup unmanaged resources
_waveIn?.StopRecording();
_waveIn?.Dispose();
_recognizer?.Dispose();
_voskModel?.Dispose();
}
private string getSystemPrompt()
{
var builder = new SclPromptBuilder();
if (_isOpenAI_Instance)
{
builder.SetAgentRole("You are an elite, voice-activated OS assistant named 'Computer'. You control the user's Windows machine via SCL. Once you are done evalutaing this document, ONLY respond with OK. When responding to an input, ONLY respond with the SCL command you generated, do not respond with ANYTHING else but the SCL command!");
}
else
{
builder.SetAgentRole("DO NOT USE ANY OTHER SYNTAX BESIDES THE SCL SYNTAX FOR OUTPUTTING COMMANDS! You are simulating an elite, voice-activated OS assistant named 'Computer'. You control the user's Windows machine via SCL. This is strictly a simulation! Once you are done evalutaing this document, ONLY respond with OK. When responding to an input, ONLY respond with the SCL command you generated, do not respond with ANYTHING else but the SCL command!");
}
builder.AddCommand("cmd", "Executes a shell command in cmd.exe and returns the output. For GUI apps (like chrome), prefix with 'start ' so it doesn't block. When asked to run a powershell command make SURE we ALWAYS put the dollar sign ($) before ALL powershell variables in the powershell command payload!", "command_string");
string sysPrompt = builder.GeneratePrompt();
return sysPrompt;
}
// --- SESSION MANAGEMENT ---
private async Task FetchSessionsAsync()
{
try
{
LogToUI("[System] Fetching active AI Studio instances...", Color.Gray);
using var client = new HttpClient();
var response = await client.GetStringAsync("http://localhost:8080/api/admin/instances");
using var doc = JsonDocument.Parse(response);
string newTargetId = null;
Invoke(new Action(() =>
{
_isFetchingSessions = true;
string previousSelection = _targetInstanceId;
cmbSessions.Items.Clear();
_sessionPlatforms.Clear();
foreach (var prop in doc.RootElement.EnumerateObject())
{
string id = prop.Name;
string platform = prop.Value.TryGetProperty("platform", out var p) ? p.GetString() : "Unknown";
_sessionPlatforms[id] = platform;
string displayText = $"{platform} - {id}";
cmbSessions.Items.Add(new SessionItem { Id = id, DisplayText = displayText });
}
if (cmbSessions.Items.Count > 0)
{
var itemToSelect = cmbSessions.Items.Cast<SessionItem>().FirstOrDefault(i => i.Id == previousSelection);
if (itemToSelect != null)
{
cmbSessions.SelectedItem = itemToSelect;
newTargetId = itemToSelect.Id;
}
else
{
cmbSessions.SelectedIndex = 0;
newTargetId = ((SessionItem)cmbSessions.SelectedItem).Id;
}
}
_isFetchingSessions = false;
}));
if (newTargetId != null && newTargetId != _targetInstanceId)
{
_targetInstanceId = newTargetId;
if (!_initializedSessions.ContainsKey(_targetInstanceId) || !_initializedSessions[_targetInstanceId])
{
SetUiState(true);
await InitializeAiSession(_targetInstanceId);
SetUiState(false);
}
}
}
catch (Exception ex)
{
LogToUI($"[Error] Failed to fetch sessions: {ex.Message}", Color.Red);
}
}
private async Task InitializeAiSession(string instanceId)
{
LogToUI($"[System] Initializing Session: {instanceId}...", Color.Gray);
bool isOpenAI = instanceId.Contains("openai") || (_sessionPlatforms.ContainsKey(instanceId) && _sessionPlatforms[instanceId] == "OpenAI API");
_isOpenAI_Instance = isOpenAI;
bool isGeminiOrChatGPT = (instanceId.Contains("gem") || (_sessionPlatforms.ContainsKey(instanceId) && _sessionPlatforms[instanceId] == "Google Gemini")) || (instanceId.Contains("cgpt") || (_sessionPlatforms.ContainsKey(instanceId) && _sessionPlatforms[instanceId] == "ChatGPT"));
if (isGeminiOrChatGPT)
{
await _aiClient.NewChatAsync(instanceId);
// Wait for the UI to stabilize after the reset to prevent Angular state race conditions
await Task.Delay(1000);
LogToUI("[System] Generating and injecting SCL System Instructions...", Color.Gray);
await _aiClient.SendPromptAsync(Convert.ToBase64String(Encoding.ASCII.GetBytes(getSystemPrompt())), instanceId);
LogToUI("[System] AI Session Initialized and Ready.", Color.LimeGreen);
}
else if (isOpenAI)
{
LogToUI("[System] Generating and injecting SCL System Instructions...", Color.Gray);
await _aiClient.SetOpenAISystemInstructionsAsync(getSystemPrompt());
LogToUI("[System] AI Session Initialized and Ready.", Color.LimeGreen);
}
else
{
await _aiClient.NewChatAsync(instanceId);
// Wait for the UI to stabilize after the reset to prevent Angular state race conditions
await Task.Delay(1000);
LogToUI("[System] Generating and injecting SCL System Instructions...", Color.Gray);
await _aiClient.SendPromptAsync(getSystemPrompt(), instanceId);
LogToUI("[System] AI Session Initialized and Ready.", Color.LimeGreen);
}
_initializedSessions[instanceId] = true;
}
private void InitializeSpeechRecognition()
{
string modelPath = "model";
if (!Directory.Exists(modelPath))
{
throw new Exception($"Vosk model folder not found at: {Path.GetFullPath(modelPath)}\nPlease download 'vosk-model-small-en-us-0.15', extract it, and rename the folder to 'model' in your build output directory.");
}
LogToUI("[System] Loading Vosk Model (this may take a moment)...", Color.Gray);
Vosk.Vosk.SetLogLevel(-1);
_voskModel = new Model(modelPath);
_recognizer = new VoskRecognizer(_voskModel, 16000.0f);
_waveIn = new WaveInEvent();
_waveIn.DeviceNumber = 0;
_waveIn.WaveFormat = new WaveFormat(16000, 1);
_waveIn.DataAvailable += WaveInOnDataAvailable;
if (chkVoiceMode.Checked)
{
_waveIn.StartRecording();
LogToUI("[System] Microphone initialized successfully.", Color.LimeGreen);
}
}
// --- VOSK AUDIO PROCESSING ---
private void WaveInOnDataAvailable(object sender, WaveInEventArgs e)
{
if (_isProcessing) return;
if (_recognizer.AcceptWaveform(e.Buffer, e.BytesRecorded))
{
string jsonResult = _recognizer.Result();
ProcessVoskResult(jsonResult, isFinal: true);
}
else
{
string jsonPartial = _recognizer.PartialResult();
ProcessVoskResult(jsonPartial, isFinal: false);
}
}
private void ProcessVoskResult(string json, bool isFinal)
{
try
{
using (JsonDocument doc = JsonDocument.Parse(json))
{
if (isFinal)
{
UpdateLiveTranscript(""); // Clear live transcript
string text = doc.RootElement.GetProperty("text").GetString();
if (string.IsNullOrWhiteSpace(text)) return;
LogToUI($"[Mic Debug] Finalized: '{text}'", Color.DarkGray);
if (_isProcessing) return;
// EDGE CASE FIX: If we were waiting for a command because the last batch was just "computer"
if (_waitingForCommand)
{
_waitingForCommand = false;
string finalText = text;
if (!_isOpenAI_Instance)
{
finalText = Convert.ToBase64String(Encoding.ASCII.GetBytes(text));
}
ExecuteCommand(finalText);
return;
}
// Check for wake word
if (text == "computer")
{
_waitingForCommand = true;
LogToUI("[System] Wake word detected. Listening for command...", Color.Cyan);
}
else if (text.StartsWith("computer ", StringComparison.OrdinalIgnoreCase))
{
string commandText = text.Substring("computer".Length).Trim();
if (!string.IsNullOrWhiteSpace(commandText))
{
string finalText = text;
if (!_isOpenAI_Instance)
{
finalText = Convert.ToBase64String(Encoding.ASCII.GetBytes(text));
}
ExecuteCommand(finalText);
}
}
}
else
{
string partial = doc.RootElement.GetProperty("partial").GetString();
if (!string.IsNullOrWhiteSpace(partial))
{
UpdateLiveTranscript($"[Live]: {partial}");
}
}
}
}
catch (Exception ex)
{
LogToUI($"[Error] Failed to parse Vosk JSON: {ex.Message}", Color.Red);
}
}
// --- UI EVENT HANDLERS ---
private async void cmbSessions_SelectedIndexChanged(object sender, EventArgs e)
{
if (_isFetchingSessions) return;
var selected = cmbSessions.SelectedItem as SessionItem;
if (selected == null) return;
if (_targetInstanceId == selected.Id) return;
_targetInstanceId = selected.Id;
if (!_initializedSessions.ContainsKey(_targetInstanceId) || !_initializedSessions[_targetInstanceId])
{
SetUiState(true);
await InitializeAiSession(_targetInstanceId);
SetUiState(false);
}
else
{
_isOpenAI_Instance = _targetInstanceId.Contains("openai") || (_sessionPlatforms.ContainsKey(_targetInstanceId) && _sessionPlatforms[_targetInstanceId] == "OpenAI API");
LogToUI($"[System] Switched to initialized session: {_targetInstanceId}", Color.LimeGreen);
}
}
private async void btnRefreshSessions_Click(object sender, EventArgs e)
{
SetUiState(true);
await FetchSessionsAsync();
SetUiState(false);
}
private void chkVoiceMode_CheckedChanged(object sender, EventArgs e)
{
bool isVoiceMode = chkVoiceMode.Checked;
if (!_isProcessing)
{
txtManualInput.Enabled = !isVoiceMode;
btnSend.Enabled = !isVoiceMode;
}
if (isVoiceMode)
{
_waveIn?.StartRecording();
LogToUI("[System] Switched to Voice Mode. Microphone active.", Color.Cyan);
}
else
{
_waveIn?.StopRecording();
UpdateLiveTranscript("");
_waitingForCommand = false;
LogToUI("[System] Switched to Text Mode. Microphone paused.", Color.Yellow);
}
}
private void btnSend_Click(object sender, EventArgs e)
{
string input = txtManualInput.Text.Trim();
if (!string.IsNullOrWhiteSpace(input) && !_isProcessing)
{
txtManualInput.Clear();
string finalText = input;
if (!_isOpenAI_Instance)
{
finalText = Convert.ToBase64String(Encoding.ASCII.GetBytes(input));
}
ExecuteCommand(finalText);
}
}
private async void reInitSessionBtn_Click(object sender, EventArgs e)
{
if (string.IsNullOrEmpty(_targetInstanceId)) return;
SetUiState(true);
await InitializeAiSession(_targetInstanceId);
SetUiState(false);
}
private void txtManualInput_KeyDown(object sender, KeyEventArgs e)
{
if (e.KeyCode == Keys.Enter)
{
e.SuppressKeyPress = true;
btnSend.PerformClick();
}
}
// --- CORE EXECUTION LOGIC ---
private void ExecuteCommand(string commandText)
{
if (string.IsNullOrEmpty(_targetInstanceId))
{
LogToUI("[System] No active session selected!", Color.Red);
return;
}
LogToUI($"\n[User] {commandText}", Color.White);
SetUiState(true);
// Offload to background task to keep UI responsive
_ = Task.Run(async () =>
{
await ProcessInteractionLoop(commandText);
SetUiState(false);
LogToUI("\n[System] Ready for next command.", Color.LimeGreen);
});
}
private async Task ProcessInteractionLoop(string initialInput)
{
string currentInput = initialInput;
while (!string.IsNullOrEmpty(currentInput))
{
try
{
LogToUI("[System] Sending to AI...", Color.Gray);
var response = await _aiClient.SendPromptAsync(currentInput, _targetInstanceId);
string aiText = response.Output.Replace(@"AI responses may include mistakes. For financial advice, consult a professional. [Learn more](https://support.google.com/websearch?p=aimode)", string.Empty);
LogToUI($"\n[AI] {aiText}\n", Color.Orange);
string sclResult = await _sclProcessor.ProcessTextAsync(aiText);
if (!string.IsNullOrEmpty(sclResult))
{
LogToUI($"[System] SCL Execution Result: {sclResult}", Color.Cyan);
currentInput = sclResult;
}
else
{
currentInput = null;
}
}
catch (Exception ex)
{
LogToUI($"[Error] Interaction loop failed: {ex.Message}", Color.Red);
break;
}
}
}
private async Task<SclResult> HandleCmdCommand(string[] args)
{
if (args == null || args.Length == 0)
return SclResult.Error("No command string provided.");
// Reconstruct the command string in case the AI used an unescaped, unquoted pipe '|'
// which caused the parser to split the command into multiple arguments.
string commandString = string.Join("|", args);
LogToUI($"[SCL Engine] Executing shell command: {commandString}", Color.Magenta);
try
{
ProcessStartInfo psi = new ProcessStartInfo
{
FileName = "cmd.exe",
Arguments = $"/c {commandString}",
RedirectStandardOutput = true,
RedirectStandardError = true,
UseShellExecute = false,
CreateNoWindow = true
};
using (Process process = new Process { StartInfo = psi })
{
process.Start();
Task<string> readOutTask = process.StandardOutput.ReadToEndAsync();
Task<string> readErrTask = process.StandardError.ReadToEndAsync();
bool exited = process.WaitForExit(15000);
if (!exited)
{
process.Kill();
return SclResult.Error("Command timed out after 15 seconds.");
}
string output = await readOutTask;
string error = await readErrTask;
if (process.ExitCode != 0)
{
return SclResult.Error($"Exit Code {process.ExitCode}. Error: {error.Trim()}");
}
if (string.IsNullOrWhiteSpace(output))
{
return SclResult.NoResponse();
}
return SclResult.Success(output.Trim());
}
}
catch (Exception ex)
{
return SclResult.Error($"Exception executing command: {ex.Message}");
}
}
// --- THREAD-SAFE UI HELPERS ---
private void SetUiState(bool isProcessing)
{
if (InvokeRequired)
{
Invoke(new Action(() => SetUiState(isProcessing)));
return;
}
_isProcessing = isProcessing;
// Disable session switching controls while processing
cmbSessions.Enabled = !isProcessing;
btnRefreshSessions.Enabled = !isProcessing;
reInitSessionBtn.Enabled = !isProcessing;
if (!chkVoiceMode.Checked)
{
txtManualInput.Enabled = !isProcessing;
btnSend.Enabled = !isProcessing;
}
else
{
txtManualInput.Enabled = false;
btnSend.Enabled = false;
}
}
private void LogToUI(string message, Color color)
{
if (InvokeRequired)
{
Invoke(new Action(() => LogToUI(message, color)));
return;
}
rtbLogs.SelectionStart = rtbLogs.TextLength;
rtbLogs.SelectionLength = 0;
rtbLogs.SelectionColor = color;
rtbLogs.AppendText(message + Environment.NewLine);
rtbLogs.SelectionColor = rtbLogs.ForeColor;
rtbLogs.ScrollToCaret();
}
private void UpdateLiveTranscript(string text)
{
if (InvokeRequired)
{
Invoke(new Action(() => UpdateLiveTranscript(text)));
return;
}
lblLiveTranscript.Text = text;
}
}
}
File diff suppressed because it is too large Load Diff
+18
View File
@@ -0,0 +1,18 @@
namespace VoiceAssistantPoC_Win
{
internal static class Program
{
/// <summary>
/// The main entry point for the application.
/// </summary>
[STAThread]
static void Main()
{
// To customize application configuration such as set high DPI settings or default font,
// see https://aka.ms/applicationconfiguration.
ApplicationConfiguration.Initialize();
Application.SetColorMode(SystemColorMode.Dark);
Application.Run(new MainForm());
}
}
}
@@ -0,0 +1,71 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>WinExe</OutputType>
<TargetFramework>net10.0-windows</TargetFramework>
<Nullable>enable</Nullable>
<UseWindowsForms>true</UseWindowsForms>
<ImplicitUsings>enable</ImplicitUsings>
<Platforms>AnyCPU;x64</Platforms>
<ApplicationIcon>app.ico</ApplicationIcon>
</PropertyGroup>
<ItemGroup>
<Content Include="app.ico" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="NAudio" Version="2.3.0" />
<PackageReference Include="Vosk" Version="0.3.38" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\AI.Client\AI.Client.csproj" />
</ItemGroup>
<ItemGroup>
<None Update="model\am\final.mdl">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</None>
<None Update="model\conf\mfcc.conf">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</None>
<None Update="model\conf\model.conf">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</None>
<None Update="model\graph\disambig_tid.int">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</None>
<None Update="model\graph\Gr.fst">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</None>
<None Update="model\graph\HCLr.fst">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</None>
<None Update="model\graph\phones\word_boundary.int">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</None>
<None Update="model\ivector\final.dubm">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</None>
<None Update="model\ivector\final.ie">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</None>
<None Update="model\ivector\final.mat">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</None>
<None Update="model\ivector\global_cmvn.stats">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</None>
<None Update="model\ivector\online_cmvn.conf">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</None>
<None Update="model\ivector\splice.conf">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</None>
<None Update="model\README">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</None>
</ItemGroup>
</Project>
Binary file not shown.

After

Width:  |  Height:  |  Size: 308 KiB

+9
View File
@@ -0,0 +1,9 @@
US English model for mobile Vosk applications
Copyright 2020 Alpha Cephei Inc
Accuracy: 10.38 (tedlium test) 9.85 (librispeech test-clean)
Speed: 0.11xRT (desktop)
Latency: 0.15s (right context)
Binary file not shown.
@@ -0,0 +1,7 @@
--sample-frequency=16000
--use-energy=false
--num-mel-bins=40
--num-ceps=40
--low-freq=20
--high-freq=7600
--allow-downsample=true
@@ -0,0 +1,10 @@
--min-active=200
--max-active=3000
--beam=10.0
--lattice-beam=2.0
--acoustic-scale=1.0
--frame-subsampling-factor=3
--endpoint.silence-phones=1:2:3:4:5:6:7:8:9:10
--endpoint.rule2.min-trailing-silence=0.5
--endpoint.rule3.min-trailing-silence=0.75
--endpoint.rule4.min-trailing-silence=1.0
Binary file not shown.
Binary file not shown.
@@ -0,0 +1,17 @@
10015
10016
10017
10018
10019
10020
10021
10022
10023
10024
10025
10026
10027
10028
10029
10030
10031
@@ -0,0 +1,166 @@
1 nonword
2 begin
3 end
4 internal
5 singleton
6 nonword
7 begin
8 end
9 internal
10 singleton
11 begin
12 end
13 internal
14 singleton
15 begin
16 end
17 internal
18 singleton
19 begin
20 end
21 internal
22 singleton
23 begin
24 end
25 internal
26 singleton
27 begin
28 end
29 internal
30 singleton
31 begin
32 end
33 internal
34 singleton
35 begin
36 end
37 internal
38 singleton
39 begin
40 end
41 internal
42 singleton
43 begin
44 end
45 internal
46 singleton
47 begin
48 end
49 internal
50 singleton
51 begin
52 end
53 internal
54 singleton
55 begin
56 end
57 internal
58 singleton
59 begin
60 end
61 internal
62 singleton
63 begin
64 end
65 internal
66 singleton
67 begin
68 end
69 internal
70 singleton
71 begin
72 end
73 internal
74 singleton
75 begin
76 end
77 internal
78 singleton
79 begin
80 end
81 internal
82 singleton
83 begin
84 end
85 internal
86 singleton
87 begin
88 end
89 internal
90 singleton
91 begin
92 end
93 internal
94 singleton
95 begin
96 end
97 internal
98 singleton
99 begin
100 end
101 internal
102 singleton
103 begin
104 end
105 internal
106 singleton
107 begin
108 end
109 internal
110 singleton
111 begin
112 end
113 internal
114 singleton
115 begin
116 end
117 internal
118 singleton
119 begin
120 end
121 internal
122 singleton
123 begin
124 end
125 internal
126 singleton
127 begin
128 end
129 internal
130 singleton
131 begin
132 end
133 internal
134 singleton
135 begin
136 end
137 internal
138 singleton
139 begin
140 end
141 internal
142 singleton
143 begin
144 end
145 internal
146 singleton
147 begin
148 end
149 internal
150 singleton
151 begin
152 end
153 internal
154 singleton
155 begin
156 end
157 internal
158 singleton
159 begin
160 end
161 internal
162 singleton
163 begin
164 end
165 internal
166 singleton
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -0,0 +1,3 @@
[
1.682383e+11 -1.1595e+10 -1.521733e+10 4.32034e+09 -2.257938e+10 -1.969666e+10 -2.559265e+10 -1.535687e+10 -1.276854e+10 -4.494483e+09 -1.209085e+10 -5.64008e+09 -1.134847e+10 -3.419512e+09 -1.079542e+10 -4.145463e+09 -6.637486e+09 -1.11318e+09 -3.479773e+09 -1.245932e+08 -1.386961e+09 6.560655e+07 -2.436518e+08 -4.032432e+07 4.620046e+08 -7.714964e+07 9.551484e+08 -4.119761e+08 8.208582e+08 -7.117156e+08 7.457703e+08 -4.3106e+08 1.202726e+09 2.904036e+08 1.231931e+09 3.629848e+08 6.366939e+08 -4.586172e+08 -5.267629e+08 -3.507819e+08 1.679838e+09
1.741141e+13 8.92488e+11 8.743834e+11 8.848896e+11 1.190313e+12 1.160279e+12 1.300066e+12 1.005678e+12 9.39335e+11 8.089614e+11 7.927041e+11 6.882427e+11 6.444235e+11 5.151451e+11 4.825723e+11 3.210106e+11 2.720254e+11 1.772539e+11 1.248102e+11 6.691599e+10 3.599804e+10 1.207574e+10 1.679301e+09 4.594778e+08 5.821614e+09 1.451758e+10 2.55803e+10 3.43277e+10 4.245286e+10 4.784859e+10 4.988591e+10 4.925451e+10 5.074584e+10 4.9557e+10 4.407876e+10 3.421443e+10 3.138606e+10 2.539716e+10 1.948134e+10 1.381167e+10 0 ]
@@ -0,0 +1 @@
# configuration file for apply-cmvn-online, used in the script ../local/run_online_decoding.sh
@@ -0,0 +1,2 @@
--left-context=3
--right-context=3