Files
meeting-assistant/docs/meeting-assistant-configuration.md
T
codex 250d3b7a1e
PR and Push Build/Test / build-and-test (push) Successful in 16m43s
Generalize settings and logs assistant
2026-05-30 12:57:51 +02:00

338 lines
24 KiB
Markdown

# Meeting Assistant Configuration
Meeting Assistant uses normal .NET configuration under the `MeetingAssistant` section. The checked-in `appsettings.json` stores local paths, model names, endpoints, and environment variable names. It should not store secret values; use the configured `KeyEnv` fields for API keys.
## Example
This example is abbreviated so the most common shape is readable. The checked-in [appsettings.json](../MeetingAssistant/appsettings.json) is the canonical full example.
```json
{
"MeetingAssistant": {
"Hotkey": {
"Toggle": "Ctrl+Alt+M",
"Abort": "Ctrl+Alt+Z"
},
"Vault": {
"BaseFolder": "%USERPROFILE%\\OpenCloud\\Persönlich\\Vault\\Exxeta",
"TranscriptsFolder": "Meetings\\Transcripts",
"MeetingNotesFolder": "Meetings\\Notes",
"AssistantContextFolder": "Meetings\\Assistant Context",
"SummariesFolder": "Meetings\\Summaries",
"ProjectsFolder": "Projects",
"DictationWordsPath": "Meetings\\dictation-words.md"
},
"Recording": {
"TranscriptionProvider": "funasr",
"SampleRate": 16000,
"Channels": 1,
"MicrophoneMixGain": 1,
"SystemAudioMixGain": 1,
"StopProcessingTimeout": "00:10:00",
"MinimumCompletedMeetingDuration": "00:01:00",
"MaxMetadataAttendeeImportCount": 30,
"TemporaryRecordingsFolder": "%LOCALAPPDATA%\\MeetingAssistant\\Recordings"
},
"Automation": {
"RulesPath": "meeting-rules.local.yaml"
},
"WorkflowRulesEditor": {
"Endpoint": "",
"KeyEnv": "",
"Model": ""
},
"Screenshots": {
"Hotkey": "Ctrl+Alt+S",
"AttachmentsFolder": "Attachments"
},
"Agent": {
"Endpoint": "https://litellm.schweigert.cloud",
"KeyEnv": "LITELLM_API_KEY",
"Model": "chatgpt/gpt-5.5"
},
"Api": {
"PublicBaseUrl": "http://localhost:5090"
},
"LaunchProfiles": {
"english": {
"Hotkey": {
"Toggle": "Ctrl+Alt+E"
},
"AzureSpeech": {
"Language": "en-US",
"AutoDetectLanguages": [ "en-US" ]
}
}
}
},
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft.AspNetCore": "Warning"
}
}
}
```
## Paths And Overrides
Meeting Assistant expands environment variables such as `%USERPROFILE%` and supports `~` at the start of paths.
Relative vault paths are resolved under `Vault:BaseFolder`. Absolute paths bypass `BaseFolder`. For example, `Vault:MeetingNotesFolder` set to `Meetings\Notes` resolves below `BaseFolder`, while `C:\Notes` is used directly.
Launch profiles live under `MeetingAssistant:LaunchProfiles:{profileName}`. A profile starts with the default `MeetingAssistant` options and then binds its profile section over those defaults. This allows profile-specific language, provider, hotkey, screenshot, or agent overrides without duplicating the whole configuration.
The default profile is always named `default`. Non-default profile hotkeys are registered only when the specific profile explicitly configures that hotkey section. This avoids accidental hotkey collisions from inherited defaults. Array options currently need explicit profile override handling for `AzureSpeech:AutoDetectLanguages` and `FunAsr:ChunkSize`; both are supported.
## Vault
`Vault` configures where Meeting Assistant writes meeting notes, transcripts, assistant context notes, generated summaries, project knowledge, and dictation words. Meeting notes link to separate transcript, assistant context, and summary markdown artifacts using these configured folders.
`BaseFolder` is the root for relative vault paths. Use relative paths for files that should move with the vault root, and absolute paths for external files.
`ProjectsFolder` contains project knowledge. Each direct subfolder is treated as one project, and a meeting binds projects by listing those subfolder names in the meeting note `projects` frontmatter.
`DictationWordsPath` is reserved for providers that can accept vocabulary hints. It points to a markdown word list containing unusual project, person, product, or domain terms that should be biased during transcription when the provider supports it.
## Recording
`Recording:TranscriptionProvider` selects the speech recognition pipeline. Supported values include `funasr`, `whisper-local`, and `azure-speech`.
| Setting | Purpose |
| --- | --- |
| `SampleRate` | PCM sample rate for captured audio. The current providers expect 16000 Hz. |
| `Channels` | PCM channel count for the mixed stream. The current providers expect mono (`1`). |
| `MicrophoneMixGain` | Gain applied to cleaned microphone samples before final mixing. |
| `SystemAudioMixGain` | Gain applied to system loopback samples before final mixing. |
| `StopProcessingTimeout` | Maximum time to wait for post-stop processing such as transcription drain, finalization, OCR wait, and summary handoff. |
| `MinimumCompletedMeetingDuration` | Runs stopped before this duration are treated like aborts, so accidental same-minute starts do not produce meeting artifacts. |
| `MetadataLookupTimeout` | Foreground timeout for initial metadata lookup when starting a meeting. |
| `BackgroundMetadataLookupTimeout` | Longer background timeout for metadata that can continue after transcription starts. |
| `MaxMetadataAttendeeImportCount` | Maximum Outlook attendee count that will be copied into meeting frontmatter. |
| `OpenMeetingNoteDelay` | Delay before opening the newly created Obsidian note, giving the file system and app time to settle. |
| `TemporaryRecordingsFolder` | Folder for temporary mixed WAV files while a run is active. |
During recording, Meeting Assistant captures microphone and system loopback separately, buffers both streams to align samples, cleans the microphone stream through a local adaptive echo canceller using loopback as the far-end reference, then mixes the cleaned microphone and system streams into the normal 16 kHz mono PCM chunks. If one source is quiet beyond the alignment timeout, the available source is mixed with synthetic silence so microphone-only speech and system-only playback keep flowing to transcription.
`Recording:MicrophoneMixGain` and `Recording:SystemAudioMixGain` are applied during the final mix and default to `1`. `Recording:TemporaryRecordingsFolder` controls where the temporary mixed WAV is written while the run is active. Temporary WAV files are deleted after the run completes, and stale temporary recordings from interrupted runs are deleted when the application starts.
`Recording:MaxMetadataAttendeeImportCount` limits how many attendees Outlook metadata enrichment imports into meeting-note frontmatter. The default is `30`; when an appointment has more attendees than that, Meeting Assistant still imports title, agenda, and scheduled end time, but leaves attendees empty because large invites are usually presentation-style meetings.
`Hotkey:Abort` configures a global discard shortcut. The default is `Ctrl+Alt+Z`. Aborting an active recording stops capture/transcription, deletes the meeting note, transcript, assistant context, summary file if present, and linked screenshot attachments for that run, and skips automatic summary generation.
Launch profile toggle hotkeys start a meeting when idle. When a different launch profile toggle is pressed during an active meeting, Meeting Assistant keeps the same meeting note, transcript, assistant context, and metadata; drains the current speech recognition pipeline without summarizing; appends a transcript marker; clears run-local speaker mappings; and continues transcription through the target profile.
## FunASR
`funasr` streams 16 kHz PCM audio to the configured FunASR WebSocket endpoint. When `FunAsr:Backend:Enabled` is true, Meeting Assistant manages a local Docker-backed FunASR runtime. On application startup it begins a non-blocking warm-up for the default launch profile and any named launch profile that selects `funasr`.
| Setting | Purpose |
| --- | --- |
| `Endpoint` | WebSocket endpoint for the FunASR streaming server. |
| `Mode` | FunASR mode sent in streaming requests, usually `2pass`. |
| `ChunkSize` | Three-part FunASR chunk size array. |
| `ChunkInterval` | FunASR chunk interval sent with streaming requests. |
| `EncoderChunkLookBack` | Encoder lookback count for streaming context. |
| `DecoderChunkLookBack` | Decoder lookback count for streaming context. |
| `Itn` | Enables inverse text normalization when the backend supports it. |
| `EmitOnlinePartials` | Emits online partial transcripts instead of waiting only for final two-pass text. |
| `WavName` | Logical WAV name sent to FunASR. |
| `FinalResultTimeout` | Time to wait for a final FunASR response after the stream ends. |
The warm-up pulls the configured online streaming image, prepares the mounted model and hotword folder, replaces any stale container with the configured name, starts the two-pass WebSocket server mapped to the endpoint port, keeps the container alive after the FunASR startup script backgrounds the server process, and stops it when the app shuts down. Recording can start while the backend is still warming up; captured audio is queued and then streamed once the WebSocket backend is healthy.
`FunAsr:Backend` controls the managed Docker runtime:
| Setting | Purpose |
| --- | --- |
| `Enabled` | Starts and warms up the Docker-backed backend from Meeting Assistant. |
| `DockerCommand` | Docker executable name or path. |
| `Image` | FunASR runtime image. |
| `ContainerName` | Container name used for replacement, startup, and shutdown. |
| `ModelsFolder` | Host folder mounted as persistent model and hotword storage. |
| `ContainerPort` | Container and host port used by the WebSocket endpoint. |
| `Privileged` | Runs the container with Docker privileged mode when true. |
| `CommandTimeout` | Timeout for Docker commands such as pull/run/build. |
| `StartupTimeout` | Timeout for backend WebSocket readiness. |
| `StopOnDispose` | Stops the managed container when the app shuts down. |
| `ServerCommand` | Shell command executed inside the container to start FunASR. |
FunASR response fields such as `spk_name`, `spk`, and sentence-level speaker metadata are preserved in transcript segments; missing speaker fields are written as `Unknown`.
If `FunAsr:Diarization:Enabled` is true, FunASR Python `AutoModel` can run with VAD, punctuation, and CAMPPlus over the temporary WAV after capture stops and the streaming ASR provider drains the already-captured audio. If sentence-level speaker labels are returned, the transcript markdown is rewritten with the final speaker-attributed segments. If final diarization is disabled or returns no segments, the live streaming transcript is kept.
`FunAsr:Diarization` controls that final pass:
| Setting | Purpose |
| --- | --- |
| `Enabled` | Runs the final FunASR diarization/finalization pass over the temporary WAV. |
| `AsrModel` | FunASR ASR model name for finalization. |
| `VadModel` | FunASR VAD model name. |
| `PunctuationModel` | FunASR punctuation model name. |
| `SpeakerModel` | FunASR speaker model, for example `cam++`. |
| `BatchSizeSeconds` | Audio batch size for finalization. |
| `BatchSizeThresholdSeconds` | Threshold used by the finalizer before batching. |
| `PresetSpeakerCount` | Optional fixed speaker-count hint for diarization. |
| `CommandTimeout` | Timeout for the final FunASR command. |
## Whisper Local
`whisper-local` uses Whisper.NET with a local ggml model and remains available by setting `Recording:TranscriptionProvider` to `whisper-local`. The model file is not committed to this repository; place it at the configured `WhisperLocal:ModelPath` before using live transcription.
| Setting | Purpose |
| --- | --- |
| `ModelPath` | Path to the local ggml Whisper model file. |
| `Language` | Whisper language hint, or `auto` for automatic detection. |
| `WindowSeconds` | Window size used when streaming audio into local Whisper. |
When `WhisperLocal:Diarization:Enabled` is true, the final post-processing pass runs pyannote in Docker over the temporary WAV and reads the Hugging Face token from `WhisperLocal:Diarization:Token` or `TokenEnv` (`HF_TOKEN` by default). `AnnotationSource` selects pyannote's `speaker_diarization` or `exclusive_speaker_diarization` output. `AlignmentMode` selects whether Meeting Assistant assigns the best-overlap pyannote speaker label to each Whisper segment or rebuilds the finished transcript as pyannote speaker-turn segments with matching Whisper text.
Active pyannote runtimes are warmed up on application start so image setup and model download do not wait for the first diarization request.
Pyannote diarization settings are shared by local Whisper finalization and speaker-identification validation:
| Setting | Purpose |
| --- | --- |
| `Enabled` | Enables the pyannote-backed pass. |
| `DockerCommand` | Docker executable name or path. |
| `BaseImage` | Python base image used when building the local pyannote image. |
| `Image` | Local pyannote Docker image tag. |
| `BuildImage` | Builds the local image when it is missing. |
| `ModelsFolder` | Host folder mounted as Hugging Face, torch, and pip cache. |
| `Model` | Pyannote model name. |
| `AnnotationSource` | Selects `SpeakerDiarization` or `ExclusiveSpeakerDiarization`. |
| `AlignmentMode` | Selects `BestOverlap` or `PyannoteTurns` transcript alignment. |
| `Token` | Optional Hugging Face token value. Prefer `TokenEnv` for normal use. |
| `TokenEnv` | Environment variable name that contains the Hugging Face token. |
| `CommandTimeout` | Timeout for the pyannote command. |
## Azure Speech
`azure-speech` streams Meeting Assistant's captured PCM chunks to Azure AI Speech using the Speech SDK `ConversationTranscriber`; it does not use an Azure-owned microphone capture or MAS/AEC input path.
Configure `AzureSpeech:Endpoint`, `Region`, `Language`, and either `Key` or `KeyEnv`; this repository uses `AZURE_SPEECH_KEY` by default and does not store the key in appsettings. `DiarizeIntermediateResults` enables diarization for intermediate and final conversation transcription results.
| Setting | Purpose |
| --- | --- |
| `Endpoint` | Optional Azure Speech endpoint. When blank, `Region` plus subscription key is used. |
| `Region` | Azure Speech region used for subscription-based config. |
| `Language` | Fixed recognition language. Set to `auto` to enable Azure source language auto-detection. |
| `AutoDetectLanguages` | Candidate languages used only when `Language` is `auto`. |
| `LanguageIdMode` | Azure language identification mode; `AtStart` or `Continuous`. Other values normalize to `Continuous`. |
| `Key` | Optional inline Azure Speech key. Prefer `KeyEnv`. |
| `KeyEnv` | Environment variable name that contains the Azure Speech key. |
| `RecognitionStopTimeout` | Timeout while stopping the ConversationTranscriber after closing the audio stream. |
| `DiarizeIntermediateResults` | Requests diarized intermediate results from Azure. |
| `PostProcessingOption` | Accepted by config but currently skipped for the live `ConversationTranscriber` path; the app logs a warning if set. |
| `PhraseListWeight` | Weight applied to dictation-word phrase-list hints. |
Azure returns generic speaker IDs such as `Guest-1` and `Guest-2`, which Meeting Assistant writes directly into live transcript segments. `RecognitionStopTimeout` bounds SDK shutdown after the captured audio stream has been closed; the default is 3 minutes so Azure has time to finalize longer speaker-recognition samples.
## Speaker Identification
Speaker identity matching keeps candidate samples only after a diarized speaker has at least `SpeakerIdentification:MinimumSampleSpeechDuration` of continuous speech, defaulting to 30 seconds. Adjacent same-speaker segments may be combined when the gap is no larger than `MaximumSampleSegmentGap`, but a different speaker resets the pending span.
| Setting | Purpose |
| --- | --- |
| `Enabled` | Enables live speaker identity matching and sample collection. |
| `DatabasePath` | SQLite database path for identities, aliases, references, and samples. |
| `InitialDelay` | Delay after recording starts before the first live identity pass. |
| `Interval` | Interval between live identity passes. |
| `MatchBatchSize` | Number of identities processed per model matching batch. |
| `MaxMatchCandidates` | Maximum known identities considered during one match pass. |
| `MatchIdentityActiveAge` | Age window for identities considered active enough for automatic matching. |
| `MaxSnippetsPerSpeaker` | Maximum stored voice snippets retained per speaker identity. |
| `MinimumSampleSpeechDuration` | Minimum uninterrupted same-speaker speech span needed before storing a sample. |
| `MaximumSampleSegmentGap` | Maximum gap allowed when combining adjacent same-speaker segments into one sample. |
| `SilenceBetweenSnippetsSeconds` | Silence padding inserted between snippets during Azure identity matching. |
| `LiveSampleBufferDuration` | How long live transcript/audio material is retained for extracting samples. |
| `MergeRecentIdentityAge` | Age window used by diagnostics that merge recent duplicate identities. |
| `MatchTimeout` | Timeout for an Azure-backed identity matching request. |
`SpeakerIdentification:AzureSpeech` is an advanced nested override for speaker identity matching. It uses the same shape as `AzureSpeech` and lets identity matching use different Azure language, endpoint, or key settings than live transcription when needed. If it is left unset, the normal Azure Speech settings remain the practical default.
`SpeakerIdentification:PyannoteValidation` is an optional secondary confidence layer. When enabled, pyannote rejects multi-speaker samples and must confirm an Azure-confirmed identity match before Meeting Assistant accepts it. It uses the same Docker-based pyannote runtime shape as local Whisper finalization and defaults the validation command timeout to 1 hour because local model setup can take substantial time.
| Setting | Purpose |
| --- | --- |
| `MinimumSingleSpeakerCoverage` | Required fraction of the tested sample that pyannote must attribute to a single speaker. |
| `MinimumMatchingKnownSnippetRatio` | Required pyannote agreement ratio between the new sample and known snippets for an accepted identity. |
| `Diarization` | Nested pyannote settings used for this validation pass. |
## Automation
`Automation:RulesPath` points to an optional local YAML rules file. The default `meeting-rules.local.yaml` is ignored by git. Rules can trigger on meeting creation, assistant-context state transitions, or identified speakers; conditions are evaluated with NCalc-style expressions and step values can use Razor syntax against `Model.Meeting`, `Model.Event`, and `Model.Speaker`.
See `docs/meeting-workflow-engine.md` for the detailed YAML format, supported variables, examples, and extension notes.
`POST /diagnostics/workflow/reload` reloads application configuration so workflow automation settings such as `Automation:RulesPath` can be changed without restarting the application. A meeting run that already captured its options keeps using those options.
## Screenshots
`Screenshots:Hotkey` configures a global hotkey that captures the currently active window during an active meeting. Screenshots are written under `Screenshots:AttachmentsFolder`, which defaults to an `Attachments` folder beside the assistant context note, and each capture appends a timestamped markdown image link to the assistant context.
`Screenshots:Ocr` optionally enables vision extraction for screenshots. Blank `Endpoint` or `Model` values fall back to the summary `Agent` endpoint and model. `Key` or `KeyEnv` can be set specifically for OCR; otherwise the summary agent key configuration is used. Automatic summarization waits for pending screenshot OCR work to complete or hit `Timeout` before the assistant context moves to `summarizing`.
| Setting | Purpose |
| --- | --- |
| `Enabled` | Sends screenshots to the OCR/vision model when true. |
| `Endpoint` | Optional OpenAI-compatible Responses endpoint override. |
| `Key` | Optional inline API key. Prefer `KeyEnv`. |
| `KeyEnv` | Environment variable name for the OCR API key. |
| `Model` | Optional OCR model override. Blank falls back to `Agent:Model`. |
| `Prompt` | OCR instruction prompt. The default asks for visible speakers/presenters, slide text as markdown, diagrams as Mermaid, scene descriptions, participant certainty, and presentation/shared-screen crop coordinates when confidently isolatable. |
| `Timeout` | Maximum time to wait for OCR before summarization continues. |
## Agent And Summary
`Agent` configures the Microsoft Agent Framework summary pipeline. `Key` may be set directly for local testing, but `KeyEnv` is preferred; by default the API key is read from `LITELLM_API_KEY`.
| Setting | Purpose |
| --- | --- |
| `Endpoint` | OpenAI-compatible endpoint base URL. |
| `Key` | Optional inline API key. Prefer `KeyEnv`. |
| `KeyEnv` | Environment variable name for the agent API key. |
| `Model` | Model id sent to the endpoint. |
| `EnableThinking` | Enables reasoning options in requests when supported by the model/backend. |
| `ReasoningEffort` | Reasoning effort: `None`, `Low`, `Medium`, `High`, or `ExtraHigh`. |
| `ReconnectionAttempts` | Retry count for transient model endpoint failures. |
| `ReconnectionDelay` | Delay between retry attempts. |
| `ContextWindowTokens` | Estimated context-window size used by compaction decisions. |
| `MaxOutputTokens` | Maximum output tokens for agent responses. |
| `EnableCompaction` | Enables conversation compaction before requests exceed the configured context budget. |
| `CompactionRemainingRatio` | Remaining-context ratio that triggers compaction. |
| `ResponsesCompactPath` | Relative Responses compaction path, usually `responses/compact`. |
| `InitialPrompt` | Optional complete replacement for the default summary-agent system instructions. |
After transcription has fully finished, Meeting Assistant automatically runs the summary pipeline for the meeting. The summary agent writes the full markdown summary through `write_summary` and must provide a required `oneliner` value, which is stored in summary frontmatter and must not contain line breaks.
The summary agent can add and remove meeting-note attendees when transcript or OCR evidence is clear. It can override transcript speaker labels only when the evidence is very certain, and it can delete wrongfully matched identities. Final speaker identity learning and candidate updates run after the summary pipeline finishes so they use the summary-refined attendee list and any recorded speaker identity changes.
`ContextWindowTokens`, `MaxOutputTokens`, `EnableCompaction`, and `CompactionRemainingRatio` configure summary-agent context monitoring. Meeting Assistant estimates the outgoing Responses payload size, logs the estimated token count and remaining context, and compacts the conversation when only the configured remaining ratio is left.
## Workflow Rules Editor
`WorkflowRulesEditor` configures the tray-launched settings/logs assistant. Blank values inherit from `Agent`, so it uses the summarizer endpoint, key, model, reasoning, retry, output, and compaction settings unless explicitly overridden.
The overridable fields are `Endpoint`, `Key`, `KeyEnv`, `Model`, `EnableThinking`, `ReasoningEffort`, `ReconnectionAttempts`, `ReconnectionDelay`, `ContextWindowTokens`, `MaxOutputTokens`, `EnableCompaction`, `CompactionRemainingRatio`, `ResponsesCompactPath`, and `InitialPrompt`.
The assistant can edit workflow rules, manage speaker identities, read and replace the local appsettings file, read this configuration document, search and read copied OpenSpec specs, and inspect application logs.
The `search_spec` and `read_spec_file` tools are scoped to the copied `openspec/specs` markdown tree. Spec files are copied into build and publish output through an MSBuild glob, so newly added folders under `openspec/specs` are included automatically.
## Logs
The top-level `Logging` section is standard ASP.NET Core logging configuration and controls framework/console log levels, for example `Logging:LogLevel:Microsoft.AspNetCore`.
Independently from stdout and stderr redirection, Meeting Assistant writes an application-owned log under:
```text
%TEMP%\MeetingAssistant\Logs\meeting-assistant.log
```
On startup it rotates the previous current file to `meeting-assistant.log.1` and keeps up to `.4`. The settings/logs assistant reads and searches these current and rotated files. If another app instance or test host still has the file open, rotation is skipped and logging appends to the current file so startup is not blocked.
## API
`Api:PublicBaseUrl` is used to build clickable local links, such as summary retry links in generated failure notes.