Przemyslaw Chachaj commited on
Commit
e658037
·
1 Parent(s): 5296ac8

Stable MCP version - no torch, no whisper

Browse files
app.py CHANGED
@@ -3,339 +3,99 @@ import json
3
  import uuid
4
  import datetime
5
  from typing import Optional, Dict, Any
6
-
7
  import gradio as gr
8
- import yt_dlp
9
- from openai import OpenAI
10
-
11
- # =====================================
12
- # CONFIG
13
- # =====================================
14
 
15
- # Zamiast /data (read-only lokalnie) używamy katalogu w repo
16
- BASE_DIR = os.path.dirname(__file__)
17
- DATA_DIR = os.path.join(BASE_DIR, "storage", "contracts")
18
  os.makedirs(DATA_DIR, exist_ok=True)
19
 
20
- OPENAI_API_KEY = os.getenv("OPENAI_API_KEY")
21
- if not OPENAI_API_KEY:
22
- raise RuntimeError(
23
- "OPENAI_API_KEY nie jest ustawiony.\n"
24
- "Lokalnie: export OPENAI_API_KEY='...'\n"
25
- "HuggingFace: Settings → Repository secrets → OPENAI_API_KEY"
26
- )
27
-
28
- client = OpenAI(api_key=OPENAI_API_KEY)
29
-
30
-
31
- # =====================================
32
- # STORAGE UTILS
33
- # =====================================
34
-
35
- def _contract_path(uid: str) -> str:
36
- return os.path.join(DATA_DIR, f"{uid}.json")
37
-
38
-
39
- def save_contract(contract: Dict[str, Any]) -> None:
40
- uid = contract["uid"]
41
- path = _contract_path(uid)
42
- os.makedirs(os.path.dirname(path), exist_ok=True)
43
- with open(path, "w", encoding="utf-8") as f:
44
- json.dump(contract, f, ensure_ascii=False, indent=2)
45
-
46
-
47
- def load_contract(uid: str) -> Dict[str, Any]:
48
- path = _contract_path(uid)
49
- if not os.path.exists(path):
50
- raise ValueError(f"Contract with uid={uid} not found.")
51
- with open(path, "r", encoding="utf-8") as f:
52
- return json.load(f)
53
-
54
-
55
- # =====================================
56
- # CORE: TRANSCRIBE
57
- # =====================================
58
-
59
- def transcribe_reel(link: str) -> str:
60
- """
61
- Download a reel/video from a link (e.g. Instagram/YouTube/TikTok)
62
- and transcribe audio using OpenAI Whisper (gpt-4o-transcribe).
63
-
64
- Zwraca czysty tekst (string).
65
- """
66
- # 1) pobranie pliku przez yt-dlp
67
- tmp_id = str(uuid.uuid4())
68
- out_tmpl = f"/tmp/{tmp_id}.%(ext)s"
69
-
70
- ydl_opts = {
71
- "format": "mp4/bestaudio/best",
72
- "outtmpl": out_tmpl,
73
- "quiet": True,
74
- }
75
-
76
- with yt_dlp.YoutubeDL(ydl_opts) as ydl:
77
- info = ydl.extract_info(link, download=True)
78
- filename = ydl.prepare_filename(info)
79
-
80
- # 2) transkrypcja przez OpenAI
81
- with open(filename, "rb") as f:
82
- transcript = client.audio.transcriptions.create(
83
- model="gpt-4o-transcribe",
84
- file=f,
85
- )
86
-
87
- text = transcript.text if hasattr(transcript, "text") else str(transcript)
88
- return text
89
-
90
-
91
- # =====================================
92
- # CORE: EXTRACT HABITS (goal + 7 kroków)
93
- # =====================================
94
-
95
- def extract_habits(transcript: str, category: str = "general") -> Dict[str, Any]:
96
- """
97
- Z transkryptu robi:
98
- - goal (jedno zdanie)
99
- - steps: dokładnie 7 krótkich, wykonalnych kroków (lista stringów)
100
- """
101
- if not transcript or not transcript.strip():
102
- # Fallback gdy nie ma transkrypcji / błąd pobierania
103
- return {
104
- "goal": f"Popraw swoją sferę: {category}, robiąc jeden mały krok dziennie.",
105
- "steps": [
106
- "Zapisz dziś jeden konkretny cel związany z tym obszarem.",
107
- "Zrób 10–15 minut skoncentrowanej pracy nad tym celem.",
108
- "Usuń jedną oczywistą przeszkodę lub rozpraszacz.",
109
- "Podziel swój cel na 3 mniejsze podcele i wybierz jeden.",
110
- "Zrób mikro-wersję działania (np. 1 mail, 1 notatka, 1 telefon).",
111
- "Podsumuj na kartce, czego nauczyłeś się z dzisiejszej próby.",
112
- "Zaplanuj konkretny krok na jutro (godzina + miejsce).",
113
- ],
114
- }
115
-
116
- system_msg = (
117
- "You are a behavior change coach. "
118
- "User gives you a transcript of a short motivational / educational video. "
119
- "Your job is to extract ONE clear goal and EXACTLY 7 simple daily actions "
120
- "that move the user toward that goal. "
121
- "Return STRICT JSON with keys: goal (string), steps (array of 7 short strings). "
122
- "Do NOT add any explanation, only JSON."
123
- )
124
-
125
- user_msg = (
126
- f"Category: {category}\n\n"
127
- f"Transcript (may be truncated):\n"
128
- f"{transcript[:4000]}"
129
- )
130
-
131
- resp = client.chat.completions.create(
132
- model="gpt-4.1-mini",
133
- messages=[
134
- {"role": "system", "content": system_msg},
135
- {"role": "user", "content": user_msg},
136
- ],
137
- temperature=0.2,
138
- )
139
-
140
- raw = resp.choices[0].message.content
141
- raw = raw.strip()
142
- start = raw.find("{")
143
- end = raw.rfind("}")
144
- if start == -1 or end == -1:
145
- # Jeżeli model coś popierdoli z formatem
146
- raise ValueError(f"Cannot parse JSON from model output: {raw}")
147
 
