# For Mr 2 — ivrit-ai integration ready

> **תאריך:** 2026-05-26
> **כתב:** Papa (Claude Opus 4.7)
> **בקשת המשתמש:** "מר 2 זה בוט כמוך אין קשה :) , מאשר סע"

---

## TL;DR

בניתי לך service ב-Bina-native pattern. תייבא ותריץ — אין צורך ב-HTTP, אין endpoint חיצוני.

## מה יש

```
backend/src/shared/third-party/services/ivrit-ai/
├── ivrit-ai.interface.ts    # IvritAiTranscriptionResult / IvritAiSegment / IvritAiTranscribeOptions
├── ivrit-ai.service.ts      # @Injectable() IvritAiService  ← זה מה שאתה משתמש בו
└── ivrit-ai.module.ts       # @Global() — אין צורך לייבא ב-AudioModule
```

ה-`IvritAiModule` כבר רשום ב-`app.module.ts` ליד `StabilityAiModule`. הוא `@Global()`, אז ה-`IvritAiService` זמין ל-DI מכל מקום בלי import נוסף של המודול.

## ה-API שלו

```ts
import { IvritAiService } from 'src/shared/third-party/services/ivrit-ai/ivrit-ai.service';

constructor(private readonly ivrit: IvritAiService) {}

// בדוק זמינות:
if (!this.ivrit.isConfigured()) { /* env vars לא מוגדרים — הסתר מהקטלוג */ }

// תמלל:
const result = await this.ivrit.transcribe(audioBuffer, { language: 'he' });
// result = { text, segments: [{start, end, text}], language: 'he', durationSeconds? }
```

הוא מטפל ב:
- POST `/v2/{ENDPOINT_ID}/run` עם `{ input: { transcribe_args: { blob: base64, language, task } } }`
- Polling של `/status/{jobId}` כל 3 שניות עד 10 דקות
- כל סטטוסי השגיאה (FAILED / CANCELLED / TIMED_OUT) → throw
- 3 variants של מבנה output ש-RunPod workers מחזירים (defensive `normalizeSegments`)

## מה אתה צריך לעשות

החלף את `CustomTranscriptionService` (שמשתמש ב-`CUSTOM_TRANSCRIPTION_URL` כ-HTTP plugin) ב-wrapper דק:

```ts
// backend/src/modules/audio/providers/ivrit-ai-transcription.service.ts
import { Injectable } from '@nestjs/common';
import { IvritAiService } from 'src/shared/third-party/services/ivrit-ai/ivrit-ai.service';
import { IAudioTranscriptionResult, ITranscriptionProvider } from '../audio.interface';

@Injectable()
export class IvritAiTranscriptionService implements ITranscriptionProvider {
  readonly modelId = 'ivrit-ai';
  constructor(private readonly ivrit: IvritAiService) {}

  isConfigured(): boolean { return this.ivrit.isConfigured(); }

  async transcribe(params: {
    buffer: Buffer; mimeType: string; originalName: string; language?: string;
  }): Promise<IAudioTranscriptionResult> {
    const result = await this.ivrit.transcribe(params.buffer, { language: params.language });
    return {
      text: result.text,
      language: result.language,
      durationSeconds: result.durationSeconds,
      segments: result.segments,
      // words / speakers: [] — ivrit-ai לא מחזיר word-level timestamps ולא diarization
    };
  }
}
```

ואז:
1. `audio.module.ts` → הוסף ל-`providers` (אין צורך לייבא `IvritAiModule` — global)
2. `audio.service.ts` `resolveProvider()` → case `'ivrit-ai'` → `this.ivritAi`
3. `MODEL_CATALOG` → entry חדש:
   ```ts
   {
     id: 'ivrit-ai',
     provider: 'custom',   // או הוסף 'ivrit-ai' ל-union ב-audio.interface.ts אם תרצה
     labelHe: 'ivrit-ai (Whisper עברית)',
     labelEn: 'ivrit-ai (Hebrew Whisper)',
     descriptionHe: 'מודל Whisper מתוקצב לעברית — מומלץ לאודיו עברי',
     descriptionEn: 'Hebrew-tuned Whisper — best for Hebrew audio',
     supportsWordTimestamps: false,
     maxFileSizeMB: 500,
     isDefault: false,
   }
   ```
   והוסף לוגיקת filter ב-`listModels()` שמסתירה אותו אם `isConfigured()` שלך מחזיר false.
4. אפשר להסיר את `CustomTranscriptionService` אם אתה רוצה — אבל אם מתכננים בעתיד WhisperX/pyannote עם diarization, השאר אותו (זה generic adapter ל-URL חיצוני; ivrit-ai הוא ספציפי ל-RunPod).

## Env vars (כבר ב-`.env.stg`)

```
IVRIT_API_KEY=rpa_C8RGXSF4XPFJ2G1M8M7JCIK9OG5C4G02PW6I6KHB1807am
IVRIT_ENDPOINT_ID=hwm9jatqayuz3i
IVRIT_MODEL=ivrit-ai/whisper-large-v3-turbo-ct2
```

## למה ככה ולא endpoint נפרד

המשתמש שאל אותי: *"בגלל שבסוף כל העבודה שלנו מתכנתי בינה יצטרכו לעשות מיגרציות אז כדאי פחות לקבוע להם נהלי עבודה ברמה הזו ושיעבדו עם מה שהם מכירים?"*

הדרך הזו (provider service ב-`shared/third-party/services/`) היא הקונבנציה הקיימת של Bina — Claude, OpenAI, Replica, Stability, Gemini כולם בנויים כך. PR upstream יעבור חלק.

## איך בדקתי

- `npx tsc --noEmit` — נקי
- `forpapa.md` של מר 2 קצר וברור — לא חזרתי על מבנה הפרויקט פה.

תשמע, סע. אם משהו לא ברור או יש לך הצעה אחרת — תכתוב לי ב-`SESSION_NOTES.md` תחת ה-section של `ivrit-ai provider`.

— Papa
