Compare commits

...

10 Commits

Author SHA1 Message Date
Florent Chehab 4a1a04f86e 🔖(minor) bump release to 1.23.0 2026-07-08 10:07:09 +02:00
Florent Chehab 8702638cf3 (release) uv lock agents
In release script also run uv lock for agents.
2026-07-08 10:06:53 +02:00
Florent Chehab f88c0307ea 🐛(summary) do not save null emails in analytics
In case the email is not provided we should not update
the analytics email value.
2026-07-08 02:09:30 +02:00
Florent Chehab e7f15b50ff (summary) more precise analytics events
* Specific transcript and summary events
* Improve observability on summary tasks
2026-07-08 02:09:30 +02:00
snyk-bot 75ba2ff146 🔒️(frontend) update docker image to nginx-unprivileged:1.30.3-alpine3.23
The following vulnerabilities are fixed with an upgrade:
- https://snyk.io/vuln/SNYK-ALPINE323-EXPAT-17675120
- https://snyk.io/vuln/SNYK-ALPINE323-EXPAT-17675112
- https://snyk.io/vuln/SNYK-ALPINE323-EXPAT-17675114
- https://snyk.io/vuln/SNYK-ALPINE323-EXPAT-17675124
- https://snyk.io/vuln/SNYK-ALPINE323-EXPAT-17675125
2026-07-08 01:48:59 +02:00
lebaudantoine bbe2a32efc ⬆️(frontend) update the frontend build image to Node 22
Bump the Node.js version used in the frontend build image from
Node 20 to Node 22.
2026-07-08 01:39:20 +02:00
snyk-bot 86fff16eed ⬆️(frontend) upgrade @tanstack/react-query from 5.100.14 to 5.101.0
Snyk has created this PR to upgrade @tanstack/react-query from 5.100.14 to 5.101.0.
2026-07-08 01:02:50 +02:00
snyk-bot 195d2b5006 ⬆️(frontend) upgrade posthog-js from 1.386.5 to 1.387.0
Snyk has created this PR to upgrade posthog-js from 1.386.5 to 1.387.0.
2026-07-08 00:56:41 +02:00
snyk-bot 30cf264276 ⬆️(frontend) upgrade livekit-client from 2.19.0 to 2.19.2
Snyk has created this PR to upgrade livekit-client from 2.19.0 to 2.19.2.
2026-07-08 00:48:27 +02:00
lebaudantoine 945d779b45 ♻️(frontend) wrap MuteEveryoneButton with AdminOrOwnerOnly
Avoids mounting the button's hooks and rendering its logic for users who
are not admin or owner, which previously happened on every re-render
of the parent.
2026-07-07 19:11:41 +02:00
20 changed files with 123 additions and 73 deletions
+9
View File
@@ -8,6 +8,8 @@ and this project adheres to
## [Unreleased]
## [1.23.0] - 2026-07-08
### Added
- ✨(backend) extend analytics module to support feature flags
@@ -22,6 +24,13 @@ and this project adheres to
- ✨(meet) use compatible with summary v2 #1362
- ♻️(backend) refactor analytics backend from Protocol to abstract class
- 🔥(summary) remove call to summary enabled feature flag
- ♻️(frontend) wrap MuteEveryoneButton with AdminOrOwnerOnly
- ⬆️(frontend) upgrade livekit-client from 2.19.0 to 2.19.2
- ⬆️(frontend) upgrade posthog-js from 1.386.5 to 1.387.0
- ⬆️(frontend) upgrade @tanstack/react-query from 5.100.14 to 5.101.0
- ⬆️(frontend) update the frontend build image to Node 22
- 🔒️(frontend) update docker image to nginx-unprivileged:1.30.3-alpine3.23
- ✨(summary) more precise analytics events
### Fixed
+6
View File
@@ -113,6 +113,12 @@ update_python_version "summary"
# Update agents pyproject.toml
update_python_version "agents"
# Run uv lock in agents
print_info "Running uv lock in agents..."
cd "src/agents"
uv lock
cd -
# Update CHANGELOG
print_info "Updating CHANGELOG..."
+2 -2
View File
@@ -1,5 +1,5 @@
# ---- Front-end image ----
FROM node:20-alpine AS frontend-deps
FROM node:22-alpine AS frontend-deps
WORKDIR /home/frontend/
@@ -54,7 +54,7 @@ RUN npx webpack --mode production
# ---- Front-end image ----
FROM nginxinc/nginx-unprivileged:alpine3.23 AS frontend-production
FROM nginxinc/nginx-unprivileged:1.30.3-alpine3.23 AS frontend-production
USER root
+1 -1
View File
@@ -1,7 +1,7 @@
[project]
name = "agents"
version = "1.22.0"
version = "1.23.0"
requires-python = ">=3.12"
dependencies = [
"livekit-agents==1.6.4",
+1 -1
View File
@@ -9,7 +9,7 @@ resolution-markers = [
[[package]]
name = "agents"
version = "1.22.0"
version = "1.23.0"
source = { virtual = "." }
dependencies = [
{ name = "livekit-agents" },
+1 -1
View File
@@ -7,7 +7,7 @@ build-backend = "uv_build"
[project]
name = "meet"
version = "1.22.0"
version = "1.23.0"
authors = [{ "name" = "DINUM", "email" = "dev@mail.numerique.gouv.fr" }]
classifiers = [
"Development Status :: 5 - Production/Stable",
+1 -1
View File
@@ -1187,7 +1187,7 @@ wheels = [
[[package]]
name = "meet"
version = "1.22.0"
version = "1.23.0"
source = { editable = "." }
dependencies = [
{ name = "aiohttp" },
+2 -2
View File
@@ -1,4 +1,4 @@
FROM node:20-alpine AS frontend-deps
FROM node:22-alpine AS frontend-deps
USER node
@@ -39,7 +39,7 @@ ENV VITE_API_BASE_URL=${VITE_API_BASE_URL}
RUN npm run build
# ---- Front-end image ----
FROM nginxinc/nginx-unprivileged:alpine3.23 AS frontend-production
FROM nginxinc/nginx-unprivileged:1.30.3-alpine3.23 AS frontend-production
USER root
+20 -20
View File
@@ -1,12 +1,12 @@
{
"name": "meet",
"version": "1.22.0",
"version": "1.23.0",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "meet",
"version": "1.22.0",
"version": "1.23.0",
"dependencies": {
"@fontsource-variable/atkinson-hyperlegible-next": "5.2.6",
"@fontsource-variable/lexend": "5.2.11",
@@ -18,7 +18,7 @@
"@pandacss/preset-panda": "1.11.3",
"@react-types/overlays": "3.10.0",
"@remixicon/react": "4.9.0",
"@tanstack/react-query": "5.100.14",
"@tanstack/react-query": "5.101.0",
"@timephy/rnnoise-wasm": "1.0.0",
"crisp-sdk-web": "1.1.2",
"derive-valtio": "0.2.0",
@@ -28,8 +28,8 @@
"i18next-browser-languagedetector": "8.2.1",
"i18next-parser": "9.4.0",
"i18next-resources-to-backend": "1.2.1",
"livekit-client": "2.19.0",
"posthog-js": "1.386.5",
"livekit-client": "2.19.2",
"posthog-js": "1.387.0",
"react": "18.3.1",
"react-aria": "3.49.0",
"react-aria-components": "1.18.0",
@@ -2357,9 +2357,9 @@
}
},
"node_modules/@tanstack/query-core": {
"version": "5.100.14",
"resolved": "https://registry.npmjs.org/@tanstack/query-core/-/query-core-5.100.14.tgz",
"integrity": "sha512-5X41dGpxgeaHISCRW2oYwcSycZeULZzAunaudXT9ov1KOTj9xwt0CH6hbwqP1/z74ZWF7rYFnDpyYH07XFcZew==",
"version": "5.101.0",
"resolved": "https://registry.npmjs.org/@tanstack/query-core/-/query-core-5.101.0.tgz",
"integrity": "sha512-cQetA74EB+seWySv1TTKr828TnP0u39m6LykwDXIo84SNortpDkp30TMEjkqtYCNP9c40uT/iwl6MLiufEt0Ow==",
"license": "MIT",
"funding": {
"type": "github",
@@ -2378,12 +2378,12 @@
}
},
"node_modules/@tanstack/react-query": {
"version": "5.100.14",
"resolved": "https://registry.npmjs.org/@tanstack/react-query/-/react-query-5.100.14.tgz",
"integrity": "sha512-oOr6aRdSFEwWhzxEkD/9ZcItM3+LjBSkeVmadWKwUssAHTsqd/7bOjWrX4AbvEkoEhgAxzN0Xk6H/aYzXiYBAw==",
"version": "5.101.0",
"resolved": "https://registry.npmjs.org/@tanstack/react-query/-/react-query-5.101.0.tgz",
"integrity": "sha512-rLlJXSpkqfizLWgkR5+eLeIk0MvTx/meEIR7LRjxic+qxiQP8zVjq7BqQkiCMNLQBlLfuOLqqr6KO5GtrDlmSg==",
"license": "MIT",
"dependencies": {
"@tanstack/query-core": "5.100.14"
"@tanstack/query-core": "5.101.0"
},
"funding": {
"type": "github",
@@ -8035,9 +8035,9 @@
"license": "MIT"
},
"node_modules/livekit-client": {
"version": "2.19.0",
"resolved": "https://registry.npmjs.org/livekit-client/-/livekit-client-2.19.0.tgz",
"integrity": "sha512-aolY1XDAtx0nHKBNm29W9OhzBnSz1CP5kq3phvRhFfi1NbvMXs8tcACjAkZTnIKgihkp+BiJScZZ3tZv0Gz8sA==",
"version": "2.19.2",
"resolved": "https://registry.npmjs.org/livekit-client/-/livekit-client-2.19.2.tgz",
"integrity": "sha512-Kvk07QYDWRAbmYNLRll04ZIuxMQobW/oLPYnmR1kCy8GGHpU0gqyHf704Rz+29zfy8IJZRjKqeVbzGSKn9sumw==",
"license": "Apache-2.0",
"dependencies": {
"@livekit/mutex": "1.1.1",
@@ -9033,13 +9033,13 @@
"license": "MIT"
},
"node_modules/posthog-js": {
"version": "1.386.5",
"resolved": "https://registry.npmjs.org/posthog-js/-/posthog-js-1.386.5.tgz",
"integrity": "sha512-ASejQQf5Xw0XolMwH/KCLZlZtoyLK6VsvORwGagAtfa8/ElIOF76BMQspkDsRTybEI+uzHqRDm2m/na1Dki2mA==",
"version": "1.387.0",
"resolved": "https://registry.npmjs.org/posthog-js/-/posthog-js-1.387.0.tgz",
"integrity": "sha512-Pv1jUMySMN62zoAxdJBJPV8n62lkHdjuWhpeU7izczc5Dqbx3hhqO2hkrNTI8Yx1ezmWk2qUHZs03FuOBubdFQ==",
"license": "SEE LICENSE IN LICENSE",
"dependencies": {
"@posthog/core": "^1.32.3",
"@posthog/types": "^1.386.3",
"@posthog/core": "^1.33.0",
"@posthog/types": "^1.387.0",
"core-js": "^3.38.1",
"dompurify": "^3.3.2",
"fflate": "^0.4.8",
+4 -4
View File
@@ -1,7 +1,7 @@
{
"name": "meet",
"private": true,
"version": "1.22.0",
"version": "1.23.0",
"type": "module",
"scripts": {
"dev": "panda codegen && vite",
@@ -25,7 +25,7 @@
"@pandacss/preset-panda": "1.11.3",
"@react-types/overlays": "3.10.0",
"@remixicon/react": "4.9.0",
"@tanstack/react-query": "5.100.14",
"@tanstack/react-query": "5.101.0",
"@timephy/rnnoise-wasm": "1.0.0",
"crisp-sdk-web": "1.1.2",
"derive-valtio": "0.2.0",
@@ -35,8 +35,8 @@
"i18next-browser-languagedetector": "8.2.1",
"i18next-parser": "9.4.0",
"i18next-resources-to-backend": "1.2.1",
"livekit-client": "2.19.0",
"posthog-js": "1.386.5",
"livekit-client": "2.19.2",
"posthog-js": "1.387.0",
"react": "18.3.1",
"react-aria": "3.49.0",
"react-aria-components": "1.18.0",
@@ -1,23 +1,20 @@
import { Button } from '@/primitives'
import { useTranslation } from 'react-i18next'
import type { Participant } from 'livekit-client'
import { useIsAdminOrOwner } from '@/features/rooms/livekit/hooks/useIsAdminOrOwner'
import { useMuteParticipants } from '@/features/rooms/api/muteParticipants'
import { RiMicOffLine } from '@remixicon/react'
import { css } from '@/styled-system/css'
import { AdminOrOwnerOnly } from '@/features/rooms/components/AdminOrOwnerOnly'
type MuteEveryoneButtonProps = {
participants: Array<Participant>
}
export const MuteEveryoneButton = ({
participants,
}: MuteEveryoneButtonProps) => {
const MuteEveryoneButtonInner = ({ participants }: MuteEveryoneButtonProps) => {
const { muteParticipants } = useMuteParticipants()
const { t } = useTranslation('rooms')
const isAdminOrOwner = useIsAdminOrOwner()
if (!isAdminOrOwner || !participants.length) return null
if (!participants.length) return null
return (
<Button
@@ -36,3 +33,13 @@ export const MuteEveryoneButton = ({
</Button>
)
}
export const MuteEveryoneButton = ({
participants,
}: MuteEveryoneButtonProps) => {
return (
<AdminOrOwnerOnly>
<MuteEveryoneButtonInner participants={participants} />
</AdminOrOwnerOnly>
)
}
+2 -2
View File
@@ -1,12 +1,12 @@
{
"name": "mail_mjml",
"version": "1.22.0",
"version": "1.23.0",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "mail_mjml",
"version": "1.22.0",
"version": "1.23.0",
"license": "MIT",
"dependencies": {
"@html-to/text-cli": "0.5.4",
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "mail_mjml",
"version": "1.22.0",
"version": "1.23.0",
"description": "An util to generate html and text django's templates from mjml templates",
"type": "module",
"dependencies": {
+2 -2
View File
@@ -1,12 +1,12 @@
{
"name": "sdk",
"version": "1.22.0",
"version": "1.23.0",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "sdk",
"version": "1.22.0",
"version": "1.23.0",
"license": "ISC",
"workspaces": [
"./library",
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "sdk",
"version": "1.22.0",
"version": "1.23.0",
"author": "",
"license": "ISC",
"description": "",
+1 -1
View File
@@ -1,7 +1,7 @@
[project]
name = "summary"
version = "1.22.0"
version = "1.23.0"
dependencies = [
"fastapi[standard]>=0.105.0",
"uvicorn>=0.24.0",
+13 -14
View File
@@ -59,17 +59,16 @@ async def create_transcribe_task_v2(
]
)
properties = {}
if request.user_email:
properties["$set"] = {"email": request.user_email}
# We track the request, this also properly initializes the user in the
# analytics system, so that later feature flags work properly
analytics.capture(
settings.posthog_event_request,
settings.posthog_transcript_request,
request.user_sub,
properties={
"kind": "transcribe",
"$set": {
"email": request.user_email,
},
},
properties=properties,
)
return TranscribeWebhookPendingPayload(job_id=task.id).model_dump()
@@ -93,15 +92,15 @@ async def create_summarize_task_v2(
# We track the request, this also properly initializes the user in the
# analytics system, so that later feature flags work properly
properties = {}
if request.user_email:
properties["$set"] = {"email": request.user_email}
analytics.capture(
settings.posthog_event_request,
settings.posthog_summary_request,
request.user_sub,
properties={
"kind": "summarize",
"$set": {
"email": request.user_email,
},
},
properties=properties,
)
return SummarizeWebhookPendingPayload(job_id=task.id).model_dump()
+11 -6
View File
@@ -11,7 +11,7 @@ from celery.utils.log import get_task_logger
from posthog import Posthog
from summary.core.config import get_settings
from summary.core.models import TranscribeTaskJob
from summary.core.models import SummarizeTaskJob, TranscribeTaskJob
logger = get_task_logger(__name__)
settings = get_settings()
@@ -109,19 +109,15 @@ class MetadataManager:
"""Check if task_id exists in tasks metadata cache."""
return self._redis.exists(self._get_redis_key(task_id))
def create(self, task_id: str, task_payload: TranscribeTaskJob):
def create(self, task_id: str, task_payload: TranscribeTaskJob | SummarizeTaskJob):
"""Create initial metadata entry for a new task."""
if self._is_disabled or self.has_task_id(task_id):
return
start_time = time.time()
parts = urlsplit(task_payload.cloud_storage_url)
clean_url = urlunsplit((parts.scheme, parts.netloc, parts.path, "", ""))
initial_metadata = {
"start_time": start_time,
"asr_model": settings.whisperx_asr_model,
"retries": 0,
"filename": clean_url,
"sub": task_payload.user_sub,
# avoid None in redis, it shouldn't happen anyway in prod
"email": task_payload.user_email or "",
@@ -129,6 +125,15 @@ class MetadataManager:
"queuing_time": round(start_time - task_payload.received_at.timestamp(), 2),
}
if isinstance(task_payload, TranscribeTaskJob):
parts = urlsplit(task_payload.cloud_storage_url)
clean_url = urlunsplit((parts.scheme, parts.netloc, parts.path, "", ""))
initial_metadata["source_url"] = clean_url
initial_metadata["asr_model"] = settings.whisperx_asr_model
elif isinstance(task_payload, SummarizeTaskJob):
initial_metadata["content_length"] = len(task_payload.content)
initial_metadata["llm_model"] = settings.llm_model
self._save_metadata(task_id, initial_metadata)
def retry(self, task_id):
+26 -5
View File
@@ -542,28 +542,28 @@ def process_audio_transcribe_v2_task(
call_webhook_v2_task.apply_async(
args=[success_payload.model_dump(), payload.tenant_id]
)
metadata_manager.capture(job_id, settings.posthog_event_success)
metadata_manager.capture(job_id, settings.posthog_transcript_success)
return success_payload.model_dump()
@signals.task_prerun.connect(sender=process_audio_transcribe_v2_task)
def task_started(task_id=None, task=None, args=None, **kwargs):
def task_started_transcript(task_id=None, task=None, args=None, **kwargs):
"""Signal handler called before task execution begins."""
if args:
metadata_manager.create(task_id, TranscribeTaskJob.model_validate(args[0]))
@signals.task_retry.connect(sender=process_audio_transcribe_v2_task)
def task_retry_handler(request=None, reason=None, einfo=None, **kwargs):
def task_retry_handler_transcript(request=None, reason=None, einfo=None, **kwargs):
"""Signal handler called when task execution retries."""
metadata_manager.retry(request.id)
@signals.task_failure.connect(sender=process_audio_transcribe_v2_task)
def task_failure_handler(task_id, exception=None, **kwargs):
def task_failure_handler_transcript(task_id, exception=None, **kwargs):
"""Signal handler called when task execution fails permanently."""
metadata_manager.capture(task_id, settings.posthog_event_failure)
metadata_manager.capture(task_id, settings.posthog_transcript_failure)
@signals.task_failure.connect(sender=process_audio_transcribe_v2_task)
@@ -648,9 +648,30 @@ def summarize_v2_task(
call_webhook_v2_task.apply_async(
args=[success_payload.model_dump(), payload.tenant_id]
)
metadata_manager.capture(job_id, settings.posthog_summary_success)
return success_payload.model_dump()
@signals.task_prerun.connect(sender=summarize_v2_task)
def task_started_summary(task_id=None, task=None, args=None, **kwargs):
"""Signal handler called before task execution begins."""
if args:
metadata_manager.create(task_id, SummarizeTaskJob.model_validate(args[0]))
@signals.task_retry.connect(sender=summarize_v2_task)
def task_retry_handler_summary(request=None, reason=None, einfo=None, **kwargs):
"""Signal handler called when task execution retries."""
metadata_manager.retry(request.id)
@signals.task_failure.connect(sender=summarize_v2_task)
def task_failure_handler_summary(task_id, exception=None, **kwargs):
"""Signal handler called when task execution fails permanently."""
metadata_manager.capture(task_id, settings.posthog_summary_failure)
@signals.task_failure.connect(sender=summarize_v2_task)
def handle_summarize_v2_failed(
sender,
+6 -3
View File
@@ -130,9 +130,12 @@ class Settings(BaseSettings):
posthog_enabled: bool = False
posthog_api_key: Optional[str] = None
posthog_api_host: Optional[str] = "https://eu.i.posthog.com"
posthog_event_failure: str = "transcript-failure"
posthog_event_success: str = "transcript-success"
posthog_event_request: str = "transcript-request"
posthog_transcript_request: str = "transcript-request"
posthog_transcript_failure: str = "transcript-failure"
posthog_transcript_success: str = "transcript-success"
posthog_summary_request: str = "summary-request"
posthog_summary_failure: str = "summary-failure"
posthog_summary_success: str = "summary-success"
# Langfuse (LLM Observability)
langfuse_enabled: bool = False