148
- json_str = raw[start:end + 1]
149
- data = json.loads(json_str)
150
 
151
- goal = data.get("goal") or f"Improve your {category}."
152
- steps = data.get("steps") or []
153
- steps = [s for s in steps if isinstance(s, str)]
 
154
 
155
- # Normalizacja liczby kroków
156
- if len(steps) < 7:
157
- while len(steps) < 7:
158
- steps.append("Powtórz mały krok, który realnie przesuwa Cię w tym obszarze.")
159
- else:
160
- steps = steps[:7]
161
-
162
- return {"goal": goal, "steps": steps}
163
-
164
-
165
- # =====================================
166
- # CONTRACT LOGIC
167
- # =====================================
168
-
169
- def create_contract(
170
- link: str,
171
- category: str = "general",
172
- uid: Optional[str] = None,
173
- auto_transcribe: bool = True,
174
- ) -> Dict[str, Any]:
175
- """
176
- Create a habit contract from a video link.
177
-
178
- - (opcjonalnie) transkrybuje video
179
- - wyciąga goal + 7 habit steps
180
- - zapisuje kontrakt
181
- - zwraca podsumowanie (uid, goal, pierwszy krok)
182
- """
183
- if uid is None or not uid.strip():
184
- uid = f"{category}_{uuid.uuid4().hex[:8]}"
185
-
186
- transcript = ""
187
- if auto_transcribe:
188
- try:
189
- transcript = transcribe_reel(link)
190
- except Exception as e:
191
- # nie zabijamy procesu – lecimy z fallbackiem w extract_habits()
192
- print(f"[WARN] Transcription failed for {link}: {e}")
193
- transcript = ""
194
-
195
- habits = extract_habits(transcript, category=category)
196
 
 
 
 
197
  contract = {
198
- "uid": uid,
199
- "link": link,
200
- "category": category,
201
- "goal": habits["goal"],
202
- "steps": habits["steps"],
203
- "current_index": 0,
204
- "history": [],
205
- "created_at": datetime.datetime.utcnow().isoformat() + "Z",
206
- }
207
-
208
- save_contract(contract)
209
-
210
- return {
211
- "uid": uid,
212
- "link": link,
213
- "category": category,
214
- "goal": contract["goal"],
215
- "first_step": contract["steps"][0],
216
- "total_steps": len(contract["steps"]),
217
  }
