mirror of
https://github.com/Monadical-SAS/reflector.git
synced 2025-12-20 12:19:06 +00:00
ci: add pre-commit hook and fix linting issues (#545)
* style: deactivate PLC0415 only on part that it's ok + re-run pre-commit run --all * ci: add pre-commit hook * build: move from yarn to pnpm * build: move from yarn to pnpm * build: fix node-version * ci: install pnpm prior node (?) * build: update deps and pnpm trying to fix vercel build * feat: docker www corepack * style: pre-commit --------- Co-authored-by: Igor Loskutov <igor.loskutoff@gmail.com>
This commit is contained in:
24
.github/workflows/pre-commit.yml
vendored
Normal file
24
.github/workflows/pre-commit.yml
vendored
Normal file
@@ -0,0 +1,24 @@
|
||||
name: pre-commit
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
push:
|
||||
branches: [main]
|
||||
|
||||
jobs:
|
||||
pre-commit:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v5
|
||||
- uses: actions/setup-python@v5
|
||||
- uses: pnpm/action-setup@v4
|
||||
with:
|
||||
version: 10
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 22
|
||||
cache: "pnpm"
|
||||
cache-dependency-path: "www/pnpm-lock.yaml"
|
||||
- name: Install dependencies
|
||||
run: cd www && pnpm install --frozen-lockfile
|
||||
- uses: pre-commit/action@v3.0.1
|
||||
@@ -6,7 +6,7 @@ repos:
|
||||
- id: format
|
||||
name: run format
|
||||
language: system
|
||||
entry: bash -c 'cd www && npx prettier --write .'
|
||||
entry: bash -c 'cd www && pnpm format'
|
||||
pass_filenames: false
|
||||
files: ^www/
|
||||
|
||||
|
||||
12
CLAUDE.md
12
CLAUDE.md
@@ -62,7 +62,7 @@ uv run python -m reflector.tools.process path/to/audio.wav
|
||||
**Setup:**
|
||||
```bash
|
||||
# Install dependencies
|
||||
yarn install
|
||||
pnpm install
|
||||
|
||||
# Copy configuration templates
|
||||
cp .env_template .env
|
||||
@@ -72,19 +72,19 @@ cp config-template.ts config.ts
|
||||
**Development:**
|
||||
```bash
|
||||
# Start development server
|
||||
yarn dev
|
||||
pnpm dev
|
||||
|
||||
# Generate TypeScript API client from OpenAPI spec
|
||||
yarn openapi
|
||||
pnpm openapi
|
||||
|
||||
# Lint code
|
||||
yarn lint
|
||||
pnpm lint
|
||||
|
||||
# Format code
|
||||
yarn format
|
||||
pnpm format
|
||||
|
||||
# Build for production
|
||||
yarn build
|
||||
pnpm build
|
||||
```
|
||||
|
||||
### Docker Compose (Full Stack)
|
||||
|
||||
@@ -79,7 +79,7 @@ Start with `cd www`.
|
||||
**Installation**
|
||||
|
||||
```bash
|
||||
yarn install
|
||||
pnpm install
|
||||
cp .env_template .env
|
||||
cp config-template.ts config.ts
|
||||
```
|
||||
@@ -89,7 +89,7 @@ Then, fill in the environment variables in `.env` and the configuration in `conf
|
||||
**Run in development mode**
|
||||
|
||||
```bash
|
||||
yarn dev
|
||||
pnpm dev
|
||||
```
|
||||
|
||||
Then (after completing server setup and starting it) open [http://localhost:3000](http://localhost:3000) to view it in the browser.
|
||||
@@ -99,7 +99,7 @@ Then (after completing server setup and starting it) open [http://localhost:3000
|
||||
To generate the TypeScript files from the openapi.json file, make sure the python server is running, then run:
|
||||
|
||||
```bash
|
||||
yarn openapi
|
||||
pnpm openapi
|
||||
```
|
||||
|
||||
### Backend
|
||||
|
||||
@@ -39,7 +39,7 @@ services:
|
||||
image: node:18
|
||||
ports:
|
||||
- "3000:3000"
|
||||
command: sh -c "yarn install && yarn dev"
|
||||
command: sh -c "corepack enable && pnpm install && pnpm dev"
|
||||
restart: unless-stopped
|
||||
working_dir: /app
|
||||
volumes:
|
||||
|
||||
@@ -5,23 +5,21 @@ Revises: b7df9609542c
|
||||
Create Date: 2025-08-05 19:36:41.740957
|
||||
|
||||
"""
|
||||
|
||||
from typing import Sequence, Union
|
||||
|
||||
from alembic import op
|
||||
import sqlalchemy as sa
|
||||
from alembic import op
|
||||
|
||||
|
||||
revision: str = '0bc0f3ff0111'
|
||||
down_revision: Union[str, None] = 'b7df9609542c'
|
||||
revision: str = "0bc0f3ff0111"
|
||||
down_revision: Union[str, None] = "b7df9609542c"
|
||||
branch_labels: Union[str, Sequence[str], None] = None
|
||||
depends_on: Union[str, Sequence[str], None] = None
|
||||
|
||||
|
||||
def upgrade() -> None:
|
||||
op.add_column('transcript',
|
||||
sa.Column('webvtt', sa.Text(), nullable=True)
|
||||
)
|
||||
op.add_column("transcript", sa.Column("webvtt", sa.Text(), nullable=True))
|
||||
|
||||
|
||||
def downgrade() -> None:
|
||||
op.drop_column('transcript', 'webvtt')
|
||||
op.drop_column("transcript", "webvtt")
|
||||
|
||||
@@ -5,43 +5,42 @@ Revises: 0bc0f3ff0111
|
||||
Create Date: 2025-08-07 11:27:38.473517
|
||||
|
||||
"""
|
||||
|
||||
from typing import Sequence, Union
|
||||
|
||||
from alembic import op
|
||||
import sqlalchemy as sa
|
||||
|
||||
|
||||
revision: str = '116b2f287eab'
|
||||
down_revision: Union[str, None] = '0bc0f3ff0111'
|
||||
revision: str = "116b2f287eab"
|
||||
down_revision: Union[str, None] = "0bc0f3ff0111"
|
||||
branch_labels: Union[str, Sequence[str], None] = None
|
||||
depends_on: Union[str, Sequence[str], None] = None
|
||||
|
||||
|
||||
def upgrade() -> None:
|
||||
conn = op.get_bind()
|
||||
if conn.dialect.name != 'postgresql':
|
||||
if conn.dialect.name != "postgresql":
|
||||
return
|
||||
|
||||
|
||||
op.execute("""
|
||||
ALTER TABLE transcript ADD COLUMN search_vector_en tsvector
|
||||
ALTER TABLE transcript ADD COLUMN search_vector_en tsvector
|
||||
GENERATED ALWAYS AS (
|
||||
setweight(to_tsvector('english', coalesce(title, '')), 'A') ||
|
||||
setweight(to_tsvector('english', coalesce(webvtt, '')), 'B')
|
||||
) STORED
|
||||
""")
|
||||
|
||||
|
||||
op.create_index(
|
||||
'idx_transcript_search_vector_en',
|
||||
'transcript',
|
||||
['search_vector_en'],
|
||||
postgresql_using='gin'
|
||||
"idx_transcript_search_vector_en",
|
||||
"transcript",
|
||||
["search_vector_en"],
|
||||
postgresql_using="gin",
|
||||
)
|
||||
|
||||
|
||||
def downgrade() -> None:
|
||||
conn = op.get_bind()
|
||||
if conn.dialect.name != 'postgresql':
|
||||
if conn.dialect.name != "postgresql":
|
||||
return
|
||||
|
||||
op.drop_index('idx_transcript_search_vector_en', table_name='transcript')
|
||||
op.drop_column('transcript', 'search_vector_en')
|
||||
|
||||
op.drop_index("idx_transcript_search_vector_en", table_name="transcript")
|
||||
op.drop_column("transcript", "search_vector_en")
|
||||
|
||||
@@ -5,17 +5,16 @@ Revises: 116b2f287eab
|
||||
Create Date: 2025-08-11 19:11:01.316947
|
||||
|
||||
"""
|
||||
|
||||
import json
|
||||
from typing import Sequence, Union
|
||||
|
||||
from alembic import op
|
||||
import sqlalchemy as sa
|
||||
from sqlalchemy import text
|
||||
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision: str = '8120ebc75366'
|
||||
down_revision: Union[str, None] = '116b2f287eab'
|
||||
revision: str = "8120ebc75366"
|
||||
down_revision: Union[str, None] = "116b2f287eab"
|
||||
branch_labels: Union[str, Sequence[str], None] = None
|
||||
depends_on: Union[str, Sequence[str], None] = None
|
||||
|
||||
@@ -88,7 +87,7 @@ def upgrade() -> None:
|
||||
# Update the webvtt field
|
||||
connection.execute(
|
||||
text("UPDATE transcript SET webvtt = :webvtt WHERE id = :id"),
|
||||
{"webvtt": webvtt_content, "id": transcript_id}
|
||||
{"webvtt": webvtt_content, "id": transcript_id},
|
||||
)
|
||||
updated_count += 1
|
||||
print(f"✓ Updated transcript {transcript_id}")
|
||||
@@ -104,6 +103,4 @@ def upgrade() -> None:
|
||||
|
||||
def downgrade() -> None:
|
||||
"""Clear WebVTT field for all transcripts."""
|
||||
op.execute(
|
||||
text("UPDATE transcript SET webvtt = NULL")
|
||||
)
|
||||
op.execute(text("UPDATE transcript SET webvtt = NULL"))
|
||||
|
||||
@@ -104,3 +104,7 @@ select = [
|
||||
|
||||
[tool.ruff.lint.per-file-ignores]
|
||||
"reflector/processors/summary/summary_builder.py" = ["E501"]
|
||||
"gpu/**.py" = ["PLC0415"]
|
||||
"reflector/tools/**.py" = ["PLC0415"]
|
||||
"migrations/versions/**.py" = ["PLC0415"]
|
||||
"tests/**.py" = ["PLC0415"]
|
||||
|
||||
@@ -21,7 +21,7 @@ from reflector.db.rooms import rooms
|
||||
from reflector.db.utils import is_postgresql
|
||||
from reflector.processors.types import Word as ProcessorWord
|
||||
from reflector.settings import settings
|
||||
from reflector.storage import get_transcripts_storage, get_recordings_storage
|
||||
from reflector.storage import get_recordings_storage, get_transcripts_storage
|
||||
from reflector.utils import generate_uuid4
|
||||
from reflector.utils.webvtt import topics_to_webvtt
|
||||
|
||||
|
||||
@@ -3,6 +3,7 @@ from reflector.settings import settings
|
||||
|
||||
|
||||
def get_transcripts_storage() -> Storage:
|
||||
assert settings.TRANSCRIPT_STORAGE_BACKEND
|
||||
return Storage.get_instance(
|
||||
name=settings.TRANSCRIPT_STORAGE_BACKEND,
|
||||
settings_prefix="TRANSCRIPT_STORAGE_",
|
||||
|
||||
@@ -44,8 +44,6 @@ def range_requests_response(
|
||||
"""Returns StreamingResponse using Range Requests of a given file"""
|
||||
|
||||
if not os.path.exists(file_path):
|
||||
from fastapi import HTTPException
|
||||
|
||||
raise HTTPException(status_code=404, detail="File not found")
|
||||
|
||||
file_size = os.stat(file_path).st_size
|
||||
|
||||
@@ -26,7 +26,7 @@ async def transcript_record_webrtc(
|
||||
raise HTTPException(status_code=400, detail="Transcript is locked")
|
||||
|
||||
# create a pipeline runner
|
||||
from reflector.pipelines.main_live_pipeline import PipelineMainLive
|
||||
from reflector.pipelines.main_live_pipeline import PipelineMainLive # noqa: PLC0415
|
||||
|
||||
pipeline_runner = PipelineMainLive(transcript_id=transcript_id)
|
||||
|
||||
|
||||
3
www/.gitignore
vendored
3
www/.gitignore
vendored
@@ -44,3 +44,6 @@ config.ts
|
||||
|
||||
# openapi logs
|
||||
openapi-ts-error-*.log
|
||||
|
||||
# pnpm
|
||||
.pnpm-store
|
||||
|
||||
@@ -1,21 +1,16 @@
|
||||
#syntax=docker/dockerfile:1.4
|
||||
FROM node:18-alpine AS base
|
||||
FROM node:22-alpine AS base
|
||||
ENV PNPM_HOME="/pnpm"
|
||||
ENV PATH="$PNPM_HOME:$PATH"
|
||||
RUN corepack enable
|
||||
|
||||
# Install dependencies only when needed
|
||||
FROM base AS deps
|
||||
# Check https://github.com/nodejs/docker-node/tree/b4117f9333da4138b03a546ec926ef50a31506c3#nodealpine to understand why libc6-compat might be needed.
|
||||
RUN apk add --no-cache libc6-compat
|
||||
WORKDIR /app
|
||||
|
||||
# Install dependencies based on the preferred package manager
|
||||
COPY --link package.json yarn.lock* package-lock.json* pnpm-lock.yaml* ./
|
||||
RUN \
|
||||
if [ -f yarn.lock ]; then yarn --frozen-lockfile; \
|
||||
elif [ -f package-lock.json ]; then npm ci; \
|
||||
elif [ -f pnpm-lock.yaml ]; then yarn global add pnpm && pnpm i --frozen-lockfile; \
|
||||
else echo "Lockfile not found." && exit 1; \
|
||||
fi
|
||||
|
||||
COPY --link package.json pnpm-lock.yaml* ./
|
||||
RUN --mount=type=cache,id=pnpm,target=/pnpm/store pnpm install --frozen-lockfile
|
||||
|
||||
# Rebuild the source code only when needed
|
||||
FROM base AS builder
|
||||
@@ -29,7 +24,7 @@ COPY --link . .
|
||||
ENV NEXT_TELEMETRY_DISABLED 1
|
||||
|
||||
# If using npm comment out above and use below instead
|
||||
RUN yarn build
|
||||
RUN pnpm build
|
||||
# RUN npm run build
|
||||
|
||||
# Production image, copy all the files and run next
|
||||
@@ -41,8 +36,8 @@ ENV NODE_ENV production
|
||||
# ENV NEXT_TELEMETRY_DISABLED 1
|
||||
|
||||
RUN \
|
||||
addgroup --system --gid 1001 nodejs; \
|
||||
adduser --system --uid 1001 nextjs
|
||||
addgroup --system --gid 1001 nodejs; \
|
||||
adduser --system --uid 1001 nextjs
|
||||
|
||||
COPY --from=builder --link /app/public ./public
|
||||
|
||||
|
||||
@@ -26,7 +26,7 @@ export default function TranscriptBrowser() {
|
||||
page,
|
||||
selectedSourceKind,
|
||||
selectedRoomId,
|
||||
searchTerm
|
||||
searchTerm,
|
||||
);
|
||||
const userName = useSessionUser().name;
|
||||
const [deletionLoading, setDeletionLoading] = useState(false);
|
||||
@@ -51,7 +51,7 @@ export default function TranscriptBrowser() {
|
||||
|
||||
const handleFilterTranscripts = (
|
||||
sourceKind: SourceKind | null,
|
||||
roomId: string
|
||||
roomId: string,
|
||||
) => {
|
||||
setSelectedSourceKind(sourceKind);
|
||||
setSelectedRoomId(roomId);
|
||||
@@ -107,7 +107,7 @@ export default function TranscriptBrowser() {
|
||||
setDeletionLoading(false);
|
||||
onCloseDeletion();
|
||||
setDeletedItemIds((prev) =>
|
||||
prev ? [...prev, transcriptId] : [transcriptId]
|
||||
prev ? [...prev, transcriptId] : [transcriptId],
|
||||
);
|
||||
})
|
||||
.catch((err) => {
|
||||
@@ -130,7 +130,7 @@ export default function TranscriptBrowser() {
|
||||
if (status === "already running") {
|
||||
setError(
|
||||
new Error("Processing is already running, please wait"),
|
||||
"Processing is already running, please wait"
|
||||
"Processing is already running, please wait",
|
||||
);
|
||||
}
|
||||
})
|
||||
@@ -141,7 +141,7 @@ export default function TranscriptBrowser() {
|
||||
};
|
||||
|
||||
const transcriptToDelete = response?.items?.find(
|
||||
(i) => i.id === transcriptToDeleteId
|
||||
(i) => i.id === transcriptToDeleteId,
|
||||
);
|
||||
const dialogTitle = transcriptToDelete?.title || "Unnamed Transcript";
|
||||
const dialogDate = transcriptToDelete?.created_at
|
||||
|
||||
@@ -165,6 +165,8 @@ const TranscriptCreate = () => {
|
||||
options={supportedLanguages}
|
||||
value={targetLanguage}
|
||||
onChange={onLanguageChange}
|
||||
onBlur={() => {}}
|
||||
onFocus={() => {}}
|
||||
placeholder="Choose your language"
|
||||
/>
|
||||
</Box>
|
||||
|
||||
@@ -136,7 +136,6 @@ export default function Player(props: PlayerProps) {
|
||||
content,
|
||||
drag: false,
|
||||
resize: false,
|
||||
top: 0,
|
||||
});
|
||||
region.on("click", (e) => {
|
||||
e.stopPropagation();
|
||||
|
||||
@@ -10,7 +10,7 @@ import FileUploadButton from "./fileUploadButton";
|
||||
import useWebRTC from "./useWebRTC";
|
||||
import useAudioDevice from "./useAudioDevice";
|
||||
import { Box, Flex, IconButton, Menu, RadioGroup } from "@chakra-ui/react";
|
||||
import { LuScreenShare, LuMic, LuPlay, LuStopCircle } from "react-icons/lu";
|
||||
import { LuScreenShare, LuMic, LuPlay, LuCircleStop } from "react-icons/lu";
|
||||
|
||||
type RecorderProps = {
|
||||
transcriptId: string;
|
||||
@@ -253,7 +253,7 @@ export default function Recorder(props: RecorderProps) {
|
||||
mr={2}
|
||||
onClick={handleRecClick}
|
||||
>
|
||||
{isRecording ? <LuStopCircle /> : <LuPlay />}
|
||||
{isRecording ? <LuCircleStop /> : <LuPlay />}
|
||||
</IconButton>
|
||||
{!isRecording && (window as any).chrome && (
|
||||
<IconButton
|
||||
|
||||
@@ -127,7 +127,7 @@ export default function ShareAndPrivacy(props: ShareAndPrivacyProps) {
|
||||
{isOwner && api && (
|
||||
<Select.Root
|
||||
key={shareMode.value}
|
||||
value={[shareMode.value]}
|
||||
value={[shareMode.value || ""]}
|
||||
onValueChange={(e) => updateShareMode(e.value[0])}
|
||||
disabled={shareLoading}
|
||||
collection={shareOptions}
|
||||
@@ -145,11 +145,7 @@ export default function ShareAndPrivacy(props: ShareAndPrivacyProps) {
|
||||
<Select.Positioner>
|
||||
<Select.Content>
|
||||
{shareOptions.items.map((option) => (
|
||||
<Select.Item
|
||||
key={option.value}
|
||||
item={option}
|
||||
label={option.label}
|
||||
>
|
||||
<Select.Item key={option.value} item={option}>
|
||||
{option.label}
|
||||
<Select.ItemIndicator />
|
||||
</Select.Item>
|
||||
|
||||
@@ -50,7 +50,7 @@ export default function ShareZulip(props: ShareZulipProps & BoxProps) {
|
||||
filter: streamItemsFilter,
|
||||
set: streamItemsSet,
|
||||
} = useListCollection({
|
||||
items: [],
|
||||
initialItems: [] as { label: string; value: string }[],
|
||||
filter: contains,
|
||||
});
|
||||
|
||||
@@ -59,7 +59,7 @@ export default function ShareZulip(props: ShareZulipProps & BoxProps) {
|
||||
filter: topicItemsFilter,
|
||||
set: topicItemsSet,
|
||||
} = useListCollection({
|
||||
items: [],
|
||||
initialItems: [] as { label: string; value: string }[],
|
||||
filter: contains,
|
||||
});
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
"use client";
|
||||
import { useError } from "./errorContext";
|
||||
import { useEffect, useState } from "react";
|
||||
import * as Sentry from "@sentry/react";
|
||||
import * as Sentry from "@sentry/nextjs";
|
||||
|
||||
const ErrorMessage: React.FC = () => {
|
||||
const { error, setError, humanMessage } = useError();
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
"openapi": "openapi-ts"
|
||||
},
|
||||
"dependencies": {
|
||||
"@chakra-ui/react": "^3.22.0",
|
||||
"@chakra-ui/react": "^3.24.2",
|
||||
"@emotion/react": "^11.14.0",
|
||||
"@fortawesome/fontawesome-svg-core": "^6.4.0",
|
||||
"@fortawesome/free-solid-svg-icons": "^6.4.0",
|
||||
@@ -22,9 +22,8 @@
|
||||
"@whereby.com/browser-sdk": "^3.3.4",
|
||||
"autoprefixer": "10.4.20",
|
||||
"axios": "^1.8.2",
|
||||
"chakra-react-select": "^4.9.1",
|
||||
"eslint": "^9.9.1",
|
||||
"eslint-config-next": "^14.2.7",
|
||||
"eslint": "^9.33.0",
|
||||
"eslint-config-next": "^14.2.31",
|
||||
"fontawesome": "^5.6.3",
|
||||
"ioredis": "^5.4.1",
|
||||
"jest-worker": "^29.6.2",
|
||||
@@ -44,8 +43,6 @@
|
||||
"redlock": "^5.0.0-beta.2",
|
||||
"sass": "^1.63.6",
|
||||
"simple-peer": "^9.11.1",
|
||||
"superagent": "^8.0.9",
|
||||
"supports-color": "^9.4.0",
|
||||
"tailwindcss": "^3.3.2",
|
||||
"typescript": "^5.1.6",
|
||||
"wavesurfer.js": "^7.4.2"
|
||||
@@ -59,5 +56,6 @@
|
||||
"@types/react": "18.2.20",
|
||||
"prettier": "^3.0.0",
|
||||
"vercel": "^37.3.0"
|
||||
}
|
||||
},
|
||||
"packageManager": "pnpm@10.14.0+sha512.ad27a79641b49c3e481a16a805baa71817a04bbe06a38d17e60e2eaee83f6a146c6a688125f5792e48dd5ba30e7da52a5cda4c3992b9ccf333f9ce223af84748"
|
||||
}
|
||||
|
||||
12969
www/pnpm-lock.yaml
generated
Normal file
12969
www/pnpm-lock.yaml
generated
Normal file
File diff suppressed because it is too large
Load Diff
@@ -9,8 +9,8 @@
|
||||
"incremental": true,
|
||||
"esModuleInterop": true,
|
||||
"target": "esnext",
|
||||
"module": "CommonJS",
|
||||
"moduleResolution": "node",
|
||||
"module": "esnext",
|
||||
"moduleResolution": "bundler",
|
||||
"resolveJsonModule": true,
|
||||
"isolatedModules": true,
|
||||
"jsx": "preserve",
|
||||
|
||||
7716
www/yarn.lock
7716
www/yarn.lock
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user