Public Access
479 lines
16 KiB
C#
479 lines
16 KiB
C#
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 AutomationOptions Automation { get; set; } = new();
|
|
|
|
public CalendarRecordingPromptOptions CalendarRecordingPrompts { get; set; } = new();
|
|
|
|
public ScreenshotOptions Screenshots { get; set; } = new();
|
|
|
|
public AgentOptions Agent { get; set; } = new();
|
|
|
|
public WorkflowRulesEditorOptions WorkflowRulesEditor { get; set; } = new();
|
|
|
|
public ApiOptions Api { get; set; } = new();
|
|
}
|
|
|
|
public sealed class AutomationOptions
|
|
{
|
|
public string? RulesPath { get; set; } = "meeting-rules.local.yaml";
|
|
}
|
|
|
|
public sealed class CalendarRecordingPromptOptions
|
|
{
|
|
public bool Enabled { get; set; } = true;
|
|
|
|
public TimeSpan SyncInterval { get; set; } = TimeSpan.FromMinutes(30);
|
|
|
|
public TimeSpan PromptWindow { get; set; } = TimeSpan.FromMinutes(5);
|
|
}
|
|
|
|
public sealed class ScreenshotOptions
|
|
{
|
|
public string Hotkey { get; set; } = "Ctrl+Alt+S";
|
|
|
|
public string AttachmentsFolder { get; set; } = "Attachments";
|
|
|
|
public ScreenshotOcrOptions Ocr { get; set; } = new();
|
|
}
|
|
|
|
public sealed class ScreenshotOcrOptions
|
|
{
|
|
public const string DefaultPrompt = """
|
|
This screenshot was captured during a meeting. Extract information that is useful for meeting notes.
|
|
|
|
Identify who appears to be talking, who appears to be presenting, and what is being presented when visible.
|
|
If people or participant tiles are visible, state whether it is clear from the screenshot exactly who is in the meeting or whether the visible people are only a partial participant result.
|
|
Include any attendee names that are clearly visible or strongly deduced from the screenshot in the meeting-assistant metadata. The attendee list may be partial.
|
|
If the screenshot contains slides, capture the visible text in clean markdown.
|
|
If the screenshot contains a diagram, convert it to Mermaid when possible and preserve labels accurately.
|
|
If it contains another kind of shared screen or scene, describe the scene and the relevant visible information.
|
|
If you can isolate the actual presentation, shared screen, or similarly relevant meeting content, return crop coordinates for only that content.
|
|
Crop coordinates must be pixel coordinates relative to the original screenshot: x, y, width, height.
|
|
Do not return crop coordinates for a person, webcam tile, whole app chrome, or content you cannot isolate confidently.
|
|
End your response with exactly one fenced json block containing meeting-assistant metadata, for example:
|
|
```json
|
|
{ "crop": { "x": 0, "y": 0, "width": 1920, "height": 1080 }, "attendees": ["Ada Lovelace"] }
|
|
```
|
|
Use `{ "crop": null, "attendees": [] }` when no confident presentation/shared-screen crop exists and no attendees are visible or deducible.
|
|
Do not invent information that is not visible in the screenshot.
|
|
""";
|
|
|
|
public bool Enabled { get; set; }
|
|
|
|
public string? Endpoint { get; set; }
|
|
|
|
public string? Key { get; set; }
|
|
|
|
public string KeyEnv { get; set; } = "LITELLM_API_KEY";
|
|
|
|
public string? Model { get; set; }
|
|
|
|
public string Prompt { get; set; } = DefaultPrompt;
|
|
|
|
public TimeSpan Timeout { get; set; } = TimeSpan.FromMinutes(2);
|
|
|
|
public TimeSpan GetEffectiveTimeout()
|
|
{
|
|
return Timeout > TimeSpan.Zero ? Timeout : TimeSpan.FromMinutes(2);
|
|
}
|
|
}
|
|
|
|
public sealed class HotkeyOptions
|
|
{
|
|
public string Toggle { get; set; } = "Ctrl+Alt+M";
|
|
|
|
public string Abort { get; set; } = "Ctrl+Alt+Z";
|
|
}
|
|
|
|
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 string? MicrophoneDeviceId { get; set; }
|
|
|
|
public double MicrophoneMixGain { get; set; } = 1;
|
|
|
|
public double SystemAudioMixGain { get; set; } = 1;
|
|
|
|
public TimeSpan StopProcessingTimeout { get; set; } = TimeSpan.FromMinutes(10);
|
|
|
|
public TimeSpan MinimumCompletedMeetingDuration { get; set; } = TimeSpan.Zero;
|
|
|
|
public TimeSpan MetadataLookupTimeout { get; set; } = TimeSpan.FromSeconds(10);
|
|
|
|
public TimeSpan BackgroundMetadataLookupTimeout { get; set; } = TimeSpan.FromMinutes(1);
|
|
|
|
public int MaxMetadataAttendeeImportCount { get; set; } = 30;
|
|
|
|
public TimeSpan OpenMeetingNoteDelay { get; set; } = TimeSpan.FromSeconds(1);
|
|
|
|
public TimeSpan DictationWordsReadTimeout { get; set; } = TimeSpan.FromSeconds(2);
|
|
|
|
public string TemporaryRecordingsFolder { get; set; } = @"%LOCALAPPDATA%\MeetingAssistant\Recordings";
|
|
|
|
public RecordingInactivitySafeguardOptions InactivitySafeguard { get; set; } = new();
|
|
}
|
|
|
|
public sealed class RecordingInactivitySafeguardOptions
|
|
{
|
|
public bool Enabled { get; set; } = true;
|
|
|
|
public TimeSpan FirstPromptAfter { get; set; } = TimeSpan.FromMinutes(2);
|
|
|
|
public TimeSpan[] ReminderPromptAfter { get; set; } =
|
|
[
|
|
TimeSpan.FromMinutes(5),
|
|
TimeSpan.FromMinutes(10)
|
|
];
|
|
|
|
public TimeSpan AutoStopAfter { get; set; } = TimeSpan.FromMinutes(30);
|
|
|
|
public TimeSpan InferredEndPadding { get; set; } = TimeSpan.FromMinutes(1);
|
|
|
|
public TimeSpan CheckInterval { get; set; } = TimeSpan.FromSeconds(15);
|
|
}
|
|
|
|
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; } = "westeurope";
|
|
|
|
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.FromMinutes(3);
|
|
|
|
public int ReconnectAttemptsBeforeDisconnectedMarker { get; set; } = 5;
|
|
|
|
public TimeSpan ReconnectDelay { get; set; } = TimeSpan.FromSeconds(5);
|
|
|
|
public bool DiarizeIntermediateResults { get; set; } = true;
|
|
|
|
public string? PostProcessingOption { get; set; }
|
|
|
|
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 TimeSpan MinimumSampleSpeechDuration { get; set; } = TimeSpan.FromSeconds(30);
|
|
|
|
public TimeSpan MaximumSampleSegmentGap { get; set; } = TimeSpan.FromSeconds(1);
|
|
|
|
public double SilenceBetweenSnippetsSeconds { get; set; } = 1;
|
|
|
|
public TimeSpan LiveSampleBufferDuration { get; set; } = TimeSpan.FromMinutes(10);
|
|
|
|
public TimeSpan MergeRecentIdentityAge { get; set; } = TimeSpan.FromDays(14);
|
|
|
|
public TimeSpan MatchTimeout { get; set; } = TimeSpan.FromMinutes(3);
|
|
|
|
public AzureSpeechOptions AzureSpeech { get; set; } = new();
|
|
|
|
public SpeakerIdentityPyannoteValidationOptions PyannoteValidation { get; set; } = new();
|
|
}
|
|
|
|
public sealed class SpeakerIdentityPyannoteValidationOptions
|
|
{
|
|
public bool Enabled { get; set; }
|
|
|
|
public double MinimumSingleSpeakerCoverage { get; set; } = 0.90;
|
|
|
|
public double MinimumMatchingKnownSnippetRatio { get; set; } = 0.50;
|
|
|
|
public PyannoteDiarizationOptions Diarization { get; set; } = new()
|
|
{
|
|
Enabled = true,
|
|
CommandTimeout = TimeSpan.FromHours(1),
|
|
AlignmentMode = PyannoteAlignmentMode.PyannoteTurns
|
|
};
|
|
}
|
|
|
|
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; } = "chatgpt/gpt-5.5";
|
|
|
|
public bool UseStreaming { get; set; } = true;
|
|
|
|
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 WorkflowRulesEditorOptions
|
|
{
|
|
public string? Endpoint { get; set; }
|
|
|
|
public string? Key { get; set; }
|
|
|
|
public string? KeyEnv { get; set; }
|
|
|
|
public string? Model { get; set; }
|
|
|
|
public bool? UseStreaming { get; set; }
|
|
|
|
public bool? EnableThinking { get; set; }
|
|
|
|
public ReasoningEffortOption? ReasoningEffort { get; set; }
|
|
|
|
public int? ReconnectionAttempts { get; set; }
|
|
|
|
public TimeSpan? ReconnectionDelay { get; set; }
|
|
|
|
public int? ContextWindowTokens { get; set; }
|
|
|
|
public int? MaxOutputTokens { get; set; }
|
|
|
|
public bool? EnableCompaction { get; set; }
|
|
|
|
public double? CompactionRemainingRatio { get; set; }
|
|
|
|
public string? ResponsesCompactPath { get; set; }
|
|
|
|
public string? InitialPrompt { get; set; }
|
|
|
|
public AgentOptions ToEffectiveAgentOptions(AgentOptions defaults)
|
|
{
|
|
return new AgentOptions
|
|
{
|
|
Endpoint = string.IsNullOrWhiteSpace(Endpoint) ? defaults.Endpoint : Endpoint,
|
|
Key = string.IsNullOrWhiteSpace(Key) ? defaults.Key : Key,
|
|
KeyEnv = string.IsNullOrWhiteSpace(KeyEnv) ? defaults.KeyEnv : KeyEnv!,
|
|
Model = string.IsNullOrWhiteSpace(Model) ? defaults.Model : Model!,
|
|
UseStreaming = UseStreaming ?? defaults.UseStreaming,
|
|
EnableThinking = EnableThinking ?? defaults.EnableThinking,
|
|
ReasoningEffort = ReasoningEffort ?? defaults.ReasoningEffort,
|
|
ReconnectionAttempts = ReconnectionAttempts ?? defaults.ReconnectionAttempts,
|
|
ReconnectionDelay = ReconnectionDelay ?? defaults.ReconnectionDelay,
|
|
ContextWindowTokens = ContextWindowTokens ?? defaults.ContextWindowTokens,
|
|
MaxOutputTokens = MaxOutputTokens ?? defaults.MaxOutputTokens,
|
|
EnableCompaction = EnableCompaction ?? defaults.EnableCompaction,
|
|
CompactionRemainingRatio = CompactionRemainingRatio ?? defaults.CompactionRemainingRatio,
|
|
ResponsesCompactPath = string.IsNullOrWhiteSpace(ResponsesCompactPath)
|
|
? defaults.ResponsesCompactPath
|
|
: ResponsesCompactPath!,
|
|
InitialPrompt = string.IsNullOrWhiteSpace(InitialPrompt) ? null : InitialPrompt
|
|
};
|
|
}
|
|
}
|
|
|
|
public sealed class ApiOptions
|
|
{
|
|
public string PublicBaseUrl { get; set; } = "http://localhost:5090";
|
|
}
|
|
|
|
public enum ReasoningEffortOption
|
|
{
|
|
None,
|
|
Low,
|
|
Medium,
|
|
High,
|
|
ExtraHigh
|
|
}
|