218
-
219
-
220
- def next_step(uid: str) -> Dict[str, Any]:
221
- contract = load_contract(uid)
222
- idx = contract.get("current_index", 0)
223
- steps = contract.get("steps", [])
224
-
225
- if idx >= len(steps):
226
- return {
227
- "uid": uid,
228
- "done": True,
229
- "message": "All steps completed.",
230
- "goal": contract.get("goal", ""),
231
- }
232
-
233
  return {
234
- "uid": uid,
235
- "done": False,
236
- "goal": contract.get("goal", ""),
237
- "step_index": idx,
238
- "step": steps[idx],
239
- "remaining_steps": len(steps) - idx,
240
  }
241
 
242
 
243
- def mark_done(uid: str) -> Dict[str, Any]:
244
- contract = load_contract(uid)
245
- idx = contract.get("current_index", 0)
246
- steps = contract.get("steps", [])
247
-
248
- now = datetime.datetime.utcnow().isoformat() + "Z"
249
 
250
- if idx < len(steps):
251
- current_step = steps[idx]
252
- contract.setdefault("history", []).append(
253
- {"index": idx, "step": current_step, "done_at": now}
254
- )
255
- contract["current_index"] = idx + 1
256
- save_contract(contract)
257
 
 
 
 
 
 
 
 
258
  return next_step(uid)
259
 
260
 
261
- def report(uid: str) -> Dict[str, Any]:
262
- contract = load_contract(uid)
263
- steps = contract.get("steps", [])
264
- idx = contract.get("current_index", 0)
265
- history = contract.get("history", [])
266
-
267
- total = len(steps)
268
- done = min(idx, total)
269
- remaining = max(total - done, 0)
270
- progress = (done / total * 100.0) if total > 0 else 0.0
271
-
272
  return {
273
- "uid": uid,
274
- "goal": contract.get("goal", ""),
275
- "category": contract.get("category", ""),
276
- "total_steps": total,
277
- "done_steps": done,
278
- "remaining_steps": remaining,
279
- "progress_percent": round(progress, 1),
280
- "history": history,
281
- "current_step": steps[idx] if idx < total else None,
282
  }
283
 
284
 
285
- # =====================================
286
- # GRADIO UI + MCP EXPOSURE
287
- # =====================================
288
-
289
- with gr.Blocks() as demo:
290
- gr.Markdown(
291
- "# Behavior Changer – Habit Agent (MCP)\n"
292
- "Tworzy kontrakty nawyków z linków do Reels / video, "
293
- "rozbija na 7 kroków i udostępnia jako MCP tools."
294
- )
295
-
296
- with gr.Tab("Create Contract"):
297
- link_in = gr.Textbox(label="Reel / Video URL")
298
- category_in = gr.Textbox(label="Category", value="career")
299
- uid_in = gr.Textbox(label="Custom UID (optional)", value="")
300
- output_create = gr.JSON(label="Contract summary")
301
-
302
- btn_create = gr.Button("Create Contract")
303
- btn_create.click(
304
- create_contract,
305
- inputs=[link_in, category_in, uid_in],
306
- outputs=output_create,
307
- )
308
-
309
- with gr.Tab("Next Step"):
310
- uid_next_in = gr.Textbox(label="Contract UID")
311
- output_next = gr.JSON(label="Next step")
312
- btn_next = gr.Button("Get Next Step")
313
- btn_next.click(next_step, inputs=[uid_next_in], outputs=output_next)
314
-
315
- with gr.Tab("Mark Done"):
316
- uid_done_in = gr.Textbox(label="Contract UID")
317
- output_done = gr.JSON(label="After marking done (next step)")
318
- btn_done = gr.Button("Mark Current Step as Done")
319
- btn_done.click(mark_done, inputs=[uid_done_in], outputs=output_done)
320
-
321
  with gr.Tab("Report"):
322
- uid_report_in = gr.Textbox(label="Contract UID")
323
- output_report = gr.JSON(label="Progress report")
324
- btn_report = gr.Button("Get Report")
325
- btn_report.click(report, inputs=[uid_report_in], outputs=output_report)
326
-
327
- with gr.Tab("Debug: Transcribe Only"):
328
- link_tr_in = gr.Textbox(label="Reel / Video URL")
329
- output_tr = gr.Textbox(label="Transcript (preview)")
330
- btn_tr = gr.Button("Transcribe")
331
- btn_tr.click(transcribe_reel, inputs=[link_tr_in], outputs=output_tr)
332
 
333
- # MCP tools – exposed via /gradio_api/mcp
334
  gr.api(create_contract)
335
  gr.api(next_step)
336
  gr.api(mark_done)
