ultisuite-backend/internal/orgpolicy/meet.go
R3D347HR4Y 1d063237b9
Some checks are pending
CI / Go tests (push) Waiting to run
CI / Integration tests (push) Waiting to run
CI / DB migrations (push) Waiting to run
feat(transcription): integrate Faster Whisper for Jitsi transcriptions
- Added support for Faster Whisper transcription via Jigasi and Skynet.
- Updated .env.example to include new environment variables for transcription settings.
- Enhanced Jitsi Docker Compose configuration to include Skynet and Jigasi services.
- Introduced new API endpoints for managing organizational folders in the drive service.
- Updated Nextcloud initialization script to enable external file mounting.
- Improved error handling and response structures in the drive API.
- Added new properties for organization settings related to transcription and agenda management.
2026-06-12 19:10:18 +02:00

170 lines
5.8 KiB
Go

package orgpolicy
import (
"context"
"encoding/json"
"github.com/jackc/pgx/v5"
)
// MeetPostActions configures transcript delivery after a meeting.
type MeetPostActions struct {
EmailEnabled bool `json:"email_enabled"`
EmailRecipients string `json:"email_recipients"`
EmailCustomAddresses string `json:"email_custom_addresses"`
DriveEnabled bool `json:"drive_enabled"`
DriveFolderPath string `json:"drive_folder_path"`
LLMEnabled bool `json:"llm_enabled"`
LLMProviderID string `json:"llm_provider_id"`
LLMPrompt string `json:"llm_prompt"`
LLMThenEmail bool `json:"llm_then_email"`
LLMThenDrive bool `json:"llm_then_drive"`
}
// MeetPolicy is the organisation UltiMeet / transcription policy.
type MeetPolicy struct {
TranscriptionEnabled bool `json:"transcription_enabled"`
TranscriptionMode string `json:"transcription_mode"`
TranscriptionEngine string `json:"transcription_engine"`
SkynetURL string `json:"skynet_url"`
WhisperModel string `json:"whisper_model"`
ExternalAPIURL string `json:"external_api_url"`
ExternalAPIKey string `json:"external_api_key"`
ExternalAPIProvider string `json:"external_api_provider"`
AutoStartTranscription bool `json:"auto_start_transcription"`
PostActions MeetPostActions `json:"post_actions"`
}
// PublicMeetPolicy is exposed to authenticated clients (no secrets).
type PublicMeetPolicy struct {
TranscriptionEnabled bool `json:"transcription_enabled"`
TranscriptionMode string `json:"transcription_mode"`
AutoStartTranscription bool `json:"auto_start_transcription"`
}
func defaultMeetPolicy() map[string]any {
return map[string]any{
"transcription_enabled": false,
"transcription_mode": "live",
"transcription_engine": "faster_whisper_local",
"skynet_url": "http://skynet:8000",
"whisper_model": "tiny",
"external_api_url": "",
"external_api_provider": "openai_compatible",
"external_api_key": "",
"auto_start_transcription": false,
"post_actions": map[string]any{
"email_enabled": false,
"email_recipients": "organizer",
"email_custom_addresses": "",
"drive_enabled": true,
"drive_folder_path": "/UltiMeet/Transcripts",
"llm_enabled": false,
"llm_provider_id": "",
"llm_prompt": "Résume cette réunion en français : points clés, décisions et actions à suivre.",
"llm_then_email": true,
"llm_then_drive": true,
},
}
}
func (l *Loader) MeetPolicy(ctx context.Context) (MeetPolicy, error) {
var raw []byte
err := l.db.QueryRow(ctx, `
SELECT settings FROM org_settings WHERE id = $1
`, orgSettingsSingletonID).Scan(&raw)
if err != nil && err != pgx.ErrNoRows {
return MeetPolicy{}, err
}
stored := map[string]any{}
if len(raw) > 0 {
if err := json.Unmarshal(raw, &stored); err != nil {
return MeetPolicy{}, err
}
}
meet, _ := stored["meet"].(map[string]any)
if meet == nil {
meet = defaultMeetPolicy()
}
postRaw, _ := meet["post_actions"].(map[string]any)
if postRaw == nil {
postRaw, _ = defaultMeetPolicy()["post_actions"].(map[string]any)
}
mode, _ := meet["transcription_mode"].(string)
if mode == "" {
mode = "live"
}
engine, _ := meet["transcription_engine"].(string)
if engine == "" {
engine = "faster_whisper_local"
}
skynetURL, _ := meet["skynet_url"].(string)
if skynetURL == "" {
skynetURL = "http://skynet:8000"
}
whisperModel, _ := meet["whisper_model"].(string)
if whisperModel == "" {
whisperModel = "tiny"
}
provider, _ := meet["external_api_provider"].(string)
if provider == "" {
provider = "openai_compatible"
}
driveFolder, _ := postRaw["drive_folder_path"].(string)
if driveFolder == "" {
driveFolder = "/UltiMeet/Transcripts"
}
emailRecipients, _ := postRaw["email_recipients"].(string)
if emailRecipients == "" {
emailRecipients = "organizer"
}
llmPrompt, _ := postRaw["llm_prompt"].(string)
if llmPrompt == "" {
llmPrompt = "Résume cette réunion en français : points clés, décisions et actions à suivre."
}
return MeetPolicy{
TranscriptionEnabled: boolField(meet, "transcription_enabled"),
TranscriptionMode: mode,
TranscriptionEngine: engine,
SkynetURL: skynetURL,
WhisperModel: whisperModel,
ExternalAPIURL: stringValue(meet["external_api_url"]),
ExternalAPIKey: stringValue(meet["external_api_key"]),
ExternalAPIProvider: provider,
AutoStartTranscription: boolField(meet, "auto_start_transcription"),
PostActions: MeetPostActions{
EmailEnabled: boolField(postRaw, "email_enabled"),
EmailRecipients: emailRecipients,
EmailCustomAddresses: stringValue(postRaw["email_custom_addresses"]),
DriveEnabled: boolField(postRaw, "drive_enabled"),
DriveFolderPath: driveFolder,
LLMEnabled: boolField(postRaw, "llm_enabled"),
LLMProviderID: stringValue(postRaw["llm_provider_id"]),
LLMPrompt: llmPrompt,
LLMThenEmail: boolField(postRaw, "llm_then_email"),
LLMThenDrive: boolField(postRaw, "llm_then_drive"),
},
}, nil
}
func (l *Loader) PublicMeetPolicy(ctx context.Context) (PublicMeetPolicy, error) {
policy, err := l.MeetPolicy(ctx)
if err != nil {
return PublicMeetPolicy{}, err
}
return PublicMeetPolicy{
TranscriptionEnabled: policy.TranscriptionEnabled,
TranscriptionMode: policy.TranscriptionMode,
AutoStartTranscription: policy.AutoStartTranscription,
}, nil
}
func (p MeetPolicy) LiveTranscriptionJWT() bool {
return p.TranscriptionEnabled && p.TranscriptionMode == "live"
}