Spaces:
Running
on
Zero
Running
on
Zero
malvin noel
commited on
Commit
Β·
449544a
1
Parent(s):
51bba63
change script
Browse files- app.py +37 -3
- scripts/generate_scripts.py +11 -16
app.py
CHANGED
@@ -17,6 +17,16 @@ from scripts.generate_subtitles import (
|
|
17 |
add_subtitles_to_video,
|
18 |
)
|
19 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
20 |
# ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
21 |
# Constants & utilities
|
22 |
# ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
@@ -28,6 +38,30 @@ ASSETS_DIRS = (
|
|
28 |
"./assets/video_music",
|
29 |
)
|
30 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
31 |
for d in ASSETS_DIRS:
|
32 |
os.makedirs(d, exist_ok=True)
|
33 |
|
@@ -69,10 +103,10 @@ def cb_generate_script(
|
|
69 |
f"Instruction: {instruction.strip()}\n"
|
70 |
f"π΄ Strict target duration: {target_duration}s β β {approx_words} words (must be respected)."
|
71 |
)
|
72 |
-
script = generate_script(prompt)
|
73 |
|
74 |
-
title = generate_title(script)
|
75 |
-
description = generate_description(script)
|
76 |
return script, title, description, script # last return for state update
|
77 |
|
78 |
|
|
|
17 |
add_subtitles_to_video,
|
18 |
)
|
19 |
|
20 |
+
from __future__ import annotations
|
21 |
+
import os
|
22 |
+
from typing import List
|
23 |
+
import torch
|
24 |
+
from transformers import (
|
25 |
+
AutoTokenizer,
|
26 |
+
AutoModelForCausalLM,
|
27 |
+
BitsAndBytesConfig,
|
28 |
+
)
|
29 |
+
|
30 |
# ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
31 |
# Constants & utilities
|
32 |
# ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
|
|
38 |
"./assets/video_music",
|
39 |
)
|
40 |
|
41 |
+
|
42 |
+
# ββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
43 |
+
# CONFIGURATION
|
44 |
+
# ββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
45 |
+
MODEL_ID = os.getenv("MODEL_ID", "unsloth/Qwen3-30B-A3B")
|
46 |
+
USE_INT8 = os.getenv("USE_INT8", "0") == "1" # set env var to 1 for 8βbit
|
47 |
+
THINKING = os.getenv("DEFAULT_THINKING", "1") == "1" # default reasoning mode
|
48 |
+
|
49 |
+
DTYPE = torch.bfloat16 # Hopper bf16 fastβpath
|
50 |
+
bnb_cfg = BitsAndBytesConfig(load_in_8bit=True) if USE_INT8 else None
|
51 |
+
|
52 |
+
print(f"π Loading {MODEL_ID} ({'8βbit' if USE_INT8 else 'bf16'}) β¦")
|
53 |
+
|
54 |
+
tokenizer = AutoTokenizer.from_pretrained(MODEL_ID, trust_remote_code=True)
|
55 |
+
model = AutoModelForCausalLM.from_pretrained(
|
56 |
+
MODEL_ID,
|
57 |
+
device_map="auto",
|
58 |
+
torch_dtype=(None if USE_INT8 else DTYPE),
|
59 |
+
quantization_config=bnb_cfg,
|
60 |
+
trust_remote_code=True,
|
61 |
+
).eval()
|
62 |
+
DEVICE = model.device
|
63 |
+
print("β
Model ready.")
|
64 |
+
|
65 |
for d in ASSETS_DIRS:
|
66 |
os.makedirs(d, exist_ok=True)
|
67 |
|
|
|
103 |
f"Instruction: {instruction.strip()}\n"
|
104 |
f"π΄ Strict target duration: {target_duration}s β β {approx_words} words (must be respected)."
|
105 |
)
|
106 |
+
script = generate_script(model,tokenizer,prompt)
|
107 |
|
108 |
+
title = generate_title(model,tokenizer,script)
|
109 |
+
description = generate_description(model,tokenizer,script)
|
110 |
return script, title, description, script # last return for state update
|
111 |
|
112 |
|
scripts/generate_scripts.py
CHANGED
@@ -12,14 +12,9 @@ from transformers import AutoModelForCausalLM, AutoTokenizer
|
|
12 |
|
13 |
|
14 |
@spaces.GPU()
|
15 |
-
def generate_local(prompt: str, max_new_tokens: int = 350, temperature: float = 0.7) -> str:
|
16 |
-
model_id = "Qwen/Qwen3-0.6B"
|
17 |
device = torch.device("cuda" if torch.cuda.is_available() else "cpu") # get the device the model is on
|
18 |
-
|
19 |
-
tokenizer = AutoTokenizer.from_pretrained(model_id, trust_remote_code=True)
|
20 |
-
model = AutoModelForCausalLM.from_pretrained(model_id, torch_dtype=torch.float32, trust_remote_code=True).to(device)
|
21 |
inputs = tokenizer(prompt, return_tensors="pt").to(device)
|
22 |
-
|
23 |
output_ids = model.generate(
|
24 |
**inputs,
|
25 |
max_new_tokens=max_new_tokens,
|
@@ -31,7 +26,7 @@ def generate_local(prompt: str, max_new_tokens: int = 350, temperature: float =
|
|
31 |
|
32 |
|
33 |
|
34 |
-
def generate_script(prompt: str, word_count: int = 60) -> str:
|
35 |
system_prompt = (
|
36 |
"You are an expert YouTube scriptwriter. "
|
37 |
"Your job is to write the EXACT words that will be spoken aloud in a video. "
|
@@ -44,15 +39,15 @@ def generate_script(prompt: str, word_count: int = 60) -> str:
|
|
44 |
"- Do NOT include any explanations, labels, or headers. Only output the final spoken script.\n\n"
|
45 |
"Start now:"
|
46 |
)
|
47 |
-
return generate_local(system_prompt)
|
48 |
|
49 |
|
50 |
-
def one_word(query: str) -> str:
|
51 |
prompt_final = (
|
52 |
"Extract only the unique central theme of the following text in English in JSON format like this: "
|
53 |
'{"keyword": "impact"}. Text: ' + query
|
54 |
)
|
55 |
-
result = generate_local(prompt_final, max_new_tokens=30, temperature=0.4)
|
56 |
try:
|
57 |
keyword_json = json.loads(result)
|
58 |
keyword = keyword_json.get("keyword", "")
|
@@ -62,14 +57,14 @@ def one_word(query: str) -> str:
|
|
62 |
return keyword.lower()
|
63 |
|
64 |
|
65 |
-
def generate_title(text: str) -> str:
|
66 |
prompt_final = (
|
67 |
"Generate a unique title for a YouTube Short video that is engaging and informative, "
|
68 |
"maximum 100 characters, without emojis, introduction, or explanation. Content:\n" + text
|
69 |
)
|
70 |
-
return generate_local(prompt_final, max_new_tokens=50, temperature=0.9).strip()
|
71 |
|
72 |
-
def generate_description(text: str) -> str:
|
73 |
prompt_final = (
|
74 |
"Write only the YouTube video description in English:\n"
|
75 |
"1. A compelling opening line.\n"
|
@@ -77,13 +72,13 @@ def generate_description(text: str) -> str:
|
|
77 |
"3. End with 3 relevant hashtags.\n"
|
78 |
"No emojis or introductions. Here is the text:\n" + text
|
79 |
)
|
80 |
-
return generate_local(prompt_final, max_new_tokens=300, temperature=0.7).strip()
|
81 |
|
82 |
-
def generate_tags(text: str) -> list:
|
83 |
prompt_final = (
|
84 |
"List only the important keywords for this YouTube video, separated by commas, "
|
85 |
"maximum 10 keywords. Context: " + text
|
86 |
)
|
87 |
-
result = generate_local(prompt_final, max_new_tokens=100, temperature=0.5)
|
88 |
return [tag.strip() for tag in result.split(",") if tag.strip()]
|
89 |
|
|
|
12 |
|
13 |
|
14 |
@spaces.GPU()
|
15 |
+
def generate_local(model,tokenizer, prompt: str, max_new_tokens: int = 350, temperature: float = 0.7) -> str:
|
|
|
16 |
device = torch.device("cuda" if torch.cuda.is_available() else "cpu") # get the device the model is on
|
|
|
|
|
|
|
17 |
inputs = tokenizer(prompt, return_tensors="pt").to(device)
|
|
|
18 |
output_ids = model.generate(
|
19 |
**inputs,
|
20 |
max_new_tokens=max_new_tokens,
|
|
|
26 |
|
27 |
|
28 |
|
29 |
+
def generate_script(model,tokenizer, prompt: str, word_count: int = 60) -> str:
|
30 |
system_prompt = (
|
31 |
"You are an expert YouTube scriptwriter. "
|
32 |
"Your job is to write the EXACT words that will be spoken aloud in a video. "
|
|
|
39 |
"- Do NOT include any explanations, labels, or headers. Only output the final spoken script.\n\n"
|
40 |
"Start now:"
|
41 |
)
|
42 |
+
return generate_local(model,tokenizer, system_prompt)
|
43 |
|
44 |
|
45 |
+
def one_word(model,tokenizer, query: str) -> str:
|
46 |
prompt_final = (
|
47 |
"Extract only the unique central theme of the following text in English in JSON format like this: "
|
48 |
'{"keyword": "impact"}. Text: ' + query
|
49 |
)
|
50 |
+
result = generate_local(model,tokenizer, prompt_final, max_new_tokens=30, temperature=0.4)
|
51 |
try:
|
52 |
keyword_json = json.loads(result)
|
53 |
keyword = keyword_json.get("keyword", "")
|
|
|
57 |
return keyword.lower()
|
58 |
|
59 |
|
60 |
+
def generate_title(model,tokenizer, text: str) -> str:
|
61 |
prompt_final = (
|
62 |
"Generate a unique title for a YouTube Short video that is engaging and informative, "
|
63 |
"maximum 100 characters, without emojis, introduction, or explanation. Content:\n" + text
|
64 |
)
|
65 |
+
return generate_local(model,tokenizer, prompt_final, max_new_tokens=50, temperature=0.9).strip()
|
66 |
|
67 |
+
def generate_description(model,tokenizer, text: str) -> str:
|
68 |
prompt_final = (
|
69 |
"Write only the YouTube video description in English:\n"
|
70 |
"1. A compelling opening line.\n"
|
|
|
72 |
"3. End with 3 relevant hashtags.\n"
|
73 |
"No emojis or introductions. Here is the text:\n" + text
|
74 |
)
|
75 |
+
return generate_local(model,tokenizer, prompt_final, max_new_tokens=300, temperature=0.7).strip()
|
76 |
|
77 |
+
def generate_tags(model,tokenizer, text: str) -> list:
|
78 |
prompt_final = (
|
79 |
"List only the important keywords for this YouTube video, separated by commas, "
|
80 |
"maximum 10 keywords. Context: " + text
|
81 |
)
|
82 |
+
result = generate_local(model,tokenizer, prompt_final, max_new_tokens=100, temperature=0.5)
|
83 |
return [tag.strip() for tag in result.split(",") if tag.strip()]
|
84 |
|