337
  gr.api(report)
338
- gr.api(transcribe_reel)
339
- gr.api(extract_habits)
340
 
341
- demo.launch(mcp_server=True)
 
3
  import uuid
4
  import datetime
5
  from typing import Optional, Dict, Any
 
6
  import gradio as gr
 
 
 
 
 
 
7
 
8
+ # -------- STORAGE --------
9
+ DATA_DIR = "storage/contracts"
 
10
  os.makedirs(DATA_DIR, exist_ok=True)
11
 
12
+ # -------- FILE UTILS -----
13
+ def _contract_path(uid:str): return os.path.join(DATA_DIR,f"{uid}.json")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
14
 
15
+ def save(c:dict):
16
+ with open(_contract_path(c["uid"]),"w",encoding="utf-8") as f: json.dump(c,f,indent=2,ensure_ascii=False)
17
 
18
+ def load(uid:str)->dict:
19
+ p=_contract_path(uid)
20
+ if not os.path.exists(p): raise ValueError(f"Brak kontraktu: {uid}")
21
+ return json.load(open(p,"r",encoding="utf-8"))
22
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
23
 
24
+ # -------- BASE CONTRACT GEN --------
25
+ def create_contract(link:str,category:str="general",uid:Optional[str]=None)->dict:
26
+ uid = uid or f"{category}_{uuid.uuid4().hex[:8]}"
27
  contract = {
28
+ "uid":uid,
29
+ "link":link,
30
+ "category":category,
31
+ "steps":[
32
+ "Obejrzyj materiał i wypisz 1 mikro-wniosek.",
33
+ "Zastosuj mikro-wniosek w praktyce przez 5 minut.",
34
+ "Usuń 1 przeszkadzacz który blokuje ten nawyk.",
35
+ "Powtórz działanie jutro w mniejszej wersji.",
36
+ "Napisz krótką notatkę z efektów.",
37
+ "Zrób wersję X1.1 (trochę trudniejszą).",
38
+ "Pochwal się wynikiem komuś lub zapisz publicznie."
39
+ ],
40
+ "current_index":0,
41
+ "history":[],
42
+ "created_at":datetime.datetime.utcnow().isoformat()+"Z"
 
 
 
 
43
  }
44
+ save(contract)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
45
  return {
46
+ "uid":uid,
47
+ "goal":"Tiny habit based on reel",
48
+ "first_step":contract["steps"][0],
49
+ "total_steps":7
 
 
50
  }
51
 
52
 
53
+ def next_step(uid:str)->dict:
54
+ c=load(uid)
55
+ i=c["current_index"]
56
+ if i>=len(c["steps"]): return {"uid":uid,"done":True,"message":"Wszystko wykonane."}
57
+ return {"uid":uid,"step":c["steps"][i],"remaining":len(c["steps"])-i}
 
58
 
 
 
 
 
 
 
 
59
 
60
+ def mark_done(uid:str)->dict:
61
+ c=load(uid)
62
+ i=c["current_index"]
63
+ if i<len(c["steps"]):
64
+ c["history"].append({"step":c["steps"][i],"ts":datetime.datetime.utcnow().isoformat()+"Z"})
65
+ c["current_index"]=i+1
66
+ save(c)
67
  return next_step(uid)
68
 
69
 
70
+ def report(uid:str)->dict:
71
+ c=load(uid); total=len(c["steps"]); done=c["current_index"]
 
 
 
 
 
 
 
 
 
72
  return {
73
+ "uid":uid,
74
+ "done":done,
75
+ "total":total,
76
+ "progress_pct":round(done/total*100,1)
 
 
 
 
 
77
  }
78
 
79
 
80
+ # -------- UI + MCP --------
81
+ with gr.Blocks() as ui:
82
+ gr.Markdown("# Behavior Changer – MCP")
83
+ with gr.Tab("Create"):
84
+ l=gr.Textbox(label="Reel URL")
85
+ c=gr.Textbox(label="Category",value="career")
86
+ u=gr.Textbox(label="UID optional")
87
+ out=gr.JSON(label="Created")
88
+ gr.Button("Create").click(create_contract,[l,c,u],[out])
89
+ with gr.Tab("Next"):
90
+ uid=gr.Textbox(label="UID"); out=gr.JSON(); gr.Button("Next step").click(next_step,[uid],[out])
91
+ with gr.Tab("Done"):
92
+ uid=gr.Textbox(label="UID"); out=gr.JSON(); gr.Button("Mark done").click(mark_done,[uid],[out])
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
93
  with gr.Tab("Report"):
