namespace MeetingAssistant; public sealed class MeetingAssistantOptions { public HotkeyOptions Hotkey { get; set; } = new(); public VaultOptions Vault { get; set; } = new(); public RecordingOptions Recording { get; set; } = new(); public WhisperLocalOptions WhisperLocal { get; set; } = new(); public AzureSpeechOptions AzureSpeech { get; set; } = new(); public FunAsrOptions FunAsr { get; set; } = new(); public SpeakerIdentificationOptions SpeakerIdentification { get; set; } = new(); public AgentOptions Agent { get; set; } = new(); public ApiOptions Api { get; set; } = new(); } public sealed class HotkeyOptions { public string Toggle { get; set; } = "Ctrl+Alt+M"; } public sealed class VaultOptions { public string BaseFolder { get; set; } = @"%USERPROFILE%\OpenCloud\Persönlich\Vault\Exxeta"; public string TranscriptsFolder { get; set; } = @"Meetings\Transcripts"; public string MeetingNotesFolder { get; set; } = @"Meetings\Notes"; public string AssistantContextFolder { get; set; } = @"Meetings\Assistant Context"; public string SummariesFolder { get; set; } = @"Meetings\Summaries"; public string ProjectsFolder { get; set; } = @"Projects"; public string DictationWordsPath { get; set; } = @"Meetings\dictation-words.md"; } public sealed class RecordingOptions { public string TranscriptionProvider { get; set; } = "azure-speech"; public int SampleRate { get; set; } = 16000; public int Channels { get; set; } = 1; public TimeSpan StopProcessingTimeout { get; set; } = TimeSpan.FromMinutes(10); public TimeSpan MetadataLookupTimeout { get; set; } = TimeSpan.FromSeconds(10); public TimeSpan BackgroundMetadataLookupTimeout { get; set; } = TimeSpan.FromMinutes(1); public TimeSpan OpenMeetingNoteDelay { get; set; } = TimeSpan.FromSeconds(1); public string TemporaryRecordingsFolder { get; set; } = @"%LOCALAPPDATA%\MeetingAssistant\Recordings"; } public sealed class WhisperLocalOptions { public string ModelPath { get; set; } = @"models\ggml-base.bin"; public string Language { get; set; } = "auto"; public double WindowSeconds { get; set; } = 15; public PyannoteDiarizationOptions Diarization { get; set; } = new(); } public sealed class PyannoteDiarizationOptions { public bool Enabled { get; set; } = true; public string DockerCommand { get; set; } = "docker"; public string BaseImage { get; set; } = "python:3.11-slim"; public string Image { get; set; } = "meeting-assistant-pyannote:local"; public bool BuildImage { get; set; } = true; public string ModelsFolder { get; set; } = @"%LOCALAPPDATA%\MeetingAssistant\Pyannote\models"; public string Model { get; set; } = "pyannote/speaker-diarization-3.1"; public PyannoteAnnotationSource AnnotationSource { get; set; } = PyannoteAnnotationSource.SpeakerDiarization; public PyannoteAlignmentMode AlignmentMode { get; set; } = PyannoteAlignmentMode.PyannoteTurns; public string TokenEnv { get; set; } = "HF_TOKEN"; public string? Token { get; set; } public TimeSpan CommandTimeout { get; set; } = TimeSpan.FromMinutes(30); } public sealed class AzureSpeechOptions { public string Endpoint { get; set; } = ""; public string Region { get; set; } = "germanywestcentral"; public string Language { get; set; } = "auto"; public string[] AutoDetectLanguages { get; set; } = ["de-DE", "en-US"]; public string LanguageIdMode { get; set; } = "Continuous"; public string? Key { get; set; } public string KeyEnv { get; set; } = "AZURE_SPEECH_KEY"; public TimeSpan RecognitionStopTimeout { get; set; } = TimeSpan.FromSeconds(10); public bool DiarizeIntermediateResults { get; set; } = true; public double PhraseListWeight { get; set; } = 1.5; } public enum PyannoteAnnotationSource { SpeakerDiarization, ExclusiveSpeakerDiarization } public enum PyannoteAlignmentMode { BestOverlap, PyannoteTurns } public sealed class FunAsrOptions { public string Endpoint { get; set; } = "ws://127.0.0.1:10095"; public string Mode { get; set; } = "2pass"; public int[] ChunkSize { get; set; } = [5, 10, 5]; public int ChunkInterval { get; set; } = 10; public int EncoderChunkLookBack { get; set; } = 4; public int DecoderChunkLookBack { get; set; } = 1; public bool Itn { get; set; } = true; public bool EmitOnlinePartials { get; set; } public string WavName { get; set; } = "meeting"; public TimeSpan FinalResultTimeout { get; set; } = TimeSpan.FromSeconds(10); public FunAsrBackendOptions Backend { get; set; } = new(); public FunAsrDiarizationOptions Diarization { get; set; } = new(); } public sealed class FunAsrBackendOptions { public bool Enabled { get; set; } = true; public string DockerCommand { get; set; } = "docker"; public string Image { get; set; } = "registry.cn-hangzhou.aliyuncs.com/funasr_repo/funasr:funasr-runtime-sdk-online-cpu-0.1.13"; public string ContainerName { get; set; } = "meeting-assistant-funasr"; public string ModelsFolder { get; set; } = @"%LOCALAPPDATA%\MeetingAssistant\FunASR\models"; public int ContainerPort { get; set; } = 10095; public bool Privileged { get; set; } = true; public TimeSpan CommandTimeout { get; set; } = TimeSpan.FromMinutes(15); public TimeSpan StartupTimeout { get; set; } = TimeSpan.FromMinutes(10); public bool StopOnDispose { get; set; } = true; public string ServerCommand { get; set; } = "cd /workspace/FunASR/runtime && bash run_server_2pass.sh --download-model-dir /workspace/models --vad-dir damo/speech_fsmn_vad_zh-cn-16k-common-onnx --model-dir damo/speech_paraformer-large_asr_nat-zh-cn-16k-common-vocab8404-onnx --online-model-dir damo/speech_paraformer-large_asr_nat-zh-cn-16k-common-vocab8404-online-onnx --punc-dir damo/punc_ct-transformer_zh-cn-common-vad_realtime-vocab272727-onnx --itn-dir thuduj12/fst_itn_zh --certfile 0 --hotword /workspace/models/hotwords.txt && tail -f /dev/null"; } public sealed class FunAsrDiarizationOptions { public bool Enabled { get; set; } = true; public string AsrModel { get; set; } = "paraformer-zh"; public string VadModel { get; set; } = "fsmn-vad"; public string PunctuationModel { get; set; } = "ct-punc"; public string SpeakerModel { get; set; } = "cam++"; public double BatchSizeSeconds { get; set; } = 300; public double BatchSizeThresholdSeconds { get; set; } = 60; public int? PresetSpeakerCount { get; set; } public TimeSpan CommandTimeout { get; set; } = TimeSpan.FromMinutes(30); } public sealed class SpeakerIdentificationOptions { public bool Enabled { get; set; } = true; public string DatabasePath { get; set; } = @"%LOCALAPPDATA%\MeetingAssistant\SpeakerIdentity\speaker-identities.db"; public TimeSpan InitialDelay { get; set; } = TimeSpan.FromMinutes(2); public TimeSpan Interval { get; set; } = TimeSpan.FromMinutes(2); public int MatchBatchSize { get; set; } = 6; public int MaxMatchCandidates { get; set; } = 100; public TimeSpan MatchIdentityActiveAge { get; set; } = TimeSpan.FromDays(365); public int MaxSnippetsPerSpeaker { get; set; } = 3; public double SilenceBetweenSnippetsSeconds { get; set; } = 1; public TimeSpan LiveSampleBufferDuration { get; set; } = TimeSpan.FromMinutes(10); public TimeSpan MergeRecentIdentityAge { get; set; } = TimeSpan.FromDays(14); public AzureSpeechOptions AzureSpeech { get; set; } = new(); } public sealed class AgentOptions { public string Endpoint { get; set; } = "https://litellm.schweigert.cloud"; public string? Key { get; set; } public string KeyEnv { get; set; } = "LITELLM_API_KEY"; public string Model { get; set; } = "copilot-gpt-5.5"; public bool EnableThinking { get; set; } = true; public ReasoningEffortOption ReasoningEffort { get; set; } = ReasoningEffortOption.Medium; public int ReconnectionAttempts { get; set; } = 2; public TimeSpan ReconnectionDelay { get; set; } = TimeSpan.FromSeconds(2); public int ContextWindowTokens { get; set; } = 128000; public int MaxOutputTokens { get; set; } = 8192; public bool EnableCompaction { get; set; } = true; public double CompactionRemainingRatio { get; set; } = 0.10; public string ResponsesCompactPath { get; set; } = "responses/compact"; public string? InitialPrompt { get; set; } } public sealed class ApiOptions { public string PublicBaseUrl { get; set; } = "http://localhost:5090"; } public enum ReasoningEffortOption { None, Low, Medium, High, ExtraHigh }