Compare commits

..

3 Commits

Author SHA1 Message Date
Jacques ROUSSEL 4b03946028 🔐(secrets) bump secretBump secret
In order to use our openai api we need new secret
2024-12-04 10:42:56 +01:00
lebaudantoine 68b919c189 🩹(summary) remove temporary audio files saved
I forgot to remove temporary files downloaded from the Minio bucket.
2024-12-04 10:42:56 +01:00
lebaudantoine c5544e87d6 🔧(summary) use our self-deployed models
Configure dev and staging environment to use our self-deployed
models (Whisper and LLM). Secrets need to be updated btw.

Because of outscale LB bug, which timeout after 60s, we need to
connect directly to the svc.
2024-12-04 10:42:15 +01:00
25 changed files with 107 additions and 353 deletions
+2 -2
View File
@@ -145,7 +145,7 @@ class OIDCLogoutCallbackView(MozillaOIDCOIDCLogoutView):
class OIDCAuthenticationCallbackView(MozillaOIDCAuthenticationCallbackView):
"""Custom callback view for handling the silent login flow."""
"""Custom callback view for handling the silent loging flow."""
@property
def failure_url(self):
@@ -162,7 +162,7 @@ class OIDCAuthenticationCallbackView(MozillaOIDCAuthenticationCallbackView):
class OIDCAuthenticationRequestView(MozillaOIDCAuthenticationRequestView):
"""Custom authentication view for handling the silent login flow."""
"""Custom authentication view for handling the silent loging flow."""
def get_extra_params(self, request):
"""Handle 'prompt' extra parameter for the silent login flow
+2 -4
View File
@@ -324,10 +324,8 @@ class Base(Configuration):
OIDC_AUTHENTICATE_CLASS = "core.authentication.views.OIDCAuthenticationRequestView"
OIDC_CALLBACK_CLASS = "core.authentication.views.OIDCAuthenticationCallbackView"
OIDC_CREATE_USER = values.BooleanValue(
default=True, environ_name="OIDC_CREATE_USER", environ_prefix=None
)
OIDC_VERIFY_SSL = values.BooleanValue(
default=True, environ_name="OIDC_VERIFY_SSL", environ_prefix=None
default=True,
environ_name="OIDC_CREATE_USER",
)
OIDC_FALLBACK_TO_EMAIL_FOR_IDENTIFICATION = values.BooleanValue(
default=False,
+4 -4
View File
@@ -116,10 +116,10 @@ const config: Config = {
'80%': { transform: 'rotate(20deg)' },
'100%': { transform: 'rotate(0)' },
},
pulse_background: {
'0%': { opacity: '1' },
'50%': { opacity: '0.65' },
'100%': { opacity: '1' },
pulse_mic: {
'0%': { color: 'primary', opacity: '1' },
'50%': { color: 'primary', opacity: '0.8' },
'100%': { color: 'primary', opacity: '1' },
},
},
tokens: defineTokens({
@@ -1,6 +0,0 @@
import { useConfig } from '@/api/useConfig.ts'
export const useIsAnalyticsEnabled = () => {
const { data } = useConfig()
return !!data?.analytics?.id
}
@@ -65,11 +65,6 @@ export const Conference = ({
const roomOptions = useMemo((): RoomOptions => {
return {
adaptiveStream: true,
dynacast: true,
publishDefaults: {
videoCodec: 'vp9',
},
videoCaptureDefaults: {
deviceId: userConfig.videoDeviceId ?? undefined,
},
@@ -121,13 +121,7 @@ const OpenFeedback = ({
>
{t('submit')}
</Button>
<Button
invisible
variant="secondary"
size="sm"
fullWidth
onPress={onNext}
>
<Button invisible size="sm" fullWidth onPress={onNext}>
{t('skip')}
</Button>
</VStack>
@@ -1,43 +0,0 @@
import { css } from '@/styled-system/css'
import { RiRecordCircleLine } from '@remixicon/react'
import { Text } from '@/primitives'
import { useTranslation } from 'react-i18next'
import { useRoomContext } from '@livekit/components-react'
export const RecordingStateToast = () => {
const { t } = useTranslation('rooms', { keyPrefix: 'recording' })
const room = useRoomContext()
if (!room?.isRecording) return
return (
<div
className={css({
display: 'flex',
position: 'fixed',
top: '10px',
left: '10px',
paddingY: '0.25rem',
paddingX: '0.25rem 0.35rem',
backgroundColor: 'primaryDark.200',
borderColor: 'primaryDark.400',
border: '1px solid',
color: 'white',
borderRadius: '4px',
gap: '0.5rem',
})}
>
<RiRecordCircleLine
size={20}
className={css({
color: 'white',
backgroundColor: 'danger.700',
padding: '3px',
borderRadius: '3px',
})}
/>
<Text variant={'sm'}>{t('label')}</Text>
</div>
)
}
@@ -10,7 +10,6 @@ import { useSidePanel } from '../hooks/useSidePanel'
import { ReactNode } from 'react'
import { Effects } from './Effects'
import { Chat } from '../prefabs/Chat'
import { Transcript } from './Transcript'
type StyledSidePanelProps = {
title: string
@@ -107,7 +106,6 @@ export const SidePanel = () => {
isEffectsOpen,
isChatOpen,
isSidePanelOpen,
isTranscriptOpen,
} = useSidePanel()
const { t } = useTranslation('rooms', { keyPrefix: 'sidePanel' })
@@ -129,9 +127,6 @@ export const SidePanel = () => {
<Panel isOpen={isChatOpen}>
<Chat />
</Panel>
<Panel isOpen={isTranscriptOpen}>
<Transcript />
</Panel>
</StyledSidePanel>
)
}
@@ -1,100 +0,0 @@
import { Button, Div, H, Text } from '@/primitives'
import thirdSlide from '@/assets/intro-slider/3_resume.png'
import { css } from '@/styled-system/css'
import { useHasTranscriptAccess } from '../hooks/useHasTranscriptAccess'
import { RiRecordCircleLine, RiStopCircleLine } from '@remixicon/react'
import { useRoomId } from '@/features/rooms/livekit/hooks/useRoomId'
import { useRoomContext } from '@livekit/components-react'
import {
RecordingMode,
useStartRecording,
} from '@/features/rooms/api/startRecording'
import { useStopRecording } from '@/features/rooms/api/stopRecording'
import { useEffect, useState } from 'react'
import { RoomEvent } from 'livekit-client'
import { useTranslation } from 'react-i18next'
export const Transcript = () => {
const [isLoading, setIsLoading] = useState(false)
const { t } = useTranslation('rooms', { keyPrefix: 'transcript' })
const hasTranscriptAccess = useHasTranscriptAccess()
const roomId = useRoomId()
const { mutateAsync: startRecordingRoom } = useStartRecording()
const { mutateAsync: stopRecordingRoom } = useStopRecording()
const room = useRoomContext()
useEffect(() => {
const handleRecordingStatusChanged = () => {
setIsLoading(false)
}
room.on(RoomEvent.RecordingStatusChanged, handleRecordingStatusChanged)
return () => {
room.off(RoomEvent.RecordingStatusChanged, handleRecordingStatusChanged)
}
}, [room])
const handleTranscript = async () => {
if (!roomId) {
console.warn('No room ID found')
return
}
try {
setIsLoading(true)
if (room.isRecording) {
await stopRecordingRoom({ id: roomId })
} else {
await startRecordingRoom({ id: roomId, mode: RecordingMode.Transcript })
}
} catch (error) {
console.error('Failed to handle transcript:', error)
setIsLoading(false)
}
}
if (!hasTranscriptAccess) return
return (
<Div
display="flex"
overflowY="scroll"
padding="0 1.5rem"
flexGrow={1}
flexDirection="column"
alignItems="center"
>
<img src={thirdSlide} alt={'wip'} />
{room.isRecording ? (
<>
<H lvl={2}>{t('stop.heading')}</H>
<Text variant="sm" centered wrap="balance">
{t('stop.body')}
</Text>
<div className={css({ height: '2rem' })} />
<Button isDisabled={isLoading} onPress={() => handleTranscript()}>
<RiStopCircleLine style={{ marginRight: '0.5rem' }} />{' '}
{t('stop.button')}
</Button>
</>
) : (
<>
<H lvl={2}>{t('start.heading')}</H>
<Text variant="sm" centered wrap="balance">
{t('start.body')}
</Text>
<div className={css({ height: '2rem' })} />
<Button isDisabled={isLoading} onPress={() => handleTranscript()}>
<RiRecordCircleLine style={{ marginRight: '0.5rem' }} />{' '}
{t('start.button')}
</Button>
</>
)}
</Div>
)
}
@@ -10,6 +10,7 @@ import { DialogState } from './OptionsButton'
import { Separator } from '@/primitives/Separator'
import { useSidePanel } from '../../../hooks/useSidePanel'
import { menuRecipe } from '@/primitives/menuRecipe.ts'
import { TranscriptMenuItem } from './TranscriptMenuItem'
// @todo try refactoring it to use MenuList component
export const OptionsMenuItems = ({
@@ -34,6 +35,7 @@ export const OptionsMenuItems = ({
<RiAccountBoxLine size={20} />
{t('effects')}
</MenuItem>
<TranscriptMenuItem />
</Section>
<Separator />
<Section>
@@ -0,0 +1,71 @@
import { RiRecordCircleLine, RiStopCircleLine } from '@remixicon/react'
import { useTranslation } from 'react-i18next'
import { menuRecipe } from '@/primitives/menuRecipe'
import { MenuItem } from 'react-aria-components'
import {
RecordingMode,
useStartRecording,
} from '@/features/rooms/api/startRecording'
import { useStopRecording } from '@/features/rooms/api/stopRecording'
import { useRoomContext } from '@livekit/components-react'
import { useRoomData } from '@/features/rooms/livekit/hooks/useRoomData'
import { useConfig } from '@/api/useConfig'
export const TranscriptMenuItem = () => {
const { t } = useTranslation('rooms', { keyPrefix: 'options.items' })
const apiRoomData = useRoomData()
const { mutateAsync: startRecordingRoom } = useStartRecording()
const { mutateAsync: stopRecordingRoom } = useStopRecording()
const { data } = useConfig()
const room = useRoomContext()
const handleTranscript = async () => {
const roomId = apiRoomData?.livekit?.room
if (!roomId) {
console.warn('No room ID found')
return
}
try {
if (room.isRecording) {
await stopRecordingRoom({ id: roomId })
} else {
await startRecordingRoom({ id: roomId, mode: RecordingMode.Transcript })
}
} catch (error) {
console.error('Failed to handle transcript:', error)
}
}
if (
!data?.recording?.is_enabled ||
!data?.recording?.available_modes?.includes(RecordingMode.Transcript) ||
!apiRoomData?.is_administrable
) {
return
}
return (
<MenuItem
className={menuRecipe({ icon: true }).item}
onAction={async () => await handleTranscript()}
>
{room.isRecording ? (
<>
<RiRecordCircleLine size={20} />
{t('transcript.stop')}
</>
) : (
<>
<RiStopCircleLine size={20} />
{t('transcript.start')}
</>
)}
</MenuItem>
)
}
@@ -84,12 +84,9 @@ const MicIndicator = ({ participant }: MicIndicatorProps) => {
<RiMicOffFill color={'gray'} />
) : (
<RiMicFill
className={css({
color: isSpeaking ? 'primaryDark.300' : 'primaryDark.50',
animation: isSpeaking
? 'pulse_background 800ms infinite'
: undefined,
})}
style={{
animation: isSpeaking ? 'pulse_mic 800ms infinite' : undefined,
}}
/>
)}
</Button>
@@ -1,37 +0,0 @@
import { ToggleButton } from '@/primitives'
import { RiBardLine } from '@remixicon/react'
import { useTranslation } from 'react-i18next'
import { useSidePanel } from '../../hooks/useSidePanel'
import { useHasTranscriptAccess } from '../../hooks/useHasTranscriptAccess'
import { css } from '@/styled-system/css'
export const TranscriptToggle = () => {
const { t } = useTranslation('rooms', { keyPrefix: 'controls.transcript' })
const { isTranscriptOpen, toggleTranscript } = useSidePanel()
const tooltipLabel = isTranscriptOpen ? 'open' : 'closed'
const hasTranscriptAccess = useHasTranscriptAccess()
if (!hasTranscriptAccess) return
return (
<div
className={css({
position: 'relative',
display: 'inline-block',
})}
>
<ToggleButton
square
variant="primaryTextDark"
aria-label={t(tooltipLabel)}
tooltip={t(tooltipLabel)}
isSelected={isTranscriptOpen}
onPress={() => toggleTranscript()}
>
<RiBardLine />
</ToggleButton>
</div>
)
}
@@ -1,17 +0,0 @@
import { useFeatureFlagEnabled } from 'posthog-js/react'
import { useIsAnalyticsEnabled } from '@/features/analytics/hooks/useIsAnalyticsEnabled'
import { useIsTranscriptEnabled } from './useIsTranscriptEnabled'
import { useIsAdminOrOwner } from './useIsAdminOrOwner'
export const useHasTranscriptAccess = () => {
const featureEnabled = useFeatureFlagEnabled('transcription-summary')
const isAnalyticsEnabled = useIsAnalyticsEnabled()
const isTranscriptEnabled = useIsTranscriptEnabled()
const isAdminOrOwner = useIsAdminOrOwner()
return (
(featureEnabled || !isAnalyticsEnabled) &&
isAdminOrOwner &&
isTranscriptEnabled
)
}
@@ -1,6 +0,0 @@
import { useRoomData } from './useRoomData'
export const useIsAdminOrOwner = () => {
const apiRoomData = useRoomData()
return apiRoomData?.is_administrable
}
@@ -1,11 +0,0 @@
import { RecordingMode } from '@/features/rooms/api/startRecording'
import { useConfig } from '@/api/useConfig'
export const useIsTranscriptEnabled = () => {
const { data } = useConfig()
return (
data?.recording?.is_enabled &&
data?.recording?.available_modes?.includes(RecordingMode.Transcript)
)
}
@@ -1,6 +0,0 @@
import { useRoomData } from './useRoomData'
export const useRoomId = () => {
const apiRoomData = useRoomData()
return apiRoomData?.livekit?.room
}
@@ -5,7 +5,6 @@ export enum PanelId {
PARTICIPANTS = 'participants',
EFFECTS = 'effects',
CHAT = 'chat',
TRANSCRIPT = 'transcript',
}
export const useSidePanel = () => {
@@ -15,7 +14,6 @@ export const useSidePanel = () => {
const isParticipantsOpen = activePanelId == PanelId.PARTICIPANTS
const isEffectsOpen = activePanelId == PanelId.EFFECTS
const isChatOpen = activePanelId == PanelId.CHAT
const isTranscriptOpen = activePanelId == PanelId.TRANSCRIPT
const isSidePanelOpen = !!activePanelId
const toggleParticipants = () => {
@@ -30,20 +28,14 @@ export const useSidePanel = () => {
layoutStore.activePanelId = isEffectsOpen ? null : PanelId.EFFECTS
}
const toggleTranscript = () => {
layoutStore.activePanelId = isTranscriptOpen ? null : PanelId.TRANSCRIPT
}
return {
activePanelId,
toggleParticipants,
toggleChat,
toggleEffects,
toggleTranscript,
isChatOpen,
isParticipantsOpen,
isEffectsOpen,
isSidePanelOpen,
isTranscriptOpen,
}
}
@@ -13,10 +13,8 @@ import { HandToggle } from '../components/controls/HandToggle'
import { SelectToggleDevice } from '../components/controls/SelectToggleDevice'
import { LeaveButton } from '../components/controls/LeaveButton'
import { ScreenShareToggle } from '../components/controls/ScreenShareToggle'
import { SupportToggle } from '../components/controls/SupportToggle'
import { TranscriptToggle } from '../components/controls/TranscriptToggle'
import { css } from '@/styled-system/css'
import { SupportToggle } from '@/features/rooms/livekit/components/controls/SupportToggle.tsx'
/** @public */
export type ControlBarControls = {
@@ -158,7 +156,6 @@ export function ControlBar({
>
<ChatToggle />
<ParticipantsToggle />
<TranscriptToggle />
<SupportToggle />
</div>
</div>
@@ -28,7 +28,6 @@ import { FocusLayout } from '../components/FocusLayout'
import { ParticipantTile } from '../components/ParticipantTile'
import { SidePanel } from '../components/SidePanel'
import { useSidePanel } from '../hooks/useSidePanel'
import { RecordingStateToast } from '../components/RecordingStateToast'
const LayoutWrapper = styled(
'div',
@@ -213,7 +212,6 @@ export function VideoConference({ ...props }: VideoConferenceProps) {
)}
<RoomAudioRenderer />
<ConnectionStateToast />
<RecordingStateToast />
</div>
)
}
+7 -24
View File
@@ -71,10 +71,6 @@
"open": "",
"closed": ""
},
"transcript": {
"open": "",
"closed": ""
},
"support": ""
},
"options": {
@@ -83,7 +79,11 @@
"feedbacks": "",
"settings": "",
"username": "",
"effects": ""
"effects": "",
"transcript": {
"start": "",
"stop": ""
}
}
},
"effects": {
@@ -102,32 +102,18 @@
"heading": {
"participants": "",
"effects": "",
"chat": "",
"transcript": ""
"chat": ""
},
"content": {
"participants": "",
"effects": "",
"chat": "",
"transcript": ""
"chat": ""
},
"closeButton": ""
},
"chat": {
"disclaimer": ""
},
"transcript": {
"start": {
"heading": "",
"body": "",
"button": ""
},
"stop": {
"heading": "",
"body": "",
"button": ""
}
},
"rating": {
"submit": "",
"question": "",
@@ -164,8 +150,5 @@
"raisedHands": "",
"lowerParticipantHand": "",
"lowerParticipantsHand": ""
},
"recording": {
"label": ""
}
}
+7 -24
View File
@@ -70,10 +70,6 @@
"open": "Hide everyone",
"closed": "See everyone"
},
"transcript": {
"open": "Hide AI assistant",
"closed": "Show AI assistant"
},
"support": "Support"
},
"options": {
@@ -82,7 +78,11 @@
"feedbacks": "Give us feedbacks",
"settings": "Settings",
"username": "Update Your Name",
"effects": "Apply effects"
"effects": "Apply effects",
"transcript": {
"start": "Start meeting transcription",
"stop": "Stop ongoing transcription"
}
}
},
"effects": {
@@ -101,32 +101,18 @@
"heading": {
"participants": "Participants",
"effects": "Effects",
"chat": "Messages in the chat",
"transcript": "AI Assistant"
"chat": "Messages in the chat"
},
"content": {
"participants": "participants",
"effects": "effects",
"chat": "messages",
"transcript": "AI assistant"
"chat": "messages"
},
"closeButton": "Hide {{content}}"
},
"chat": {
"disclaimer": "The messages are visible to participants only at the time they are sent. All messages are deleted at the end of the call."
},
"transcript": {
"start": {
"heading": "Start the Assistant!",
"body": "The assistant automatically starts recording your meeting audio (limited to 1 hour). At the end, you'll receive a clear and concise summary of the discussion directly via email.",
"button": "Start"
},
"stop": {
"heading": "Recording in Progress...",
"body": "Your meeting is currently being recorded. You will receive a summary via email once the meeting ends.",
"button": "Stop Recording"
}
},
"rating": {
"submit": "Submit",
"question": "What do you think about the quality of your call?",
@@ -163,8 +149,5 @@
"raisedHands": "Raised hands",
"lowerParticipantHand": "Lower {{name}}'s hand",
"lowerParticipantsHand": "Lower all hands"
},
"recording": {
"label": "Recording"
}
}
+7 -24
View File
@@ -70,10 +70,6 @@
"open": "Masquer les participants",
"closed": "Afficher les participants"
},
"transcript": {
"open": "Masquer l'assistant IA",
"closed": "Afficher l'assistant IA"
},
"support": "Support"
},
"options": {
@@ -82,7 +78,11 @@
"feedbacks": "Partager votre avis",
"settings": "Paramètres",
"username": "Choisir votre nom",
"effects": "Appliquer des effets"
"effects": "Appliquer des effets",
"transcript": {
"start": "Démarrer la transcription",
"stop": "Arrêter la transcription en cours"
}
}
},
"effects": {
@@ -101,32 +101,18 @@
"heading": {
"participants": "Participants",
"effects": "Effets",
"chat": "Messages dans l'appel",
"transcript": "Assistant IA"
"chat": "Messages dans l'appel"
},
"content": {
"participants": "les participants",
"effects": "les effets",
"chat": "les messages",
"transcript": "l'assistant IA"
"chat": "les messages"
},
"closeButton": "Masquer {{content}}"
},
"chat": {
"disclaimer": "Les messages sont visibles par les participants uniquement au moment de\nleur envoi. Tous les messages sont supprimés à la fin de l'appel."
},
"transcript": {
"start": {
"heading": "Démarrer l'assistant !",
"body": "L'assistant démarre automatiquement l'enregistrement sonore de votre réunion (limité à 1h). À la fin, vous recevrez un résumé clair et concis des échanges directement par e-mail.",
"button": "Démarrer"
},
"stop": {
"heading": "Enregistrement en cours …",
"body": "L'enregistrement de votre réunion est en cours. Vous recevrez un compte-rendu par email une fois la réunion terminée.",
"button": "Arrêter l'enregistrement"
}
},
"rating": {
"submit": "Envoyer",
"question": "Que pensez-vous de la qualité de votre appel ?",
@@ -163,8 +149,5 @@
"raisedHands": "Mains levées",
"lowerParticipantHand": "Baisser la main de {{name}}",
"lowerParticipantsHand": "Baisser la main de tous les participants"
},
"recording": {
"label": "Enregistrement"
}
}
-8
View File
@@ -55,14 +55,6 @@ export const text = cva({
textAlign: 'inherit',
},
},
wrap: {
balance: {
textWrap: 'balance',
},
pretty: {
textWrap: 'pretty',
},
},
bold: {
true: {
fontWeight: 'bold',