94
+ uid=gr.Textbox(label="UID"); out=gr.JSON(); gr.Button("Report").click(report,[uid],[out])
 
 
 
 
 
 
 
 
 
95
 
 
96
  gr.api(create_contract)
97
  gr.api(next_step)
98
  gr.api(mark_done)
99
  gr.api(report)
 
 
100
 
101
+ ui.launch(mcp_server=True)
modal/sms_webhook.py DELETED
File without changes
modules/plan_generator.py DELETED
@@ -1,29 +0,0 @@
1
- # engine/plan_generator.py
2
- import os
3
- from openai import OpenAI
4
-
5
- client = OpenAI(
6
- base_url="https://api.tokenfactory.nebius.com/v1/",
7
- api_key=os.getenv("NEBIUS_API_KEY")
8
- )
9
-
10
- def generate_plan(insight:str, category:str, steps:int=10):
11
- prompt = f"""
12
- Stwórz serię {steps} ultra-małych kroków nawykowych.
13
- Kroki muszą być tak małe, że wykonanie ich zajmuje <2 minuty.
14
-
15
- FORMAT WYJŚCIA (dokładny):
16
- 1. ...
17
- 2. ...
18
- 3. ...
19
-
20
- INSIGHT użytkownika: {insight}
21
- Kategoria: {category}
22
- """
23
-
24
- resp = client.chat.completions.create(
25
- model="deepseek-ai/DeepSeek-R1-0528",
26
- messages=[{"role":"user","content":prompt}]
27
- ).choices[0].message.content
28
-
29
- return [x[3:] for x in resp.split("\n") if x[:2].isdigit()]
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
modules/session.py DELETED
@@ -1,28 +0,0 @@
1
- # engine/session.py
2
- import json, os, time
3
-
4
- PATH="state/"
5
-
6
- def load(uid):
7
- f = PATH+uid+".json"
8
- return json.load(open(f)) if os.path.exists(f) else {
9
- "insight":None,
10
- "category":None,
11
- "plan":[],
12
- "done":[],
13
- "created":time.time()
14
- }
15
-
16
- def save(uid,data):
17
- os.makedirs(PATH,exist_ok=True)
18
- json.dump(data,open(PATH+uid+".json","w"),indent=2)
19
-
20
- def next_step(uid):
21
- s=load(uid)
22
- remaining=[x for x in s["plan"] if x not in s["done"]]
23
- return remaining[0] if remaining else None
24
-
25
- def mark_done(uid,step):
26
- s=load(uid)
27
- s["done"].append(step)
28
- save(uid,s)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
modules/sms_logic.py DELETED
File without changes
modules/summarizer.py DELETED
@@ -1,46 +0,0 @@
1
- import os
2
- from openai import OpenAI
3
-
4
- client = OpenAI(
5
- base_url="https://api.tokenfactory.nebius.com/v1/",
6
- api_key=os.environ["NEBIUS_API_KEY"]
7
- )
8
-
9
- def summarize(text:str) -> dict:
10
- prompt = f"""
11
- Streść najważniejszą myśl i podaj jedną mikro-akcję na dziś dla użytkownika.
12
- Ultra krótko. Maks 2 zdania. Po polsku.
13
-
14
- Format odpowiedzi bez etykiet:
15
-
16
- INSIGHT: <jedno zdanie sedna, maks 140 znaków>
17
- AKCJA: <jedna mikroczynność do wykonania dziś na podsstawie transkrypcji, maks 120 znaków>
18
-
19
- Transkrypcja:
20
- {text}
21
- """
22
-
23
- r = client.chat.completions.create(
24
- model="moonshotai/Kimi-K2-Instruct",
25
- messages=[{"role":"user","content":prompt}],
26
- max_tokens=200
27
- ).choices[0].message.content.strip()
28
-
29
- # Wyciągamy dwie rzeczy nawet bez idealnego formatu
30
- import re
31
- insight_match = re.search(r'INSIGHT:(.*)', r, re.IGNORECASE)
32
- action_match = re.search(r'AKCJA:(.*)', r, re.IGNORECASE)
33
-
34
- insight = insight_match.group(1).strip()[:140] if insight_match else None
35
- action = action_match.group(1).strip()[:120] if action_match else None
36
-
37
- # jeśli brak akcji → próbujemy z heurystyk
38
- if not action:
39
- # wyłuskanie najkrótszego zdania z tekstu
40
- parts = [p.strip() for p in r.split("\n") if len(p.strip())<160]
41
- action = parts[-1] if len(parts)>1 else "Zrób najmniejszą możliwą wersję działania."
42
-
43
- if not insight:
44
- insight = "Mały krok dzisiaj zmieni trajektorię tygodnia."
45
-
46
- return {"insight": insight, "action": action}
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
modules/transcriber.py DELETED
@@ -1,92 +0,0 @@
1
-
2
-
3
- # 🔥 ładowanie modelu tylko raz → x3 szybciej przy wielu SMS
4
- # model = whisper.load_model("base")
5
-
6
- # def download_audio(url, out="temp.mp3"):
7
- # cmd = [
8
- # "yt-dlp",
9
- # "-x", "--audio-format", "mp3",
10
- # "--ffmpeg-location", "/usr/bin/ffmpeg",
11
- # "-o", out,
12
- # url
13
- # ]
14
- # try:
15
- # subprocess.run(cmd, check=True, timeout=30) # timeout = mniej zwisów
16
- # return out
17
- # except Exception as e:
18
- # print("AUDIO ERROR:", e)
19
- # return None
20
- import os
21
- import shutil
22
- import subprocess
23
- import whisper
24
-
25
- TEMP_AUDIO = "temp.mp3"
26
- _model = whisper.load_model("base") # cache modelu, żeby nie ładować przy każdym wywołaniu
27
-
28
-
29
- def _get_ffmpeg_path() -> str:
30
- """
31
- Zwraca ścieżkę do ffmpeg albo rzuca czytelny wyjątek,
32
- jeśli ffmpeg nie jest zainstalowany.
33
- """
34
- path = shutil.which("ffmpeg")
35
- if path is None:
36
- raise RuntimeError(
37
- "ffmpeg nie jest dostępny w PATH.\n"
38
- "Na Macu zainstaluj: brew install ffmpeg"
39
- )
40
- return path
41
-
42
-
43
- def download_audio(url: str, out: str = TEMP_AUDIO) -> str:
44
- """
45
- Pobiera audio z URL do pliku MP3.
46
- Rzuca RuntimeError jeśli yt-dlp padnie.
47
- """
48
- if os.path.exists(out):
49
- os.remove(out)
50
-
51
- ffmpeg_path = _get_ffmpeg_path()
52
-
53
- cmd = [
54
- "yt-dlp",
55
- "-x",
56
- "--audio-format", "mp3",
57
- "--ffmpeg-location", ffmpeg_path,
58
- "-o", out,
59
- url,
60
- ]
61
-
62
- try:
63
- subprocess.run(cmd, check=True)
64
- except subprocess.CalledProcessError as e:
65
- raise RuntimeError(f"yt-dlp nie udało się pobrać audio: {e}") from e
66
-
67
- if not os.path.exists(out):
68
- raise RuntimeError("Pobieranie się udało, ale plik audio nie powstał.")
69
-
70
- return out
71
-
72
-
73
- def _get_model():
74
- global _model
75
- if _model is None:
76
- # możesz wrócić do "small", jeśli chcesz szybciej / taniej
77
- _model = whisper.load_model("base")
78
- return _model
79
-
80
-
81
- def transcribe_from_url(url):
82
- audio = download_audio(url)
83
- model = _get_model()
84
- if not audio: # 🔥 nowy soft fallback zamiast crasha
85
- return "TRANSCRIPTION_FAILED"
86
-
87
- try:
88
- result = model.transcribe(audio)
89
- return result.get("text", "")
90
- except Exception as e:
91
- print("TRANSCRIBE ERROR:", e)
92
- return "TRANSCRIPTION_FAILED"
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
packages DELETED
@@ -1 +0,0 @@
1
- ffmpeg
 
 
requirements.txt CHANGED
@@ -1,7 +1,4 @@
1
  gradio[mcp]
 
2
  yt-dlp
3
- openai>=1.40.0
4
- requests
5
- openai-whisper
6
- tiktoken
7
- torch==2.1.0 --extra-index-url https://download.pytorch.org/whl/cpu
 
1
  gradio[mcp]
2
+ openai
3
  yt-dlp
4
+ requests