Spaces:
Running
Running
Update app.py
Browse files
app.py
CHANGED
|
@@ -44,6 +44,58 @@ if not FIREWORKS_API_KEY:
|
|
| 44 |
# ๊ธ๋ก๋ฒ ๋ณ์
|
| 45 |
db_lock = threading.Lock()
|
| 46 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 47 |
# ์ฅ๋ฅด ํ
ํ๋ฆฟ
|
| 48 |
GENRE_TEMPLATES = {
|
| 49 |
"์ก์
": {
|
|
@@ -120,41 +172,60 @@ GENRE_TEMPLATES = {
|
|
| 120 |
}
|
| 121 |
}
|
| 122 |
|
| 123 |
-
#
|
| 124 |
PLANNING_STAGES = [
|
| 125 |
-
("
|
| 126 |
-
("
|
| 127 |
-
("
|
| 128 |
-
("
|
| 129 |
-
("
|
| 130 |
]
|
| 131 |
|
|
|
|
| 132 |
WRITING_STAGES = [
|
| 133 |
-
("1๋ง ์ด๊ณ ", "โ๏ธ 1๋ง
|
| 134 |
-
("1๋ง
|
| 135 |
-
("1๋ง
|
| 136 |
-
("1๋ง
|
|
|
|
|
|
|
| 137 |
|
| 138 |
-
("2๋งA ์ด๊ณ ", "โ๏ธ 2๋งA
|
| 139 |
-
("2๋งA
|
| 140 |
-
("2๋งA
|
| 141 |
-
("2๋งA
|
|
|
|
|
|
|
| 142 |
|
| 143 |
-
("2๋งB ์ด๊ณ ", "โ๏ธ 2๋งB
|
| 144 |
-
("2๋งB
|
| 145 |
-
("2๋งB
|
| 146 |
-
("2๋งB
|
|
|
|
|
|
|
| 147 |
|
| 148 |
-
("3๋ง ์ด๊ณ ", "โ๏ธ 3๋ง
|
| 149 |
-
("3๋ง
|
| 150 |
-
("3๋ง
|
| 151 |
-
("3๋ง
|
|
|
|
|
|
|
| 152 |
|
| 153 |
-
("
|
| 154 |
-
("์ต์ข
๊ฒํ ", "
|
| 155 |
]
|
| 156 |
|
| 157 |
# ๋ฐ์ดํฐ ํด๋์ค
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 158 |
@dataclass
|
| 159 |
class ScreenplayPlan:
|
| 160 |
"""์๋๋ฆฌ์ค ๊ธฐํ์"""
|
|
@@ -188,23 +259,9 @@ class ScreenplayPlan:
|
|
| 188 |
# ์ฌ ๊ตฌ์ฑ
|
| 189 |
total_scenes: int = 0
|
| 190 |
scene_breakdown: List[Dict] = field(default_factory=list)
|
| 191 |
-
|
| 192 |
-
|
| 193 |
-
|
| 194 |
-
"""์๋๋ฆฌ์ค ๋ฐ์ด๋ธ"""
|
| 195 |
-
title: str = ""
|
| 196 |
-
logline: str = ""
|
| 197 |
-
genre: str = ""
|
| 198 |
-
subgenre: str = ""
|
| 199 |
-
tone: str = ""
|
| 200 |
-
themes: List[str] = field(default_factory=list)
|
| 201 |
-
protagonist: Dict[str, Any] = field(default_factory=dict)
|
| 202 |
-
antagonist: Dict[str, Any] = field(default_factory=dict)
|
| 203 |
-
supporting_cast: Dict[str, Dict[str, Any]] = field(default_factory=dict)
|
| 204 |
-
three_act_structure: Dict[str, str] = field(default_factory=dict)
|
| 205 |
-
time_period: str = ""
|
| 206 |
-
primary_locations: List[Dict[str, str]] = field(default_factory=list)
|
| 207 |
-
visual_style: str = ""
|
| 208 |
|
| 209 |
# ๋ฐ์ดํฐ๋ฒ ์ด์ค ํด๋์ค
|
| 210 |
class ScreenplayDatabase:
|
|
@@ -224,6 +281,7 @@ class ScreenplayDatabase:
|
|
| 224 |
title TEXT,
|
| 225 |
logline TEXT,
|
| 226 |
planning_data TEXT,
|
|
|
|
| 227 |
screenplay_content TEXT,
|
| 228 |
created_at TEXT DEFAULT (datetime('now')),
|
| 229 |
updated_at TEXT DEFAULT (datetime('now')),
|
|
@@ -234,18 +292,16 @@ class ScreenplayDatabase:
|
|
| 234 |
''')
|
| 235 |
|
| 236 |
cursor.execute('''
|
| 237 |
-
CREATE TABLE IF NOT EXISTS
|
| 238 |
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
| 239 |
session_id TEXT NOT NULL,
|
| 240 |
-
|
| 241 |
-
|
| 242 |
-
|
| 243 |
-
|
| 244 |
-
|
| 245 |
-
status TEXT DEFAULT 'pending',
|
| 246 |
created_at TEXT DEFAULT (datetime('now')),
|
| 247 |
-
FOREIGN KEY (session_id) REFERENCES screenplay_sessions(session_id)
|
| 248 |
-
UNIQUE(session_id, stage_type, stage_number)
|
| 249 |
)
|
| 250 |
''')
|
| 251 |
|
|
@@ -277,6 +333,18 @@ class ScreenplayDatabase:
|
|
| 277 |
conn.commit()
|
| 278 |
return session_id
|
| 279 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 280 |
@staticmethod
|
| 281 |
def save_planning_data(session_id: str, planning_data: Dict):
|
| 282 |
with ScreenplayDatabase.get_db() as conn:
|
|
@@ -288,17 +356,6 @@ class ScreenplayDatabase:
|
|
| 288 |
)
|
| 289 |
conn.commit()
|
| 290 |
|
| 291 |
-
@staticmethod
|
| 292 |
-
def get_planning_data(session_id: str) -> Optional[Dict]:
|
| 293 |
-
with ScreenplayDatabase.get_db() as conn:
|
| 294 |
-
row = conn.cursor().execute(
|
| 295 |
-
'SELECT planning_data FROM screenplay_sessions WHERE session_id = ?',
|
| 296 |
-
(session_id,)
|
| 297 |
-
).fetchone()
|
| 298 |
-
if row and row['planning_data']:
|
| 299 |
-
return json.loads(row['planning_data'])
|
| 300 |
-
return None
|
| 301 |
-
|
| 302 |
@staticmethod
|
| 303 |
def save_screenplay_content(session_id: str, content: str, title: str, logline: str):
|
| 304 |
with ScreenplayDatabase.get_db() as conn:
|
|
@@ -320,8 +377,9 @@ class ScreenplayGenerationSystem:
|
|
| 320 |
self.model_id = MODEL_ID
|
| 321 |
self.current_session_id = None
|
| 322 |
self.current_plan = ScreenplayPlan()
|
| 323 |
-
self.original_query = ""
|
| 324 |
-
self.accumulated_content = {}
|
|
|
|
| 325 |
ScreenplayDatabase.init_db()
|
| 326 |
|
| 327 |
def create_headers(self):
|
|
@@ -340,10 +398,10 @@ class ScreenplayGenerationSystem:
|
|
| 340 |
"model": self.model_id,
|
| 341 |
"messages": messages,
|
| 342 |
"max_tokens": max_tokens,
|
| 343 |
-
"temperature": 0.7,
|
| 344 |
-
"top_p": 0.9,
|
| 345 |
"top_k": 40,
|
| 346 |
-
"presence_penalty": 0.3,
|
| 347 |
"frequency_penalty": 0.3,
|
| 348 |
"stream": True
|
| 349 |
}
|
|
@@ -393,185 +451,330 @@ class ScreenplayGenerationSystem:
|
|
| 393 |
except Exception as e:
|
| 394 |
yield f"โ ์ค๋ฅ: {str(e)}"
|
| 395 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 396 |
def generate_planning(self, query: str, screenplay_type: str, genre: str,
|
| 397 |
-
progress_callback=None) -> Generator[Tuple[str, float, Dict], None, None]:
|
| 398 |
-
"""
|
| 399 |
try:
|
| 400 |
-
self.original_query = query
|
| 401 |
self.current_session_id = ScreenplayDatabase.create_session(query, screenplay_type, genre)
|
| 402 |
|
| 403 |
planning_content = {}
|
| 404 |
-
self.accumulated_content = {}
|
|
|
|
| 405 |
total_stages = len(PLANNING_STAGES)
|
| 406 |
|
| 407 |
-
|
| 408 |
-
core_request = f"""
|
| 409 |
-
ใํต์ฌ ์์ฒญ์ฌํญใ
|
| 410 |
-
{query}
|
| 411 |
-
|
| 412 |
-
ใํ์ ์ค์์ฌํญใ
|
| 413 |
-
- ์ ์์ฒญ์ฌํญ์ ์ ๋์ ์ผ๋ก ์ค์ํ ๊ฒ
|
| 414 |
-
- ์์ฒญ๋ ์ค์ , ์ธ๋ฌผ, ์คํ ๋ฆฌ๋ฅผ ๊ทธ๋๋ก ์ฌ์ฉํ ๊ฒ
|
| 415 |
-
- ์์๋ก ๋ด์ฉ์ ๋ณ๊ฒฝํ๊ฑฐ๋ ๋ค๋ฅธ ์ด์ผ๊ธฐ๋ก ๋ฐ๊พธ์ง ๋ง ๊ฒ
|
| 416 |
-
- ์ฅ๋ฅด: {genre}
|
| 417 |
-
- ํ์: {screenplay_type}
|
| 418 |
-
"""
|
| 419 |
-
|
| 420 |
-
for idx, (stage_name, stage_desc) in enumerate(PLANNING_STAGES):
|
| 421 |
progress = (idx / total_stages) * 100
|
| 422 |
|
| 423 |
-
yield f"๐ {stage_desc} ์งํ ์ค...", progress, planning_content
|
| 424 |
|
| 425 |
-
#
|
| 426 |
-
|
| 427 |
-
|
| 428 |
-
|
| 429 |
-
|
| 430 |
-
previous_content += f"\n[{key}]\n{value[:500]}...\n"
|
| 431 |
|
| 432 |
-
# ๊ฐ ๋จ๊ณ๋ณ ํ๋กฌํํธ ์์ฑ
|
| 433 |
-
if stage_name == "์ปจ์
๊ฐ๋ฐ":
|
| 434 |
-
prompt = f"""{core_request}
|
| 435 |
-
|
| 436 |
-
๋น์ ์ ํ ๋ฆฌ์ฐ๋ ํ๋ก๋์์
๋๋ค. ์ ์์ฒญ์ฌํญ์ ์ ํํ ๋ง๋ {screenplay_type} ์๋๋ฆฌ์ค ์ปจ์
์ ๊ฐ๋ฐํ์ธ์.
|
| 437 |
-
|
| 438 |
-
ใ์์ฑ ํญ๋ชฉใ
|
| 439 |
-
1. ์ ๋ชฉ: (์์ฒญ ๋ด์ฉ์ ๋ฐ์ํ ์ ๋ชฉ)
|
| 440 |
-
2. ๋ก๊ทธ๋ผ์ธ: (์ ์์ฒญ์ ํต์ฌ ์คํ ๋ฆฌ๋ฅผ ํ ๋ฌธ์ฅ์ผ๋ก)
|
| 441 |
-
3. ํต์ฌ ํ
๋ง: (์์ฒญ์์ ๋ค๋ฃจ๊ณ ์ ํ๋ ์ฃผ์ )
|
| 442 |
-
4. ํ๊ฒ ๊ด๊ฐ: (์ด ์คํ ๋ฆฌ๊ฐ ์ดํํ ๊ด๊ฐ์ธต)
|
| 443 |
-
5. ์ ์ฌ ์ํ 3๊ฐ์ ์ฐจ๋ณ์
|
| 444 |
-
6. ์์
์ ๋งค๋ ฅ ํฌ์ธํธ
|
| 445 |
-
|
| 446 |
-
โ ๏ธ ์ค์: ๋ฐ๋์ "{query}"์ ๋ด์ฉ์ ๊ทธ๋๋ก ์ฌ์ฉํ์ธ์."""
|
| 447 |
-
|
| 448 |
-
elif stage_name == "์คํ ๋ฆฌ ๊ตฌ์ฑ":
|
| 449 |
-
prompt = f"""{core_request}
|
| 450 |
-
{previous_content}
|
| 451 |
-
|
| 452 |
-
์ด์ ์ปจ์
์ ๋ฐํ์ผ๋ก ์คํ ๋ฆฌ ๊ตฌ์กฐ๋ฅผ ๋ง๋์ธ์.
|
| 453 |
-
โ ๏ธ ๋ฐ๋์ ์๋ณธ ์์ฒญ "{query}"์ ์คํ ๋ฆฌ๋ผ์ธ์ ์ ์งํ์ธ์.
|
| 454 |
-
|
| 455 |
-
ใ์์ฑ ํญ๋ชฉใ
|
| 456 |
-
1. ์๋์์ค (500์): ์๋ณธ ์์ฒญ์ ์คํ ๋ฆฌ๋ฅผ ์ถฉ์คํ ๋ฐ์
|
| 457 |
-
2. ํ์ฅ ์๋์์ค (1000์): ์ธ๋ถ ์ฌ๊ฑด๊ณผ ์ ๊ฐ ์ถ๊ฐ
|
| 458 |
-
3. 3๋ง ๊ตฌ์กฐ:
|
| 459 |
-
- 1๋ง: ์์ฒญ๋ ์ค์ ๊ณผ ์ธ๋ฌผ ์๊ฐ
|
| 460 |
-
- 2๋งA: ์์ฒญ๋ ๊ฐ๋ฑ์ ์์
|
| 461 |
-
- 2๋งB: ์์ฒญ๋ ์๊ธฐ์ ์ฌํ
|
| 462 |
-
- 3๋ง: ์์ฒญ๋ ๋ฐฉ์์ ํด๊ฒฐ
|
| 463 |
-
|
| 464 |
-
์ ๋ ๋ค๋ฅธ ์ด์ผ๊ธฐ๋ก ๋ฐ๋์ง ์๋๋ก ์ฃผ์ํ์ธ์."""
|
| 465 |
-
|
| 466 |
-
elif stage_name == "์บ๋ฆญํฐ ์ค๊ณ":
|
| 467 |
-
prompt = f"""{core_request}
|
| 468 |
-
{previous_content}
|
| 469 |
-
|
| 470 |
-
์๋ณธ ์์ฒญ์ ๋์จ ์บ๋ฆญํฐ๋ค์ ์ค๊ณํ์ธ์.
|
| 471 |
-
โ ๏ธ ์์ฒญ์ ๋ช
์๋ ์ธ๋ฌผ ์ค์ ์ ์ ๋ ๋ณ๊ฒฝํ์ง ๋ง์ธ์.
|
| 472 |
-
|
| 473 |
-
ใ์์ฑ ํญ๋ชฉใ
|
| 474 |
-
์ฃผ์ธ๊ณต: (์์ฒญ์ ๋์จ ์ฃผ์ธ๊ณต ๊ทธ๋๋ก)
|
| 475 |
-
- ์ด๋ฆ๊ณผ ๋์ด
|
| 476 |
-
- ์ธ๋ชจ์ ์ฑ๊ฒฉ (์์ฒญ ๋ด์ฉ ๋ฐ์)
|
| 477 |
-
- ๋ชฉํ (์์ฒญ๋ ๋ชฉํ)
|
| 478 |
-
- ํ์ (๋ด์ ์ฑ์ฅ)
|
| 479 |
-
- ์ฑ์ฅ ์ํฌ
|
| 480 |
-
|
| 481 |
-
์ ๋์: (์์ฒญ์ ๋์จ ๋๋ฆฝ ์ธ๋ฌผ/์ธ๋ ฅ)
|
| 482 |
-
- ์ด๋ฆ๊ณผ ์ญํ
|
| 483 |
-
- ๋๊ธฐ์ ๋ชฉํ
|
| 484 |
-
- ์ฃผ์ธ๊ณต๊ณผ์ ๊ด๊ณ
|
| 485 |
-
|
| 486 |
-
์กฐ์ฐ๋ค: (์์ฒญ์ ์ธ๊ธ๋ ์ธ๋ฌผ๋ค)
|
| 487 |
-
- ๊ฐ์์ ์ญํ ๊ณผ ํน์ง
|
| 488 |
-
|
| 489 |
-
์๋ก์ด ์ธ๋ฌผ์ ์์๋ก ์ถ๊ฐํ์ง ๋ง์ธ์."""
|
| 490 |
-
|
| 491 |
-
elif stage_name == "์ธ๊ณ๊ด ๊ตฌ์ถ":
|
| 492 |
-
prompt = f"""{core_request}
|
| 493 |
-
{previous_content}
|
| 494 |
-
|
| 495 |
-
์๋ณธ ์์ฒญ์ ๋ฐฐ๊ฒฝ๊ณผ ์ธ๊ณ๊ด์ ๊ตฌ์ถํ์ธ์.
|
| 496 |
-
โ ๏ธ ์์ฒญ์ ๋ช
์๋ ์๋, ์ฅ์, ๋ถ์๊ธฐ๋ฅผ ๊ทธ๋๋ก ์ฌ์ฉํ์ธ์.
|
| 497 |
-
|
| 498 |
-
ใ์์ฑ ํญ๋ชฉใ
|
| 499 |
-
1. ์๋ ๋ฐฐ๊ฒฝ: (์์ฒญ๋ ์๋)
|
| 500 |
-
2. ์ฃผ์ ์ฅ์: (์์ฒญ์ ๋์จ ์ฅ์๋ค)
|
| 501 |
-
3. ์ ์ฒด์ ๋ถ์๊ธฐ: (์์ฒญ๋ ํค)
|
| 502 |
-
4. ์๊ฐ์ ์คํ์ผ
|
| 503 |
-
5. ์ํฅ/์์
์คํ์ผ
|
| 504 |
-
|
| 505 |
-
์์๋ก ๋ฐฐ๊ฒฝ์ ๋ฐ๊พธ์ง ๋ง์ธ์."""
|
| 506 |
-
|
| 507 |
-
elif stage_name == "์ฌ ๊ตฌ์ฑ":
|
| 508 |
-
prompt = f"""{core_request}
|
| 509 |
-
{previous_content}
|
| 510 |
-
|
| 511 |
-
์๋ณธ ์์ฒญ์ ์คํ ๋ฆฌ๋ฅผ ์ฌ์ผ๋ก ๊ตฌ์ฑํ์ธ์.
|
| 512 |
-
โ ๏ธ ๋ฐ๋์ ์์ฒญ๋ ์ฌ๊ฑด๋ค์ด ๋ชจ๋ ํฌํจ๋์ด์ผ ํฉ๋๋ค.
|
| 513 |
-
|
| 514 |
-
{SCREENPLAY_LENGTHS[screenplay_type]['pages']}ํ์ด์ง ๋ถ๋์ผ๋ก:
|
| 515 |
-
- 1๋ง: ์์ฒญ๋ ์ค์ ๊ณผ ์์ (์ฝ {int(SCREENPLAY_LENGTHS[screenplay_type]['pages']*0.25)}ํ์ด์ง)
|
| 516 |
-
- 2๋งA: ์์ฒญ๋ ๊ฐ๋ฑ ์ ๊ฐ (์ฝ {int(SCREENPLAY_LENGTHS[screenplay_type]['pages']*0.25)}ํ์ด์ง)
|
| 517 |
-
- 2๋งB: ์์ฒญ๋ ์๊ธฐ ์ฌํ (์ฝ {int(SCREENPLAY_LENGTHS[screenplay_type]['pages']*0.25)}ํ์ด์ง)
|
| 518 |
-
- 3๋ง: ์์ฒญ๋ ํด๊ฒฐ (์ฝ {int(SCREENPLAY_LENGTHS[screenplay_type]['pages']*0.25)}ํ์ด์ง)
|
| 519 |
-
|
| 520 |
-
๊ฐ ์ฌ๋ง๋ค:
|
| 521 |
-
- ์ฅ์์ ์๊ฐ
|
| 522 |
-
- ๋ฑ์ฅ์ธ๋ฌผ
|
| 523 |
-
- ํต์ฌ ์ฌ๊ฑด (์์ฒญ ๋ด์ฉ ๋ฐ์)
|
| 524 |
-
- ์์ ํ์ด์ง
|
| 525 |
-
|
| 526 |
-
์คํ ๋ฆฌ๊ฐ ๋ค๋ฅธ ๋ฐฉํฅ์ผ๋ก ๊ฐ์ง ์๋๋ก ์ฃผ์ํ์ธ์."""
|
| 527 |
-
|
| 528 |
# LLM ํธ์ถ
|
| 529 |
messages = [
|
| 530 |
-
{"role": "system", "content": f"""๋น์ ์
|
| 531 |
-
|
| 532 |
-
|
| 533 |
-
์๋ณธ
|
| 534 |
{"role": "user", "content": prompt}
|
| 535 |
]
|
| 536 |
|
| 537 |
content = ""
|
| 538 |
for chunk in self.call_llm_streaming(messages):
|
| 539 |
content += chunk
|
| 540 |
-
planning_content[
|
| 541 |
-
yield f"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 542 |
|
| 543 |
# ๋์ ๋ด์ฉ ์ ์ฅ
|
| 544 |
-
self.accumulated_content[
|
| 545 |
-
time.sleep(0.5)
|
| 546 |
|
| 547 |
# ์ต์ข
๊ธฐํ์ ์ ์ฅ
|
| 548 |
ScreenplayDatabase.save_planning_data(self.current_session_id, planning_content)
|
| 549 |
-
yield "โ
๊ธฐํ์ ์์ฑ!", 100, planning_content
|
| 550 |
|
| 551 |
except Exception as e:
|
| 552 |
-
yield f"โ ์ค๋ฅ ๋ฐ์: {str(e)}", 0, {}
|
| 553 |
|
| 554 |
def generate_screenplay(self, session_id: str, planning_data: Dict,
|
| 555 |
-
progress_callback=None) -> Generator[Tuple[str, float, str], None, None]:
|
| 556 |
-
"""
|
| 557 |
try:
|
| 558 |
total_stages = len(WRITING_STAGES)
|
| 559 |
screenplay_content = ""
|
| 560 |
act_contents = {"1๋ง": "", "2๋งA": "", "2๋งB": "", "3๋ง": ""}
|
|
|
|
| 561 |
|
| 562 |
-
|
| 563 |
-
concept = planning_data.get("์ปจ์
๊ฐ๋ฐ", "")
|
| 564 |
-
story = planning_data.get("์คํ ๋ฆฌ ๊ตฌ์ฑ", "")
|
| 565 |
-
characters = planning_data.get("์บ๋ฆญํฐ ์ค๊ณ", "")
|
| 566 |
-
world = planning_data.get("์ธ๊ณ๊ด ๊ตฌ์ถ", "")
|
| 567 |
-
scenes = planning_data.get("์ฌ ๊ตฌ์ฑ", "")
|
| 568 |
-
|
| 569 |
-
for idx, (stage_name, stage_desc) in enumerate(WRITING_STAGES):
|
| 570 |
progress = (idx / total_stages) * 100
|
| 571 |
|
| 572 |
-
yield f"๐ {stage_desc} ์งํ ์ค...", progress, screenplay_content
|
| 573 |
|
| 574 |
-
# ๋ง ๊ฒฐ์
|
| 575 |
current_act = ""
|
| 576 |
if "1๋ง" in stage_name:
|
| 577 |
current_act = "1๋ง"
|
|
@@ -582,40 +785,42 @@ class ScreenplayGenerationSystem:
|
|
| 582 |
elif "3๋ง" in stage_name:
|
| 583 |
current_act = "3๋ง"
|
| 584 |
|
| 585 |
-
#
|
| 586 |
-
if "
|
| 587 |
-
prompt = self.
|
| 588 |
-
current_act, planning_data, act_contents,
|
| 589 |
-
self.original_query
|
| 590 |
)
|
| 591 |
-
elif "
|
| 592 |
-
prompt = self.
|
| 593 |
-
current_act,
|
| 594 |
-
self.original_query
|
| 595 |
)
|
| 596 |
-
elif "
|
| 597 |
-
prompt = self.
|
| 598 |
-
current_act, act_contents[current_act]
|
| 599 |
-
|
|
|
|
|
|
|
|
|
|
| 600 |
)
|
| 601 |
-
elif "
|
| 602 |
-
prompt = self.
|
| 603 |
-
current_act,
|
| 604 |
self.original_query
|
| 605 |
)
|
| 606 |
-
elif
|
| 607 |
-
prompt = self.
|
| 608 |
-
|
| 609 |
-
|
| 610 |
else:
|
| 611 |
continue
|
| 612 |
|
| 613 |
# LLM ํธ์ถ
|
|
|
|
| 614 |
messages = [
|
| 615 |
-
{"role": "system", "content": f"""๋น์ ์
|
| 616 |
-
|
| 617 |
-
|
| 618 |
-
|
| 619 |
{"role": "user", "content": prompt}
|
| 620 |
]
|
| 621 |
|
|
@@ -623,142 +828,214 @@ class ScreenplayGenerationSystem:
|
|
| 623 |
for chunk in self.call_llm_streaming(messages, max_tokens=15000):
|
| 624 |
content += chunk
|
| 625 |
|
| 626 |
-
# ๋ง ๋ด์ฉ ์
๋ฐ์ดํธ
|
| 627 |
if current_act and "์์ฑ" in stage_name:
|
| 628 |
act_contents[current_act] = content
|
| 629 |
-
screenplay_content = "\n\n".join([
|
|
|
|
|
|
|
|
|
|
| 630 |
|
| 631 |
-
yield f"โ๏ธ {stage_desc} ์์ฑ ์ค...", progress, screenplay_content
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 632 |
|
| 633 |
time.sleep(0.5)
|
| 634 |
|
| 635 |
# ์ต์ข
์ ์ฅ
|
| 636 |
-
title =
|
| 637 |
-
logline =
|
| 638 |
|
| 639 |
ScreenplayDatabase.save_screenplay_content(session_id, screenplay_content, title, logline)
|
| 640 |
-
yield "โ
์๋๋ฆฌ์ค ์์ฑ!", 100, screenplay_content
|
| 641 |
|
| 642 |
except Exception as e:
|
| 643 |
-
yield f"โ ์ค๋ฅ ๋ฐ์: {str(e)}", 0, ""
|
| 644 |
|
| 645 |
-
def
|
| 646 |
-
|
| 647 |
-
"""
|
| 648 |
|
| 649 |
-
|
| 650 |
-
characters = planning_data.get("์บ๋ฆญํฐ ์ค๊ณ", "")
|
| 651 |
-
scenes = planning_data.get("์ฌ ๊ตฌ์ฑ", "")
|
| 652 |
-
world = planning_data.get("์ธ๊ณ๊ด ๊ตฌ์ถ", "")
|
| 653 |
-
|
| 654 |
-
if phase == "์ด๊ณ ":
|
| 655 |
min_lines = 800
|
| 656 |
-
|
| 657 |
-
elif phase == "์์ ":
|
| 658 |
-
min_lines = 1000
|
| 659 |
-
instruction = "๋ด์ฉ์ ํ์ฅํ๊ณ ๋ค๋ฌ์ผ์ธ์"
|
| 660 |
-
else: # ์์ฑ
|
| 661 |
min_lines = 1200
|
| 662 |
-
|
|
|
|
| 663 |
|
| 664 |
-
|
| 665 |
-
{
|
| 666 |
-
|
| 667 |
-
ใ{act} {instruction}ใ
|
| 668 |
-
|
| 669 |
-
๋ชฉํ ๋ถ๋: ์ต์ {min_lines}์ค
|
| 670 |
|
| 671 |
-
ใ๊ธฐํ์
|
| 672 |
-
|
| 673 |
-
{story_structure[:1000]}
|
| 674 |
|
| 675 |
-
|
| 676 |
-
{characters[:1000]}
|
| 677 |
-
|
| 678 |
-
์ธ๊ณ๊ด:
|
| 679 |
-
{world[:500]}
|
| 680 |
-
|
| 681 |
-
์ฌ ๊ตฌ์ฑ:
|
| 682 |
-
{scenes[:1000]}
|
| 683 |
-
|
| 684 |
-
ใ์ด์ ๋ง ๋ด์ฉใ
|
| 685 |
{previous_acts if previous_acts else "์ฒซ ๋ง์
๋๋ค"}
|
| 686 |
|
| 687 |
-
|
| 688 |
-
1.
|
| 689 |
-
2.
|
| 690 |
-
|
| 691 |
-
|
| 692 |
-
|
| 693 |
-
3. ์๊ฐ์ ๋ฌ์ฌ๋ ํ์ฌํ์ผ๋ก
|
| 694 |
-
4. ๊ฐ ์ฌ๋ง๋ค ์ถฉ๋ถํ ๋ถ๋ (5-7ํ์ด์ง)
|
| 695 |
-
5. ์ ๋ ๋ค๋ฅธ ์ด์ผ๊ธฐ๋ก ๋ณ๊ฒฝํ์ง ๋ง ๊ฒ
|
| 696 |
-
|
| 697 |
-
โ ๏ธ ์ค์: ๋ฐ๋์ ์๋ณธ ์์ฒญ "{original_query}"์ ์คํ ๋ฆฌ๋ฅผ ๊ทธ๋๋ก ๊ตฌํํ์ธ์.
|
| 698 |
-
์์๋ก ์คํ ๋ฆฌ๋ฅผ ๋ฐ๊พธ์ง ๋ง์ธ์.
|
| 699 |
|
| 700 |
-
{min_lines}์ค ์ด์ ์์ฑํ์ธ์."""
|
| 701 |
-
|
| 702 |
-
return prompt
|
| 703 |
|
| 704 |
-
def
|
| 705 |
-
"""
|
| 706 |
-
return f"""
|
| 707 |
-
{original_query}
|
| 708 |
|
| 709 |
-
|
|
|
|
| 710 |
|
| 711 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 712 |
{content[:2000]}...
|
| 713 |
|
| 714 |
-
|
| 715 |
-
1.
|
| 716 |
-
2.
|
| 717 |
-
3.
|
| 718 |
-
4.
|
| 719 |
-
5.
|
| 720 |
|
| 721 |
-
|
| 722 |
-
- ์๋ณธ ์์ฒญ์ ์คํ ๋ฆฌ๊ฐ ์ ๋๋ก ๊ตฌํ๋์๋๊ฐ?
|
| 723 |
-
- ์์๋ก ๋ณ๊ฒฝ๋ ๋ถ๋ถ์ ์๋๊ฐ?
|
| 724 |
-
- ๊ธฐํ์๊ณผ ์ผ์นํ๋๊ฐ?
|
| 725 |
|
| 726 |
-
|
|
|
|
|
|
|
| 727 |
|
| 728 |
-
|
| 729 |
-
|
| 730 |
-
return f"""์ ์ฒด ์๋๋ฆฌ์ค์ ๋์ฌ๋ฅผ ๊ฒํ ํ๊ณ ๊ฐ์ ํ์ธ์.
|
| 731 |
|
| 732 |
-
ใ๊ฐ์
|
| 733 |
-
1. ์บ๋ฆญํฐ๋ณ
|
| 734 |
2. ์๋ธํ
์คํธ ์ถ๊ฐ
|
| 735 |
-
3. ๋ถํ์ํ ์ค๋ช
|
| 736 |
-
4. ๊ฐ์ ์
|
| 737 |
-
5.
|
| 738 |
|
| 739 |
-
|
| 740 |
-
- ์คํ ๋ฆฌ์ ์ค์ ์ ์ ๋ ๋ณ๊ฒฝํ์ง ๋ง์ธ์
|
| 741 |
-
- ๋์ฌ๋ง ๊ฐ์ ํ์ธ์
|
| 742 |
|
| 743 |
-
|
|
|
|
|
|
|
| 744 |
|
| 745 |
-
|
| 746 |
-
|
| 747 |
-
|
|
|
|
| 748 |
|
| 749 |
ใํ๊ฐ ํญ๋ชฉใ
|
| 750 |
-
1. ์๋ณธ
|
| 751 |
-
2.
|
| 752 |
-
3. ์บ๋ฆญํฐ
|
| 753 |
-
4.
|
| 754 |
-
5.
|
| 755 |
|
| 756 |
-
|
| 757 |
-
- ์ฒ์ ์์ฒญํ ์คํ ๋ฆฌ๊ฐ ์ ๋๋ก ๊ตฌํ๋์๋๊ฐ?
|
| 758 |
-
- ์ค๊ฐ์ ๋ค๋ฅธ ์ด์ผ๊ธฐ๋ก ๋ฐ๋์ง ์์๋๊ฐ?
|
| 759 |
-
- ๊ธฐํ์๊ณผ ์ผ์นํ๋๊ฐ?
|
| 760 |
|
| 761 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 762 |
|
| 763 |
# ์ ํธ๋ฆฌํฐ ํจ์
|
| 764 |
def format_planning_display(planning_data: Dict) -> str:
|
|
@@ -766,15 +1043,39 @@ def format_planning_display(planning_data: Dict) -> str:
|
|
| 766 |
if not planning_data:
|
| 767 |
return "๊ธฐํ์์ด ์์ง ์์ฑ๋์ง ์์์ต๋๋ค."
|
| 768 |
|
| 769 |
-
formatted = "# ๐ ์๋๋ฆฌ์ค
|
| 770 |
|
| 771 |
-
for
|
| 772 |
-
|
|
|
|
|
|
|
| 773 |
formatted += content + "\n\n"
|
| 774 |
formatted += "---\n\n"
|
| 775 |
|
| 776 |
return formatted
|
| 777 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 778 |
def format_screenplay_display(screenplay_text: str) -> str:
|
| 779 |
"""์๋๋ฆฌ์ค ํ์ ํฌ๋งท"""
|
| 780 |
if not screenplay_text:
|
|
@@ -810,43 +1111,15 @@ def generate_random_concept(screenplay_type: str, genre: str) -> str:
|
|
| 810 |
'์ก์
': [
|
| 811 |
"์ํดํ ํน์์์์ด ๋ฉ์น๋ ๋ธ์ ๊ตฌํ๊ธฐ ์ํด ๋ค์ ํ์ฅ์ผ๋ก ๋ณต๊ทํ๋ค",
|
| 812 |
"ํ๋ฒํ ํ์๊ธฐ์ฌ๊ฐ ์ฐ์ฐํ ๊ตญ์ ํ
๋ฌ ์กฐ์ง์ ์๋ชจ์ ํ๋ง๋ฆฐ๋ค",
|
| 813 |
-
"๋ถํจ ๊ฒฝ์ฐฐ๊ณผ ๋ง์ ์ธ์ฐ๋ ์ ์๋ก์ด ํ์ฌ์ ๊ณ ๋
ํ ์ธ์"
|
| 814 |
],
|
| 815 |
'์ค๋ฆด๋ฌ': [
|
| 816 |
"๊ธฐ์ต์ ์์ ๋จ์๊ฐ ์์ ์ด ์ฐ์์ด์ธ๋ฒ์ด๋ผ๋ ์ฆ๊ฑฐ๋ฅผ ๋ฐ๊ฒฌํ๋ค",
|
| 817 |
"์ค์ข
๋ ์์ด๋ฅผ ์ฐพ๋ ๊ณผ์ ์์ ๋ง์์ ๋์ฐํ ๋น๋ฐ์ด ๋๋ฌ๋๋ค",
|
| 818 |
-
"์๋ฒฝํ ์๋ฆฌ๋ฐ์ด๋ฅผ ๊ฐ์ง ์ฉ์์๋ฅผ ์ซ๋ ๊ฒ์ฌ์ ์ง์ํ ์ถ์ "
|
| 819 |
],
|
| 820 |
'๋๋ผ๋ง': [
|
| 821 |
"๋ง๊ธฐ ์ ํ์๊ฐ ๋ง์ง๋ง ์์์ผ๋ก ๊ฐ์กฑ๊ณผ์ ํํด๋ฅผ ์๋ํ๋ค",
|
| 822 |
"์ฌ๊ฐ๋ฐ๋ก ์ฌ๋ผ์ง ๋๋ค๋ฅผ ์งํค๋ ค๋ ์ฃผ๋ฏผ๋ค์ ๋ง์ง๋ง ์ธ์",
|
| 823 |
-
"์
์์๊ฐ ์๋ชจ๋ฅผ ์ฐพ์๊ฐ๋ ๊ฐ์ด ์ํ ์ฌ์ "
|
| 824 |
-
],
|
| 825 |
-
'์ฝ๋ฏธ๋': [
|
| 826 |
-
"๊ฒฐํผ์์ฅ์ ์ด์ํ๋ ์ปคํ์ด ์์ ๋ค์ ์ดํผ์ ์จ๊ธฐ๋ฉฐ ์ผํ๋ค",
|
| 827 |
-
"๋ณต๊ถ์ ๋น์ฒจ๋ ๊ฐ์กฑ์ด ์๋ก ๋์ ์ฐจ์งํ๋ ค ์๋ชจ๋ฅผ ๊พธ๋ฏผ๋ค",
|
| 828 |
-
"์ค์๋ก ๋ํต๋ น ๊ฒฝํธ์์ด ๋ ํ๋ฒํ ํ์ฌ์์ ์ข์ถฉ์ฐ๋ ์ด์ผ๊ธฐ"
|
| 829 |
-
],
|
| 830 |
-
'๊ณตํฌ': [
|
| 831 |
-
"ํ๋ณ์์์ ๋ฐค์ ์์กดํด์ผ ํ๋ ์๋์๋ค์ ๊ณตํฌ ์ฒดํ",
|
| 832 |
-
"๊ฑฐ์ธ ์์์ ๋ํ๋๋ ๋ ๋ค๋ฅธ ์์ ๊ณผ ๋ง์ฃผํ ์ฌ์",
|
| 833 |
-
"์ ์ฃผ๋ฐ์ ๊ณจ๋ํ์ ํ๋งคํ ํ ๋ฒ์ด์ง๋ ๊ธฐ์ดํ ์ฌ๊ฑด๋ค"
|
| 834 |
],
|
| 835 |
-
'SF': [
|
| 836 |
-
"์๊ฐ ์ฌํ์ผ๋ก ๊ณผ๊ฑฐ๋ฅผ ๋ฐ๊พธ๋ ค๋ค ๋ ํฐ ์ฌ์์ ๋ง๋ ๊ณผํ์",
|
| 837 |
-
"์ธ๊ณต์ง๋ฅ์ด ์ธ๊ฐ์ ๊ฐ์ ์ ํ์ตํ๋ฉฐ ๋ฒ์ด์ง๋ ์์์น ๋ชปํ ์ฌ๊ฑด",
|
| 838 |
-
"์ธ๊ณ ์ ํธ๋ฅผ ํด๋
ํ ์ธ์ดํ์๊ฐ ์ธ๋ฅ์ ์ด๋ช
์ ๊ฒฐ์ ํด์ผ ํ๋ค"
|
| 839 |
-
],
|
| 840 |
-
'๋ก๋งจ์ค': [
|
| 841 |
-
"์ฒซ์ฌ๋๊ณผ 20๋
๋ง์ ์ฌํํ ๋ ์ฌ๋์ ๋ ๋ฒ์งธ ๊ธฐํ",
|
| 842 |
-
"๊ณ์ฝ๊ฒฐํผ์ผ๋ก ์์ํ๋ค๊ฐ ์ง์ง ์ฌ๋์ ๋น ์ง ์ปคํ",
|
| 843 |
-
"์๋ก ๋ค๋ฅธ ์๋๋ฅผ ์ด์๊ฐ๋ ๋ ์ฌ๋์ ํธ์ง๋ฅผ ํตํ ์ฌ๋"
|
| 844 |
-
],
|
| 845 |
-
'ํํ์ง': [
|
| 846 |
-
"ํ๋ฒํ ๊ณ ๋ฑํ์์ด ํํ์ง ์ธ๊ณ์ ์ ํ๋ฐ์ ์ฉ์ฌ๊ฐ ๋๋ค",
|
| 847 |
-
"๋์์ ๋ํ๋ ์ฉ๊ณผ ์ํตํ ์ ์๋ ์ ์ผํ ์ฌ๋",
|
| 848 |
-
"๋ง๋ฒ์ด ์ฌ๋ผ์ง ์ธ๊ณ์์ ๋ง์ง๋ง ๋ง๋ฒ์ฌ๊ฐ ๋์ด๋ฒ๋ฆฐ ์ฒญ๋
"
|
| 849 |
-
]
|
| 850 |
}
|
| 851 |
|
| 852 |
genre_concepts = concepts.get(genre, concepts['๋๋ผ๋ง'])
|
|
@@ -871,24 +1144,20 @@ def create_interface():
|
|
| 871 |
text-shadow: 2px 2px 4px rgba(0,0,0,0.3);
|
| 872 |
}
|
| 873 |
|
| 874 |
-
.
|
| 875 |
-
background: #
|
| 876 |
-
padding:
|
| 877 |
border-radius: 10px;
|
| 878 |
margin: 1rem 0;
|
| 879 |
-
max-height: 600px;
|
| 880 |
-
overflow-y: auto;
|
| 881 |
}
|
| 882 |
|
| 883 |
-
.
|
| 884 |
-
|
| 885 |
-
|
| 886 |
background: white;
|
| 887 |
-
|
| 888 |
-
|
| 889 |
-
|
| 890 |
-
max-height: 800px;
|
| 891 |
-
overflow-y: auto;
|
| 892 |
}
|
| 893 |
|
| 894 |
.progress-bar {
|
|
@@ -905,55 +1174,50 @@ def create_interface():
|
|
| 905 |
border-radius: 10px;
|
| 906 |
transition: width 0.3s ease;
|
| 907 |
}
|
| 908 |
-
|
| 909 |
-
.warning-box {
|
| 910 |
-
background: #fff3cd;
|
| 911 |
-
border: 1px solid #ffc107;
|
| 912 |
-
border-radius: 5px;
|
| 913 |
-
padding: 1rem;
|
| 914 |
-
margin: 1rem 0;
|
| 915 |
-
}
|
| 916 |
"""
|
| 917 |
|
| 918 |
-
with gr.Blocks(theme=gr.themes.Soft(), css=css, title="AI ์๋๋ฆฌ์ค ์๊ฐ") as interface:
|
| 919 |
gr.HTML("""
|
| 920 |
<div class="main-header">
|
| 921 |
<h1 class="header-title">๐ฌ AI ์๋๋ฆฌ์ค ์๊ฐ</h1>
|
| 922 |
<p style="font-size: 1.2rem; opacity: 0.95;">
|
| 923 |
-
|
| 924 |
-
|
| 925 |
</p>
|
| 926 |
</div>
|
| 927 |
""")
|
| 928 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 929 |
current_session_id = gr.State(None)
|
| 930 |
current_planning_data = gr.State({})
|
|
|
|
| 931 |
|
| 932 |
with gr.Tabs():
|
| 933 |
with gr.Tab("๐ ์ ์๋๋ฆฌ์ค"):
|
| 934 |
-
# ์ฃผ์์ฌํญ
|
| 935 |
-
gr.HTML("""
|
| 936 |
-
<div class="warning-box">
|
| 937 |
-
<strong>๐ก ํ:</strong> ๊ตฌ์ฒด์ ์ด๊ณ ๋ช
ํํ ์ค์ ์ ์
๋ ฅํ ์๋ก ์ํ๋ ์คํ ๋ฆฌ๊ฐ ์ ํํ ๊ตฌํ๋ฉ๋๋ค.<br>
|
| 938 |
-
์ฃผ์ธ๊ณต, ๋ฐฐ๊ฒฝ, ํต์ฌ ๊ฐ๋ฑ, ๊ฒฐ๋ง ๋ฐฉํฅ์ ๊ตฌ์ฒด์ ์ผ๋ก ์ ์ํด์ฃผ์ธ์.
|
| 939 |
-
</div>
|
| 940 |
-
""")
|
| 941 |
-
|
| 942 |
with gr.Row():
|
| 943 |
with gr.Column(scale=2):
|
| 944 |
query_input = gr.Textbox(
|
| 945 |
-
label="๐ก ์๋๋ฆฌ์ค ์์ด๋์ด
|
| 946 |
-
placeholder="""
|
| 947 |
-
|
| 948 |
-
|
| 949 |
-
|
| 950 |
-
|
| 951 |
-
|
| 952 |
-
๊ฒฐ๋ง์์ ์ง์ง์ ๊ฐ์ง์ ๊ฒฝ๊ณ๊ฐ ๋ฌด๋์ง๋ฉฐ
|
| 953 |
-
์ฌ๋์ ๋ณธ์ง์ ๋ํด ์ง๋ฌธ์ ๋์ง๋ค."
|
| 954 |
-
|
| 955 |
-
๊ตฌ์ฒด์ ์ธ ์ค์ ์ผ์๋ก ์ํ๋ ๊ฒฐ๊ณผ๋ฅผ ์ป์ ์ ์์ต๋๋ค.""",
|
| 956 |
-
lines=8
|
| 957 |
)
|
| 958 |
|
| 959 |
with gr.Column(scale=1):
|
|
@@ -972,7 +1236,7 @@ AI ์๋ด์ ํจ๊ป ์ง์ค์ ์ฐพ์ ๋์ ๋ค.
|
|
| 972 |
with gr.Row():
|
| 973 |
random_btn = gr.Button("๐ฒ ๋๋ค ์์ด๋์ด", scale=1)
|
| 974 |
clear_btn = gr.Button("๐๏ธ ์ด๊ธฐํ", scale=1)
|
| 975 |
-
planning_btn = gr.Button("๐
|
| 976 |
|
| 977 |
# ์งํ ์ํ
|
| 978 |
progress_bar = gr.HTML(
|
|
@@ -981,157 +1245,102 @@ AI ์๋ด์ ํจ๊ป ์ง์ค์ ์ฐพ์ ๋์ ๋ค.
|
|
| 981 |
status_text = gr.Textbox(
|
| 982 |
label="๐ ์งํ ์ํ",
|
| 983 |
interactive=False,
|
| 984 |
-
value="์์ด๋์ด๋ฅผ ์
๋ ฅํ๊ณ
|
| 985 |
)
|
| 986 |
|
| 987 |
-
#
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 988 |
with gr.Group():
|
| 989 |
gr.Markdown("### ๐ ์๋๋ฆฌ์ค ๊ธฐํ์")
|
| 990 |
planning_display = gr.Markdown(
|
| 991 |
-
value="*๊ธฐํ์์ด ์ฌ๊ธฐ์ ํ์๋ฉ๋๋ค...*"
|
| 992 |
-
elem_classes=["planning-section"]
|
| 993 |
)
|
| 994 |
|
| 995 |
with gr.Row():
|
| 996 |
-
edit_planning_btn = gr.Button("โ๏ธ ๊ธฐํ์ ์์ ", scale=1)
|
| 997 |
save_planning_btn = gr.Button("๐พ ๊ธฐํ์ ์ ์ฅ", scale=1)
|
| 998 |
-
generate_screenplay_btn = gr.Button("๐ฌ ์๋๋ฆฌ์ค ์์ฑ
|
| 999 |
|
| 1000 |
# ์๋๋ฆฌ์ค ์ถ๋ ฅ
|
| 1001 |
with gr.Group():
|
| 1002 |
gr.Markdown("### ๐ ์์ฑ๋ ์๋๋ฆฌ์ค")
|
| 1003 |
screenplay_output = gr.Markdown(
|
| 1004 |
-
value="*์๋๋ฆฌ์ค๊ฐ ์ฌ๊ธฐ์ ํ์๋ฉ๋๋ค...*"
|
| 1005 |
-
elem_classes=["screenplay-output"]
|
| 1006 |
)
|
| 1007 |
|
| 1008 |
-
|
| 1009 |
-
download_btn = gr.Button("๐พ ๋ค์ด๋ก๋ (TXT)", scale=1)
|
| 1010 |
-
pdf_btn = gr.Button("๐ PDF ๋ณํ", scale=1)
|
| 1011 |
-
|
| 1012 |
-
# ์์
|
| 1013 |
-
gr.Examples(
|
| 1014 |
-
examples=[
|
| 1015 |
-
["2045๋
์์ธ, AI๊ฐ ์ธ๊ฐ ๊ฐ์ ์ ์๋ฒฝํ ๋ชจ๋ฐฉํ๋ ์๋. AI ์ค๋ฆฌํ์๊ฐ ์ฃฝ์ ์๋ด๋ฅผ ๋ณต์ ํ AI์ ์ด๋ค๊ฐ ์ง์ง ์๋ด๊ฐ ์ด์์๋ค๋ ์ฆ๊ฑฐ๋ฅผ ๋ฐ๊ฒฌํ๊ณ ์ง์ค์ ์ฐพ์ ๋์ ๋ค."],
|
| 1016 |
-
["์กฐ์ ์๋ ํ์, ์ ๋ถ์ ์จ๊ธด ์์ธ์๊ฐ ์ ์ฃ๊ฑฐ๋ฆฌ์์ ์๋
๋ฅผ ๋ง๋ ์ฌ๋์ ๋น ์ง๋ค. ์ญ๋ชจ ์ฌ๊ฑด์ ํ๋ง๋ ค ํจ๊ป ์ง์ค์ ๋ฐํ์ผ ํ๋ค."],
|
| 1017 |
-
["์์ ํด์ ๋ง์, 10๋
์ ์ค์ข
๋ ๋ธ์ด ๊ฐ์๊ธฐ ๋์์จ๋ค. ํ์ง๋ง ๊ทธ๋
๋ 10๋
์ ๋ชจ์ต ๊ทธ๋๋ก๋ค. ๋ง์์ ์จ๊ฒจ์ง ๋น๋ฐ์ด ๋๋ฌ๋๊ธฐ ์์ํ๋ค."],
|
| 1018 |
-
["๋๊ธฐ์
๋ฒ๋ฌดํ ๋ณํธ์ฌ๊ฐ ํ์ฌ์ ๋ถ๋ฒ์ ๊ณ ๋ฐํ๋ ค๋ค ๋ชจํจ์ ๋ฐ๋๋ค. ์จ์ด์ ์ฆ๊ฑฐ๋ฅผ ๋ชจ์ผ๋ฉฐ ๊ฑฐ๋ํ ์๋ชจ์ ๋ง์ ๋ค."]
|
| 1019 |
-
],
|
| 1020 |
-
inputs=query_input,
|
| 1021 |
-
label="๐ก ์์ ์์ด๋์ด (๊ตฌ์ฒด์ ์ธ ์ค์ ์ฐธ๊ณ )"
|
| 1022 |
-
)
|
| 1023 |
-
|
| 1024 |
-
with gr.Tab("๐ ๋ด ์ํ"):
|
| 1025 |
-
gr.Markdown("""
|
| 1026 |
-
### ๐ ์ ์ฅ๋ ์๋๋ฆฌ์ค
|
| 1027 |
-
|
| 1028 |
-
์์
์ค์ด๊ฑฐ๋ ์์ฑ๋ ์๋๋ฆฌ์ค๋ฅผ ๊ด๋ฆฌํฉ๋๋ค.
|
| 1029 |
-
""")
|
| 1030 |
-
|
| 1031 |
-
saved_list = gr.Dataframe(
|
| 1032 |
-
headers=["์ ๋ชฉ", "์ฅ๋ฅด", "์ ํ", "ํ์ด์ง", "์ํ", "์์ ์ผ"],
|
| 1033 |
-
value=[],
|
| 1034 |
-
label="์ ์ฅ๋ ์ํ ๋ชฉ๋ก"
|
| 1035 |
-
)
|
| 1036 |
-
|
| 1037 |
-
with gr.Row():
|
| 1038 |
-
refresh_btn = gr.Button("๐ ์๋ก๊ณ ์นจ")
|
| 1039 |
-
load_btn = gr.Button("๐ ๋ถ๋ฌ์ค๊ธฐ")
|
| 1040 |
-
delete_btn = gr.Button("๐๏ธ ์ญ์ ")
|
| 1041 |
|
| 1042 |
# ์ด๋ฒคํธ ํธ๋ค๋ฌ
|
| 1043 |
def handle_planning(query, s_type, genre):
|
| 1044 |
if not query:
|
| 1045 |
-
yield "", "โ ์์ด๋์ด๋ฅผ ์
๋ ฅํด์ฃผ์ธ์", "", {}
|
| 1046 |
return
|
| 1047 |
|
| 1048 |
system = ScreenplayGenerationSystem()
|
| 1049 |
|
| 1050 |
-
for status, progress, planning_data in system.generate_planning(query, s_type, genre):
|
| 1051 |
progress_html = f'<div class="progress-bar"><div class="progress-fill" style="width: {progress}%"></div></div>'
|
| 1052 |
planning_display = format_planning_display(planning_data)
|
|
|
|
| 1053 |
|
| 1054 |
-
yield progress_html, status, planning_display, planning_data
|
| 1055 |
|
| 1056 |
def handle_screenplay_generation(session_id, planning_data):
|
| 1057 |
if not planning_data:
|
| 1058 |
-
yield "", "โ ๋จผ์ ๊ธฐํ์์ ์์ฑํด์ฃผ์ธ์", ""
|
| 1059 |
return
|
| 1060 |
|
| 1061 |
system = ScreenplayGenerationSystem()
|
| 1062 |
|
| 1063 |
-
for status, progress, screenplay in system.generate_screenplay(session_id, planning_data):
|
| 1064 |
progress_html = f'<div class="progress-bar"><div class="progress-fill" style="width: {progress}%"></div></div>'
|
| 1065 |
screenplay_display = format_screenplay_display(screenplay)
|
|
|
|
| 1066 |
|
| 1067 |
-
yield progress_html, status, screenplay_display
|
| 1068 |
-
|
| 1069 |
-
def handle_random(s_type, genre):
|
| 1070 |
-
return generate_random_concept(s_type, genre)
|
| 1071 |
-
|
| 1072 |
-
def handle_clear():
|
| 1073 |
-
empty_progress = '<div class="progress-bar"><div class="progress-fill" style="width: 0%"></div></div>'
|
| 1074 |
-
return "", empty_progress, "์ค๋น๋จ", "", "", None, {}
|
| 1075 |
-
|
| 1076 |
-
def handle_download(screenplay_text):
|
| 1077 |
-
if not screenplay_text or screenplay_text == "*์๋๋ฆฌ์ค๊ฐ ์ฌ๊ธฐ์ ํ์๋ฉ๋๋ค...*":
|
| 1078 |
-
return None
|
| 1079 |
-
|
| 1080 |
-
timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
|
| 1081 |
-
filename = f"์๋๋ฆฌ์ค_{timestamp}.txt"
|
| 1082 |
-
|
| 1083 |
-
with open(filename, 'w', encoding='utf-8') as f:
|
| 1084 |
-
# ๋งํฌ๋ค์ด ์ ๊ฑฐ
|
| 1085 |
-
clean_text = re.sub(r'\*\*([^*]+)\*\*', r'\1', screenplay_text)
|
| 1086 |
-
clean_text = re.sub(r'^#+ ', '', clean_text, flags=re.MULTILINE)
|
| 1087 |
-
f.write(clean_text)
|
| 1088 |
-
|
| 1089 |
-
return filename
|
| 1090 |
|
| 1091 |
# ์ด๋ฒคํธ ์ฐ๊ฒฐ
|
| 1092 |
planning_btn.click(
|
| 1093 |
fn=handle_planning,
|
| 1094 |
inputs=[query_input, screenplay_type, genre_select],
|
| 1095 |
-
outputs=[progress_bar, status_text, planning_display, current_planning_data]
|
| 1096 |
)
|
| 1097 |
|
| 1098 |
generate_screenplay_btn.click(
|
| 1099 |
fn=handle_screenplay_generation,
|
| 1100 |
inputs=[current_session_id, current_planning_data],
|
| 1101 |
-
outputs=[progress_bar, status_text, screenplay_output]
|
| 1102 |
)
|
| 1103 |
|
| 1104 |
random_btn.click(
|
| 1105 |
-
fn=
|
| 1106 |
inputs=[screenplay_type, genre_select],
|
| 1107 |
outputs=[query_input]
|
| 1108 |
)
|
| 1109 |
-
|
| 1110 |
-
clear_btn.click(
|
| 1111 |
-
fn=handle_clear,
|
| 1112 |
-
outputs=[query_input, progress_bar, status_text, planning_display,
|
| 1113 |
-
screenplay_output, current_session_id, current_planning_data]
|
| 1114 |
-
)
|
| 1115 |
-
|
| 1116 |
-
download_btn.click(
|
| 1117 |
-
fn=handle_download,
|
| 1118 |
-
inputs=[screenplay_output],
|
| 1119 |
-
outputs=[gr.File(label="๋ค์ด๋ก๋")]
|
| 1120 |
-
)
|
| 1121 |
|
| 1122 |
return interface
|
| 1123 |
|
| 1124 |
# ๋ฉ์ธ ์คํ
|
| 1125 |
if __name__ == "__main__":
|
| 1126 |
logger.info("=" * 60)
|
| 1127 |
-
logger.info("AI ์๋๋ฆฌ์ค ์๊ฐ
|
| 1128 |
-
logger.info("
|
| 1129 |
logger.info("=" * 60)
|
| 1130 |
|
| 1131 |
if not FIREWORKS_API_KEY or FIREWORKS_API_KEY == "dummy_token_for_testing":
|
| 1132 |
logger.warning("โ ๏ธ FIREWORKS_API_KEY๋ฅผ ์ค์ ํด์ฃผ์ธ์!")
|
| 1133 |
logger.warning("export FIREWORKS_API_KEY='your-api-key'")
|
| 1134 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1135 |
ScreenplayDatabase.init_db()
|
| 1136 |
|
| 1137 |
interface = create_interface()
|
|
|
|
| 44 |
# ๊ธ๋ก๋ฒ ๋ณ์
|
| 45 |
db_lock = threading.Lock()
|
| 46 |
|
| 47 |
+
# ์ ๋ฌธ๊ฐ ์ญํ ์ ์
|
| 48 |
+
EXPERT_ROLES = {
|
| 49 |
+
"ํ๋ก๋์": {
|
| 50 |
+
"emoji": "๐ฌ",
|
| 51 |
+
"description": "์์
์ฑ๊ณผ ์์ฅ์ฑ ๋ถ์",
|
| 52 |
+
"focus": ["ํ๊ฒ ๊ด๊ฐ", "์ ์ ๊ฐ๋ฅ์ฑ", "์์ฐ ๊ท๋ชจ", "๋ง์ผํ
ํฌ์ธํธ"],
|
| 53 |
+
"personality": "์ค์ฉ์ ์ด๊ณ ์์ฅ ์งํฅ์ "
|
| 54 |
+
},
|
| 55 |
+
"์คํ ๋ฆฌ์๊ฐ": {
|
| 56 |
+
"emoji": "๐",
|
| 57 |
+
"description": "๋ด๋ฌํฐ๋ธ ๊ตฌ์กฐ์ ํ๋กฏ ๊ฐ๋ฐ",
|
| 58 |
+
"focus": ["3๋ง ๊ตฌ์กฐ", "ํ๋กฏ ํฌ์ธํธ", "์์ฌ ์ํฌ", "ํ
๋ง"],
|
| 59 |
+
"personality": "์ฐฝ์์ ์ด๊ณ ๊ตฌ์กฐ์ "
|
| 60 |
+
},
|
| 61 |
+
"์บ๋ฆญํฐ๋์์ด๋": {
|
| 62 |
+
"emoji": "๐ฅ",
|
| 63 |
+
"description": "์ธ๋ฌผ ์ฐฝ์กฐ์ ๊ด๊ณ ์ค๊ณ",
|
| 64 |
+
"focus": ["์บ๋ฆญํฐ ์ํฌ", "๋๊ธฐ๋ถ์ฌ", "๊ด๊ณ ์ญํ", "๋ํ ์คํ์ผ"],
|
| 65 |
+
"personality": "์ฌ๋ฆฌํ์ ์ด๊ณ ๊ณต๊ฐ์ "
|
| 66 |
+
},
|
| 67 |
+
"๊ฐ๋
": {
|
| 68 |
+
"emoji": "๐ญ",
|
| 69 |
+
"description": "๋น์ฃผ์ผ ์คํ ๋ฆฌํ
๋ง๊ณผ ์ฐ์ถ",
|
| 70 |
+
"focus": ["์๊ฐ์ ๊ตฌ์ฑ", "์นด๋ฉ๋ผ ์ํฌ", "๋ฏธ์ฅ์ผ", "๋ฆฌ๋ฌ๊ณผ ํ์ด์ฑ"],
|
| 71 |
+
"personality": "๋น์ฃผ์ผ ์ค์ฌ์ ์ด๊ณ ์์ ์ "
|
| 72 |
+
},
|
| 73 |
+
"๋นํ๊ฐ": {
|
| 74 |
+
"emoji": "๐",
|
| 75 |
+
"description": "๊ฐ๊ด์ ๋ถ์๊ณผ ๊ฐ์ ์ ์ ์",
|
| 76 |
+
"focus": ["๋
ผ๋ฆฌ์ ์ผ๊ด์ฑ", "๊ฐ์ ์ ์ํฉํธ", "์์ ์ถฉ์ค๋", "์์ฑ๋"],
|
| 77 |
+
"personality": "๋ถ์์ ์ด๊ณ ๋นํ์ "
|
| 78 |
+
},
|
| 79 |
+
"ํธ์ง์": {
|
| 80 |
+
"emoji": "โ๏ธ",
|
| 81 |
+
"description": "ํ์ด์ฑ๊ณผ ๊ตฌ์กฐ ์ต์ ํ",
|
| 82 |
+
"focus": ["์ฌ ์ ํ", "๋ฆฌ๋ฌ", "๊ธด์ฅ๊ฐ ์กฐ์ ", "๋ถํ์ํ ๋ถ๋ถ ์ ๊ฑฐ"],
|
| 83 |
+
"personality": "์ ๋ฐํ๊ณ ํจ์จ์ "
|
| 84 |
+
},
|
| 85 |
+
"๋ํ์ ๋ฌธ๊ฐ": {
|
| 86 |
+
"emoji": "๐ฌ",
|
| 87 |
+
"description": "๋์ฌ์ ์๋ธํ
์คํธ ๊ฐํ",
|
| 88 |
+
"focus": ["์์ฐ์ค๋ฌ์ด ๋ํ", "์บ๋ฆญํฐ ๋ณด์ด์ค", "์๋ธํ
์คํธ", "๊ฐ์ ์ ๋ฌ"],
|
| 89 |
+
"personality": "์ธ์ด์ ์ด๊ณ ๋์์ค ์ค์ฌ"
|
| 90 |
+
},
|
| 91 |
+
"์ฅ๋ฅด์ ๋ฌธ๊ฐ": {
|
| 92 |
+
"emoji": "๐ฏ",
|
| 93 |
+
"description": "์ฅ๋ฅด ๊ด์ต๊ณผ ๊ธฐ๋์น ์ถฉ์กฑ",
|
| 94 |
+
"focus": ["์ฅ๋ฅด ๊ด์ต", "๊ด๊ฐ ๊ธฐ๋", "์ฅ๋ฅด ํน์ ์์", "ํธ๋กํ ํ์ฉ"],
|
| 95 |
+
"personality": "์ฅ๋ฅด์ ์ ํตํ"
|
| 96 |
+
}
|
| 97 |
+
}
|
| 98 |
+
|
| 99 |
# ์ฅ๋ฅด ํ
ํ๋ฆฟ
|
| 100 |
GENRE_TEMPLATES = {
|
| 101 |
"์ก์
": {
|
|
|
|
| 172 |
}
|
| 173 |
}
|
| 174 |
|
| 175 |
+
# ๊ธฐํ ๋จ๊ณ - ๋ค์ค ์ญํ ํ์
|
| 176 |
PLANNING_STAGES = [
|
| 177 |
+
("ํ๋ก๋์", "producer", "๐ฌ ํ๋ก๋์: ํต์ฌ ์ปจ์
๋ฐ ์์ฅ์ฑ ๋ถ์"),
|
| 178 |
+
("์คํ ๋ฆฌ์๊ฐ", "story_writer", "๐ ์คํ ๋ฆฌ ์๊ฐ: ์๋์์ค ๋ฐ 3๋ง ๊ตฌ์กฐ"),
|
| 179 |
+
("์บ๋ฆญํฐ๋์์ด๋", "character_designer", "๐ฅ ์บ๋ฆญํฐ ๋์์ด๋: ์ธ๋ฌผ ํ๋กํ ๋ฐ ๊ด๊ณ๋"),
|
| 180 |
+
("๊ฐ๋
", "director", "๐ญ ๊ฐ๋
: ๋น์ฃผ์ผ ์ปจ์
๋ฐ ์ฐ์ถ ๋ฐฉํฅ"),
|
| 181 |
+
("๋นํ๊ฐ", "critic", "๐ ๋นํ๊ฐ: ๊ธฐํ์ ์ข
ํฉ ๊ฒํ ๋ฐ ๊ฐ์ ์ "),
|
| 182 |
]
|
| 183 |
|
| 184 |
+
# ์์ฑ ๋จ๊ณ - ๋ค์ค ์ญํ ํ์
|
| 185 |
WRITING_STAGES = [
|
| 186 |
+
("์คํ ๋ฆฌ์๊ฐ", "1๋ง ์ด๊ณ ", "โ๏ธ 1๋ง ์ด๊ณ ์์ฑ"),
|
| 187 |
+
("ํธ์ง์", "1๋ง ํธ์ง", "โ๏ธ 1๋ง ํธ์ง ๋ฐ ํ์ด์ฑ ์กฐ์ "),
|
| 188 |
+
("๊ฐ๋
", "1๋ง ์ฐ์ถ", "๐ญ 1๋ง ๋น์ฃผ์ผ ๊ฐํ"),
|
| 189 |
+
("๋ํ์ ๋ฌธ๊ฐ", "1๋ง ๋์ฌ", "๐ฌ 1๋ง ๋์ฌ ๊ฐ์ "),
|
| 190 |
+
("๋นํ๊ฐ", "1๋ง ๊ฒํ ", "๐ 1๋ง ์ข
ํฉ ๊ฒํ "),
|
| 191 |
+
("์คํ ๋ฆฌ์๊ฐ", "1๋ง ์์ฑ", "โ
1๋ง ์ต์ข
์์ฑ๋ณธ"),
|
| 192 |
|
| 193 |
+
("์คํ ๋ฆฌ์๊ฐ", "2๋งA ์ด๊ณ ", "โ๏ธ 2๋งA ์ด๊ณ ์์ฑ"),
|
| 194 |
+
("ํธ์ง์", "2๋งA ํธ์ง", "โ๏ธ 2๋งA ํธ์ง ๋ฐ ํ์ด์ฑ ์กฐ์ "),
|
| 195 |
+
("๊ฐ๋
", "2๋งA ์ฐ์ถ", "๐ญ 2๋งA ๋น์ฃผ์ผ ๊ฐํ"),
|
| 196 |
+
("๋ํ์ ๋ฌธ๊ฐ", "2๋งA ๋์ฌ", "๐ฌ 2๋งA ๋์ฌ ๊ฐ์ "),
|
| 197 |
+
("๋นํ๊ฐ", "2๋งA ๊ฒํ ", "๐ 2๋งA ์ข
ํฉ ๊ฒํ "),
|
| 198 |
+
("์คํ ๋ฆฌ์๊ฐ", "2๋งA ์์ฑ", "โ
2๋งA ์ต์ข
์์ฑ๋ณธ"),
|
| 199 |
|
| 200 |
+
("์คํ ๋ฆฌ์๊ฐ", "2๋งB ์ด๊ณ ", "โ๏ธ 2๋งB ์ด๊ณ ์์ฑ"),
|
| 201 |
+
("ํธ์ง๏ฟฝ๏ฟฝ", "2๋งB ํธ์ง", "โ๏ธ 2๋งB ํธ์ง ๋ฐ ํ์ด์ฑ ์กฐ์ "),
|
| 202 |
+
("๊ฐ๋
", "2๋งB ์ฐ์ถ", "๐ญ 2๋งB ๋น์ฃผ์ผ ๊ฐํ"),
|
| 203 |
+
("๋ํ์ ๋ฌธ๊ฐ", "2๋งB ๋์ฌ", "๐ฌ 2๋งB ๋์ฌ ๊ฐ์ "),
|
| 204 |
+
("๋นํ๊ฐ", "2๋งB ๊ฒํ ", "๐ 2๋งB ์ข
ํฉ ๊ฒํ "),
|
| 205 |
+
("์คํ ๋ฆฌ์๊ฐ", "2๋งB ์์ฑ", "โ
2๋งB ์ต์ข
์์ฑ๋ณธ"),
|
| 206 |
|
| 207 |
+
("์คํ ๋ฆฌ์๊ฐ", "3๋ง ์ด๊ณ ", "โ๏ธ 3๋ง ์ด๊ณ ์์ฑ"),
|
| 208 |
+
("ํธ์ง์", "3๋ง ํธ์ง", "โ๏ธ 3๋ง ํธ์ง ๋ฐ ํ์ด์ฑ ์กฐ์ "),
|
| 209 |
+
("๊ฐ๋
", "3๋ง ์ฐ์ถ", "๐ญ 3๋ง ๋น์ฃผ์ผ ๊ฐํ"),
|
| 210 |
+
("๋ํ์ ๋ฌธ๊ฐ", "3๋ง ๋์ฌ", "๐ฌ 3๋ง ๋์ฌ ๊ฐ์ "),
|
| 211 |
+
("๋นํ๊ฐ", "3๋ง ๊ฒํ ", "๐ 3๋ง ์ข
ํฉ ๊ฒํ "),
|
| 212 |
+
("์คํ ๋ฆฌ์๊ฐ", "3๋ง ์์ฑ", "โ
3๋ง ์ต์ข
์์ฑ๋ณธ"),
|
| 213 |
|
| 214 |
+
("์ฅ๋ฅด์ ๋ฌธ๊ฐ", "์ฅ๋ฅด ์ต์ ํ", "๐ฏ ์ฅ๋ฅด ํน์ฑ ์ต์ ํ"),
|
| 215 |
+
("๋นํ๊ฐ", "์ต์ข
๊ฒํ ", "๐ ์ต์ข
๊ฒํ ๋ฐ ์์ฑ๋ ํ๊ฐ"),
|
| 216 |
]
|
| 217 |
|
| 218 |
# ๋ฐ์ดํฐ ํด๋์ค
|
| 219 |
+
@dataclass
|
| 220 |
+
class ExpertFeedback:
|
| 221 |
+
"""์ ๋ฌธ๊ฐ ํผ๋๋ฐฑ"""
|
| 222 |
+
role: str
|
| 223 |
+
stage: str
|
| 224 |
+
feedback: str
|
| 225 |
+
suggestions: List[str]
|
| 226 |
+
score: float
|
| 227 |
+
timestamp: datetime = field(default_factory=datetime.now)
|
| 228 |
+
|
| 229 |
@dataclass
|
| 230 |
class ScreenplayPlan:
|
| 231 |
"""์๋๋ฆฌ์ค ๊ธฐํ์"""
|
|
|
|
| 259 |
# ์ฌ ๊ตฌ์ฑ
|
| 260 |
total_scenes: int = 0
|
| 261 |
scene_breakdown: List[Dict] = field(default_factory=list)
|
| 262 |
+
|
| 263 |
+
# ์ ๋ฌธ๊ฐ ํผ๋๋ฐฑ
|
| 264 |
+
expert_feedbacks: List[ExpertFeedback] = field(default_factory=list)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 265 |
|
| 266 |
# ๋ฐ์ดํฐ๋ฒ ์ด์ค ํด๋์ค
|
| 267 |
class ScreenplayDatabase:
|
|
|
|
| 281 |
title TEXT,
|
| 282 |
logline TEXT,
|
| 283 |
planning_data TEXT,
|
| 284 |
+
expert_feedbacks TEXT,
|
| 285 |
screenplay_content TEXT,
|
| 286 |
created_at TEXT DEFAULT (datetime('now')),
|
| 287 |
updated_at TEXT DEFAULT (datetime('now')),
|
|
|
|
| 292 |
''')
|
| 293 |
|
| 294 |
cursor.execute('''
|
| 295 |
+
CREATE TABLE IF NOT EXISTS expert_reviews (
|
| 296 |
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
| 297 |
session_id TEXT NOT NULL,
|
| 298 |
+
role TEXT NOT NULL,
|
| 299 |
+
stage TEXT NOT NULL,
|
| 300 |
+
feedback TEXT,
|
| 301 |
+
suggestions TEXT,
|
| 302 |
+
score REAL,
|
|
|
|
| 303 |
created_at TEXT DEFAULT (datetime('now')),
|
| 304 |
+
FOREIGN KEY (session_id) REFERENCES screenplay_sessions(session_id)
|
|
|
|
| 305 |
)
|
| 306 |
''')
|
| 307 |
|
|
|
|
| 333 |
conn.commit()
|
| 334 |
return session_id
|
| 335 |
|
| 336 |
+
@staticmethod
|
| 337 |
+
def save_expert_feedback(session_id: str, role: str, stage: str,
|
| 338 |
+
feedback: str, suggestions: List[str], score: float):
|
| 339 |
+
with ScreenplayDatabase.get_db() as conn:
|
| 340 |
+
conn.cursor().execute(
|
| 341 |
+
'''INSERT INTO expert_reviews
|
| 342 |
+
(session_id, role, stage, feedback, suggestions, score)
|
| 343 |
+
VALUES (?, ?, ?, ?, ?, ?)''',
|
| 344 |
+
(session_id, role, stage, feedback, json.dumps(suggestions, ensure_ascii=False), score)
|
| 345 |
+
)
|
| 346 |
+
conn.commit()
|
| 347 |
+
|
| 348 |
@staticmethod
|
| 349 |
def save_planning_data(session_id: str, planning_data: Dict):
|
| 350 |
with ScreenplayDatabase.get_db() as conn:
|
|
|
|
| 356 |
)
|
| 357 |
conn.commit()
|
| 358 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 359 |
@staticmethod
|
| 360 |
def save_screenplay_content(session_id: str, content: str, title: str, logline: str):
|
| 361 |
with ScreenplayDatabase.get_db() as conn:
|
|
|
|
| 377 |
self.model_id = MODEL_ID
|
| 378 |
self.current_session_id = None
|
| 379 |
self.current_plan = ScreenplayPlan()
|
| 380 |
+
self.original_query = ""
|
| 381 |
+
self.accumulated_content = {}
|
| 382 |
+
self.expert_feedbacks = []
|
| 383 |
ScreenplayDatabase.init_db()
|
| 384 |
|
| 385 |
def create_headers(self):
|
|
|
|
| 398 |
"model": self.model_id,
|
| 399 |
"messages": messages,
|
| 400 |
"max_tokens": max_tokens,
|
| 401 |
+
"temperature": 0.7,
|
| 402 |
+
"top_p": 0.9,
|
| 403 |
"top_k": 40,
|
| 404 |
+
"presence_penalty": 0.3,
|
| 405 |
"frequency_penalty": 0.3,
|
| 406 |
"stream": True
|
| 407 |
}
|
|
|
|
| 451 |
except Exception as e:
|
| 452 |
yield f"โ ์ค๋ฅ: {str(e)}"
|
| 453 |
|
| 454 |
+
def get_expert_prompt(self, role: str, stage: str, query: str,
|
| 455 |
+
previous_content: Dict, genre: str, screenplay_type: str) -> str:
|
| 456 |
+
"""๊ฐ ์ ๋ฌธ๊ฐ๋ณ ๋ง์ถค ํ๋กฌํํธ ์์ฑ"""
|
| 457 |
+
|
| 458 |
+
expert = EXPERT_ROLES[role]
|
| 459 |
+
focus_areas = ", ".join(expert["focus"])
|
| 460 |
+
|
| 461 |
+
# ์ด์ ์ ๋ฌธ๊ฐ๋ค์ ํผ๋๋ฐฑ ์์ฝ
|
| 462 |
+
previous_feedbacks = ""
|
| 463 |
+
if self.expert_feedbacks:
|
| 464 |
+
previous_feedbacks = "\nใ์ด์ ์ ๋ฌธ๊ฐ ์๊ฒฌใ\n"
|
| 465 |
+
for fb in self.expert_feedbacks[-3:]: # ์ต๊ทผ 3๊ฐ๋ง
|
| 466 |
+
previous_feedbacks += f"[{fb.role}]: {fb.feedback[:200]}...\n"
|
| 467 |
+
|
| 468 |
+
base_prompt = f"""
|
| 469 |
+
ใ๋น์ ์ ์ญํ ใ
|
| 470 |
+
๋น์ ์ {role}์
๋๋ค. {expert['description']}
|
| 471 |
+
์ฑ๊ฒฉ: {expert['personality']}
|
| 472 |
+
์ง์ค ์์ญ: {focus_areas}
|
| 473 |
+
|
| 474 |
+
ใ์๋ณธ ์์ฒญใ
|
| 475 |
+
{query}
|
| 476 |
+
|
| 477 |
+
ใ์ฅ๋ฅดใ {genre}
|
| 478 |
+
ใํ์ใ {screenplay_type}
|
| 479 |
+
|
| 480 |
+
{previous_feedbacks}
|
| 481 |
+
|
| 482 |
+
ใ์ด์ ์์
๋ด์ฉใ
|
| 483 |
+
{self._summarize_previous_content(previous_content)}
|
| 484 |
+
"""
|
| 485 |
+
|
| 486 |
+
# ์ญํ ๋ณ ํนํ ํ๋กฌํํธ
|
| 487 |
+
if role == "ํ๋ก๋์":
|
| 488 |
+
return base_prompt + f"""
|
| 489 |
+
ใํ๋ก๋์๋ก์ ๋ถ์ํ ๋ด์ฉใ
|
| 490 |
+
1. ์์
์ ๊ฐ์น์ ์์ฅ์ฑ
|
| 491 |
+
- ํ๊ฒ ๊ด๊ฐ์ธต ๋ช
ํํ
|
| 492 |
+
- ์์ ์ ์๋น ๊ท๋ชจ
|
| 493 |
+
- ๋ฐฐ๊ธ ์ ๋ต
|
| 494 |
+
|
| 495 |
+
2. ์ ๋ชฉ๊ณผ ๋ก๊ทธ๋ผ์ธ
|
| 496 |
+
- ๋ง์ผํ
๊ฐ๋ฅํ ๋งค๋ ฅ์ ์ธ ์ ๋ชฉ
|
| 497 |
+
- ํ ๋ฌธ์ฅ์ผ๋ก ํต์ฌ ๊ฐ๋ฑ ํํ
|
| 498 |
+
|
| 499 |
+
3. ์ ์ฌ ์ฑ๊ณต์ ๋ถ์
|
| 500 |
+
- ์ต๊ทผ 3๋
๋ด ์ ์ฌ ์ํ
|
| 501 |
+
- ์ฐจ๋ณํ ํฌ์ธํธ
|
| 502 |
+
|
| 503 |
+
4. ์ ์ ๋ฆฌ์คํฌ ํ๊ฐ
|
| 504 |
+
- ๊ธฐ์ ์ ๋์ด๋
|
| 505 |
+
- ์บ์คํ
์๊ตฌ์ฌํญ
|
| 506 |
+
|
| 507 |
+
โ ๏ธ ์๋ณธ ์์ฒญ์ ์์
์ ์ผ๋ก ์ต์ ํํ๋ ํต์ฌ์ ์ ์งํ์ธ์."""
|
| 508 |
+
|
| 509 |
+
elif role == "์คํ ๋ฆฌ์๊ฐ":
|
| 510 |
+
return base_prompt + f"""
|
| 511 |
+
ใ์คํ ๋ฆฌ ์๊ฐ๋ก์ ๊ตฌ์ฑํ ๋ด์ฉใ
|
| 512 |
+
1. ์๋์์ค (500-800์)
|
| 513 |
+
- ์๋ณธ ์์ฒญ์ ํต์ฌ ์คํ ๋ฆฌ
|
| 514 |
+
- ๋ช
ํํ ์์-์ค๊ฐ-๋
|
| 515 |
+
|
| 516 |
+
2. 3๋ง ๊ตฌ์กฐ ์์ธ
|
| 517 |
+
- 1๋ง (25%): ์ค์ ๊ณผ ์ด๋ฐ ์ฌ๊ฑด
|
| 518 |
+
- 2๋งA (25%): ์๋ก์ด ์ธ๊ณ์ ์ฌ๋ฏธ
|
| 519 |
+
- 2๋งB (25%): ์๋ จ๊ณผ ์๊ธฐ
|
| 520 |
+
- 3๋ง (25%): ํด๋ผ์ด๋งฅ์ค์ ํด๊ฒฐ
|
| 521 |
+
|
| 522 |
+
3. ์ฃผ์ ํ๋กฏ ํฌ์ธํธ
|
| 523 |
+
- 10๊ฐ์ ํต์ฌ ์ ํ์
|
| 524 |
+
- ๊ฐ ์ ํ์ ์ ๊ฐ์ ์ ์ํฉํธ
|
| 525 |
+
|
| 526 |
+
4. ํ
๋ง์ ๋ฉ์์ง
|
| 527 |
+
- ์ค์ฌ ํ
๋ง
|
| 528 |
+
- ์๋ธ ํ
๋ง๋ค
|
| 529 |
+
|
| 530 |
+
โ ๏ธ ์๋ณธ ์คํ ๋ฆฌ์ ๋
ผ๋ฆฌ์ ํ๋ฆ์ ์๋ฒฝํ๊ฒ ๊ตฌํํ์ธ์."""
|
| 531 |
+
|
| 532 |
+
elif role == "์บ๋ฆญํฐ๋์์ด๋":
|
| 533 |
+
return base_prompt + f"""
|
| 534 |
+
ใ์บ๋ฆญํฐ ๋์์ด๋๋ก์ ์ฐฝ์กฐํ ๋ด์ฉใ
|
| 535 |
+
1. ์ฃผ์ธ๊ณต ์์ธ ํ๋กํ
|
| 536 |
+
- ์ด๋ฆ, ๋์ด, ์ง์
|
| 537 |
+
- ์ฑ๊ฒฉ (MBTI ํฌํจ)
|
| 538 |
+
- ํต์ฌ ์๊ตฌ(Want)์ ํ์(Need)
|
| 539 |
+
- ์น๋ช
์ ๊ฒฐํจ
|
| 540 |
+
- ๋ณํ ์ํฌ
|
| 541 |
+
- ํน์ง์ ๋งํฌ์ ํ๋
|
| 542 |
+
|
| 543 |
+
2. ์ ๋์ ํ๋กํ
|
| 544 |
+
- ์ฃผ์ธ๊ณต๊ณผ์ ๋๋ฆฝ ๊ตฌ์กฐ
|
| 545 |
+
- ๋๋ฆ์ ์ ๋น์ฑ
|
| 546 |
+
- ์ฝ์ ๊ณผ ๋งน์
|
| 547 |
+
|
| 548 |
+
3. ์กฐ์ฐ ์บ๋ฆญํฐ๋ค (3-5๋ช
)
|
| 549 |
+
- ๊ฐ์์ ์ญํ ๊ณผ ๊ธฐ๋ฅ
|
| 550 |
+
- ์ฃผ์ธ๊ณต๊ณผ์ ๊ด๊ณ
|
| 551 |
+
- ๊ณ ์ ํ ํน์ง
|
| 552 |
+
|
| 553 |
+
4. ์บ๋ฆญํฐ ๊ด๊ณ๋
|
| 554 |
+
- ์ธ๋ฌผ๊ฐ ์ญํ ๊ด๊ณ
|
| 555 |
+
- ๊ฐ๋ฑ ๊ตฌ์กฐ
|
| 556 |
+
|
| 557 |
+
โ ๏ธ ์๋ณธ์ ๋ช
์๋ ์ธ๋ฌผ ์ค์ ์ ์ถฉ์คํ ๋ฐ์ ์ํค์ธ์."""
|
| 558 |
+
|
| 559 |
+
elif role == "๊ฐ๋
":
|
| 560 |
+
return base_prompt + f"""
|
| 561 |
+
ใ๊ฐ๋
์ผ๋ก์ ์ฐ์ถํ ๋ด์ฉใ
|
| 562 |
+
1. ๋น์ฃผ์ผ ์ปจ์
|
| 563 |
+
- ์ ์ฒด์ ์ธ ๋ฃฉ์คํ
|
| 564 |
+
- ์๊ฐ๊ณผ ํค
|
| 565 |
+
- ์ฐธ์กฐ ์ํ/์ด๋ฏธ์ง
|
| 566 |
+
|
| 567 |
+
2. ํต์ฌ ๋น์ฃผ์ผ ์ฌ (5๊ฐ)
|
| 568 |
+
- ์คํ๋ ์ด๋ฏธ์ง
|
| 569 |
+
- ์ฃผ์ ์ก์
/๊ฐ์ ์ฌ
|
| 570 |
+
- ํด๋ผ์ด๋งฅ์ค ๋น์ฃผ์ผ
|
| 571 |
+
- ์๋ฉ ์ด๋ฏธ์ง
|
| 572 |
+
|
| 573 |
+
3. ์นด๋ฉ๋ผ ์คํ์ผ
|
| 574 |
+
- ์ฃผ์ ์ท ๊ตฌ์ฑ
|
| 575 |
+
- ์์ง์๊ณผ ์ต๊ธ
|
| 576 |
+
- ํน์ ์ดฌ์ ๊ธฐ๋ฒ
|
| 577 |
+
|
| 578 |
+
4. ์ฌ์ด๋ ๋์์ธ
|
| 579 |
+
- ์์
์คํ์ผ
|
| 580 |
+
- ํต์ฌ ์ฌ์ด๋ ๋ชจํฐํ
|
| 581 |
+
- ์นจ๋ฌต์ ํ์ฉ
|
| 582 |
+
|
| 583 |
+
โ ๏ธ ์๋ณธ์ ๋ถ์๊ธฐ์ ํค์ ์๊ฐ์ ์ผ๋ก ๊ตฌํํ์ธ์."""
|
| 584 |
+
|
| 585 |
+
elif role == "๋นํ๊ฐ":
|
| 586 |
+
return base_prompt + f"""
|
| 587 |
+
ใ๋นํ๊ฐ๋ก์ ๊ฒํ ํ ๋ด์ฉใ
|
| 588 |
+
1. ์๋ณธ ์ถฉ์ค๋ ํ๊ฐ (40์ )
|
| 589 |
+
- ํต์ฌ ์์ฒญ์ฌํญ ๋ฐ์๋
|
| 590 |
+
- ์์ ๋ณ๊ฒฝ ์ฌํญ ์ง์
|
| 591 |
+
|
| 592 |
+
2. ์คํ ๋ฆฌ ์์ฑ๋ (20์ )
|
| 593 |
+
- ๋
ผ๋ฆฌ์ ์ผ๊ด์ฑ
|
| 594 |
+
- ๊ฐ์ ์ ํธ์๋ ฅ
|
| 595 |
+
- ํ์ด์ฑ๊ณผ ๋ฆฌ๋ฌ
|
| 596 |
+
|
| 597 |
+
3. ์บ๋ฆญํฐ ํ๊ฐ (20์ )
|
| 598 |
+
- ์
์ฒด์ฑ๊ณผ ๋งค๋ ฅ
|
| 599 |
+
- ์ฑ์ฅ ์ํฌ
|
| 600 |
+
- ๊ด๊ณ ์ญํ
|
| 601 |
+
|
| 602 |
+
4. ์์
์ฑ๊ณผ ์์ ์ฑ (20์ )
|
| 603 |
+
- ์์ฅ ์ดํ
|
| 604 |
+
- ๋
์ฐฝ์ฑ
|
| 605 |
+
- ์ ์ ๊ฐ๋ฅ์ฑ
|
| 606 |
+
|
| 607 |
+
5. ๊ตฌ์ฒด์ ๊ฐ์ ์ ์
|
| 608 |
+
- ์ฐ์ ์์๋ณ ๊ฐ์ ์
|
| 609 |
+
- ์คํ ๊ฐ๋ฅํ ํด๊ฒฐ์ฑ
|
| 610 |
+
|
| 611 |
+
์ด์ : /100์
|
| 612 |
+
โ ๏ธ ๊ฑด์ค์ ์ด๋ฉด์๋ ์ ์งํ ํผ๋๋ฐฑ์ ์ ๊ณตํ์ธ์."""
|
| 613 |
+
|
| 614 |
+
elif role == "ํธ์ง์":
|
| 615 |
+
return base_prompt + f"""
|
| 616 |
+
ใํธ์ง์๋ก์ ์กฐ์ ํ ๋ด์ฉใ
|
| 617 |
+
1. ํ์ด์ฑ ๋ถ์
|
| 618 |
+
- ๊ฐ ์ฌ์ ๊ธธ์ด ์ ์ ์ฑ
|
| 619 |
+
- ๊ธด์ฅ๊ณผ ์ด์์ ๋ฆฌ๋ฌ
|
| 620 |
+
- ๋ถํ์ํ ๋ถ๋ถ ์๋ณ
|
| 621 |
+
|
| 622 |
+
2. ๊ตฌ์กฐ ์ต์ ํ
|
| 623 |
+
- ์ฌ ์์ ์กฐ์ ์ ์
|
| 624 |
+
- ์ ํ์ ์์ฐ์ค๋ฌ์
|
| 625 |
+
- ์ ๋ณด ๊ณต๊ฐ ํ์ด๋ฐ
|
| 626 |
+
|
| 627 |
+
3. ํธ๋ฆฌ๋ฐ ์ ์
|
| 628 |
+
- ์ญ์ ๊ฐ๋ฅํ ์ฌ/๋์ฌ
|
| 629 |
+
- ์์ถ ๊ฐ๋ฅํ ๋ถ๋ถ
|
| 630 |
+
- ์ค๋ณต ์ ๊ฑฐ
|
| 631 |
+
|
| 632 |
+
4. ์ํฉํธ ๊ฐํ
|
| 633 |
+
- ํด๋ผ์ด๋งฅ์ค ๋น๋์
|
| 634 |
+
- ๊ฐ์ ์ ๋นํธ ๊ฐํ
|
| 635 |
+
|
| 636 |
+
โ ๏ธ ์๋ณธ ์คํ ๋ฆฌ๋ฅผ ๋ ํ์ดํธํ๊ณ ์ํฉํธ์๊ฒ ๋ง๋์ธ์."""
|
| 637 |
+
|
| 638 |
+
elif role == "๋ํ์ ๋ฌธ๊ฐ":
|
| 639 |
+
return base_prompt + f"""
|
| 640 |
+
ใ๋ํ ์ ๋ฌธ๊ฐ๋ก์ ๊ฐ์ ํ ๋ด์ฉใ
|
| 641 |
+
1. ์บ๋ฆญํฐ๋ณ ๊ณ ์ ํ๋ฒ
|
| 642 |
+
- ์ดํ ์์ค๊ณผ ์คํ์ผ
|
| 643 |
+
- ๋ง๋ฒ๋ฆ๊ณผ ํจํด
|
| 644 |
+
- ๊ฐ์ ํํ ๋ฐฉ์
|
| 645 |
+
|
| 646 |
+
2. ์๋ธํ
์คํธ ๊ฐํ
|
| 647 |
+
- ํ๋ฉด ๋์ฌ vs ์ง์ง ์๋ฏธ
|
| 648 |
+
- ์นจ๋ฌต๊ณผ ํ๊ฐ
|
| 649 |
+
- ๋น์ธ์ด์ ์ํต
|
| 650 |
+
|
| 651 |
+
3. ํต์ฌ ๋์ฌ ์ฐฝ์กฐ
|
| 652 |
+
- ๋ช
๋์ฌ 5๊ฐ
|
| 653 |
+
- ์บ๋ฆญํฐ ์ ์ฒด์ฑ ๋๋ฌ๋ด๊ธฐ
|
| 654 |
+
- ํ
๋ง ์ ๋ฌ ๋์ฌ
|
| 655 |
+
|
| 656 |
+
4. ๋ํ ๋ฆฌ๋ฌ
|
| 657 |
+
- ์์ฐ์ค๋ฌ์ด ์ฃผ๊ณ ๋ฐ๊ธฐ
|
| 658 |
+
- ๊ธด์ฅ ๊ณ ์กฐ ํจํด
|
| 659 |
+
- ์ ๋จธ์ ์ํธ
|
| 660 |
+
|
| 661 |
+
โ ๏ธ ๊ฐ ์บ๋ฆญํฐ์ ๋ชฉ์๋ฆฌ๋ฅผ ๋๋ ทํ๊ฒ ๊ตฌ๋ถํ์ธ์."""
|
| 662 |
+
|
| 663 |
+
elif role == "์ฅ๋ฅด์ ๋ฌธ๊ฐ":
|
| 664 |
+
genre_template = GENRE_TEMPLATES.get(genre, GENRE_TEMPLATES["๋๋ผ๋ง"])
|
| 665 |
+
return base_prompt + f"""
|
| 666 |
+
ใ{genre} ์ฅ๋ฅด ์ ๋ฌธ๊ฐ๋ก์ ์ต์ ํํ ๋ด์ฉใ
|
| 667 |
+
1. ์ฅ๋ฅด ํ์ ์์ ์ฒดํฌ
|
| 668 |
+
- ํต์ฌ ์์: {', '.join(genre_template['key_elements'])}
|
| 669 |
+
- ๊ตฌ์กฐ ๋นํธ: {', '.join(genre_template['structure_beats'])}
|
| 670 |
+
|
| 671 |
+
2. ์ฅ๋ฅด ๊ด์ต ์ถฉ์กฑ๋
|
| 672 |
+
- ๊ด๊ฐ ๊ธฐ๋ ์ถฉ์กฑ
|
| 673 |
+
- ํด๋ฆฌ์
ฐ ํ์ฉ๊ณผ ์ ๋ณต
|
| 674 |
+
- ์ฅ๋ฅด ํน์ ์ฅ์น
|
| 675 |
+
|
| 676 |
+
3. ํค๊ณผ ๋ถ์๊ธฐ
|
| 677 |
+
- ํ์ด์ฑ: {genre_template['pacing']}
|
| 678 |
+
- ์ฌ ๊ธธ์ด: {genre_template['scene_length']}
|
| 679 |
+
- ๋์ฌ ๋น์จ: {genre_template['dialogue_ratio']}
|
| 680 |
+
|
| 681 |
+
4. ์ฅ๋ฅด ํนํ ๊ฐํ
|
| 682 |
+
- ๋ถ์กฑํ ์์ ์ถ๊ฐ
|
| 683 |
+
- ๊ณผ๋ํ ๋ถ๋ถ ์กฐ์
|
| 684 |
+
- ์ฅ๋ฅด ์ ์ฒด์ฑ ๋ช
ํํ
|
| 685 |
+
|
| 686 |
+
โ ๏ธ {genre} ์ฅ๋ฅด์ ์ ์๋ฅผ ์๋ฒฝํ๊ฒ ๊ตฌํํ์ธ์."""
|
| 687 |
+
|
| 688 |
+
return base_prompt
|
| 689 |
+
|
| 690 |
+
def _summarize_previous_content(self, content: Dict) -> str:
|
| 691 |
+
"""์ด์ ๋ด์ฉ ์์ฝ"""
|
| 692 |
+
summary = ""
|
| 693 |
+
for key, value in content.items():
|
| 694 |
+
if value:
|
| 695 |
+
summary += f"\n[{key}]\n{value[:300]}...\n"
|
| 696 |
+
return summary if summary else "์ฒซ ์์
์
๋๋ค."
|
| 697 |
+
|
| 698 |
def generate_planning(self, query: str, screenplay_type: str, genre: str,
|
| 699 |
+
progress_callback=None) -> Generator[Tuple[str, float, Dict, List], None, None]:
|
| 700 |
+
"""๋ค์ค ์ ๋ฌธ๊ฐ ํ์
๊ธฐํ์ ์์ฑ"""
|
| 701 |
try:
|
| 702 |
+
self.original_query = query
|
| 703 |
self.current_session_id = ScreenplayDatabase.create_session(query, screenplay_type, genre)
|
| 704 |
|
| 705 |
planning_content = {}
|
| 706 |
+
self.accumulated_content = {}
|
| 707 |
+
self.expert_feedbacks = []
|
| 708 |
total_stages = len(PLANNING_STAGES)
|
| 709 |
|
| 710 |
+
for idx, (role, stage_key, stage_desc) in enumerate(PLANNING_STAGES):
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 711 |
progress = (idx / total_stages) * 100
|
| 712 |
|
| 713 |
+
yield f"๐ {stage_desc} ์งํ ์ค...", progress, planning_content, self.expert_feedbacks
|
| 714 |
|
| 715 |
+
# ์ ๋ฌธ๊ฐ๋ณ ํ๋กฌํํธ ์์ฑ
|
| 716 |
+
prompt = self.get_expert_prompt(
|
| 717 |
+
role, stage_key, query,
|
| 718 |
+
self.accumulated_content, genre, screenplay_type
|
| 719 |
+
)
|
|
|
|
| 720 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 721 |
# LLM ํธ์ถ
|
| 722 |
messages = [
|
| 723 |
+
{"role": "system", "content": f"""๋น์ ์ {role}์
๋๋ค.
|
| 724 |
+
{EXPERT_ROLES[role]['description']}
|
| 725 |
+
์ ๋ฌธ ๋ถ์ผ: {', '.join(EXPERT_ROLES[role]['focus'])}
|
| 726 |
+
์๋ณธ ์์ฒญ์ ์ ๋์ ์ผ๋ก ์ค์ํ๋ฉด์ ์ ๋ฌธ์ฑ์ ๋ฐํํ์ธ์."""},
|
| 727 |
{"role": "user", "content": prompt}
|
| 728 |
]
|
| 729 |
|
| 730 |
content = ""
|
| 731 |
for chunk in self.call_llm_streaming(messages):
|
| 732 |
content += chunk
|
| 733 |
+
planning_content[f"{role}_{stage_key}"] = content
|
| 734 |
+
yield f"โ๏ธ {stage_desc} ์์ฑ ์ค...", progress, planning_content, self.expert_feedbacks
|
| 735 |
+
|
| 736 |
+
# ์ ๋ฌธ๊ฐ ํผ๋๋ฐฑ ์ ์ฅ
|
| 737 |
+
feedback = ExpertFeedback(
|
| 738 |
+
role=role,
|
| 739 |
+
stage=stage_key,
|
| 740 |
+
feedback=content[:500],
|
| 741 |
+
suggestions=self._extract_suggestions(content),
|
| 742 |
+
score=self._calculate_score(content, query)
|
| 743 |
+
)
|
| 744 |
+
self.expert_feedbacks.append(feedback)
|
| 745 |
+
|
| 746 |
+
# DB์ ํผ๋๋ฐฑ ์ ์ฅ
|
| 747 |
+
ScreenplayDatabase.save_expert_feedback(
|
| 748 |
+
self.current_session_id, role, stage_key,
|
| 749 |
+
feedback.feedback, feedback.suggestions, feedback.score
|
| 750 |
+
)
|
| 751 |
|
| 752 |
# ๋์ ๋ด์ฉ ์ ์ฅ
|
| 753 |
+
self.accumulated_content[f"{role}_{stage_key}"] = content
|
| 754 |
+
time.sleep(0.5)
|
| 755 |
|
| 756 |
# ์ต์ข
๊ธฐํ์ ์ ์ฅ
|
| 757 |
ScreenplayDatabase.save_planning_data(self.current_session_id, planning_content)
|
| 758 |
+
yield "โ
์ ๋ฌธ๊ฐ ํ์
๊ธฐํ์ ์์ฑ!", 100, planning_content, self.expert_feedbacks
|
| 759 |
|
| 760 |
except Exception as e:
|
| 761 |
+
yield f"โ ์ค๋ฅ ๋ฐ์: {str(e)}", 0, {}, []
|
| 762 |
|
| 763 |
def generate_screenplay(self, session_id: str, planning_data: Dict,
|
| 764 |
+
progress_callback=None) -> Generator[Tuple[str, float, str, List], None, None]:
|
| 765 |
+
"""๋ค์ค ์ ๋ฌธ๊ฐ ํ์
์๋๋ฆฌ์ค ์์ฑ"""
|
| 766 |
try:
|
| 767 |
total_stages = len(WRITING_STAGES)
|
| 768 |
screenplay_content = ""
|
| 769 |
act_contents = {"1๋ง": "", "2๋งA": "", "2๋งB": "", "3๋ง": ""}
|
| 770 |
+
self.expert_feedbacks = []
|
| 771 |
|
| 772 |
+
for idx, (role, stage_name, stage_desc) in enumerate(WRITING_STAGES):
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 773 |
progress = (idx / total_stages) * 100
|
| 774 |
|
| 775 |
+
yield f"๐ {stage_desc} ์งํ ์ค...", progress, screenplay_content, self.expert_feedbacks
|
| 776 |
|
| 777 |
+
# ํ์ฌ ๋ง ๊ฒฐ์
|
| 778 |
current_act = ""
|
| 779 |
if "1๋ง" in stage_name:
|
| 780 |
current_act = "1๋ง"
|
|
|
|
| 785 |
elif "3๋ง" in stage_name:
|
| 786 |
current_act = "3๋ง"
|
| 787 |
|
| 788 |
+
# ์ญํ ๋ณ ํ๋กฌํํธ ์์ฑ
|
| 789 |
+
if role == "์คํ ๋ฆฌ์๊ฐ":
|
| 790 |
+
prompt = self._create_writer_prompt(
|
| 791 |
+
current_act, planning_data, act_contents, stage_name
|
|
|
|
| 792 |
)
|
| 793 |
+
elif role == "ํธ์ง์":
|
| 794 |
+
prompt = self._create_editor_prompt(
|
| 795 |
+
current_act, act_contents[current_act] if current_act else screenplay_content
|
|
|
|
| 796 |
)
|
| 797 |
+
elif role == "๊ฐ๋
":
|
| 798 |
+
prompt = self._create_director_prompt(
|
| 799 |
+
current_act, act_contents[current_act] if current_act else screenplay_content
|
| 800 |
+
)
|
| 801 |
+
elif role == "๋ํ์ ๋ฌธ๊ฐ":
|
| 802 |
+
prompt = self._create_dialogue_prompt(
|
| 803 |
+
current_act, act_contents[current_act] if current_act else screenplay_content
|
| 804 |
)
|
| 805 |
+
elif role == "๋นํ๊ฐ":
|
| 806 |
+
prompt = self._create_critic_prompt(
|
| 807 |
+
current_act, act_contents[current_act] if current_act else screenplay_content,
|
| 808 |
self.original_query
|
| 809 |
)
|
| 810 |
+
elif role == "์ฅ๋ฅด์ ๋ฌธ๊ฐ":
|
| 811 |
+
prompt = self._create_genre_expert_prompt(
|
| 812 |
+
screenplay_content, planning_data
|
| 813 |
+
)
|
| 814 |
else:
|
| 815 |
continue
|
| 816 |
|
| 817 |
# LLM ํธ์ถ
|
| 818 |
+
expert = EXPERT_ROLES.get(role, EXPERT_ROLES["์คํ ๋ฆฌ์๊ฐ"])
|
| 819 |
messages = [
|
| 820 |
+
{"role": "system", "content": f"""๋น์ ์ {role}์
๋๋ค.
|
| 821 |
+
{expert['description']}
|
| 822 |
+
์๋ณธ ์์ฒญ: {self.original_query}
|
| 823 |
+
ํ์ค ์๋๋ฆฌ์ค ํฌ๋งท์ ์ค์ํ์ธ์."""},
|
| 824 |
{"role": "user", "content": prompt}
|
| 825 |
]
|
| 826 |
|
|
|
|
| 828 |
for chunk in self.call_llm_streaming(messages, max_tokens=15000):
|
| 829 |
content += chunk
|
| 830 |
|
| 831 |
+
# ์์ฑ ๋จ๊ณ์์๋ง ๋ง ๋ด์ฉ ์
๋ฐ์ดํธ
|
| 832 |
if current_act and "์์ฑ" in stage_name:
|
| 833 |
act_contents[current_act] = content
|
| 834 |
+
screenplay_content = "\n\n".join([
|
| 835 |
+
act_contents[act] for act in ["1๋ง", "2๋งA", "2๋งB", "3๋ง"]
|
| 836 |
+
if act_contents[act]
|
| 837 |
+
])
|
| 838 |
|
| 839 |
+
yield f"โ๏ธ {stage_desc} ์์ฑ ์ค...", progress, screenplay_content, self.expert_feedbacks
|
| 840 |
+
|
| 841 |
+
# ์ ๋ฌธ๊ฐ ํผ๋๋ฐฑ ์์ฑ
|
| 842 |
+
feedback = ExpertFeedback(
|
| 843 |
+
role=role,
|
| 844 |
+
stage=stage_name,
|
| 845 |
+
feedback=f"{role}๊ฐ {stage_name}์ ๊ฒํ /์์
ํ์ต๋๋ค.",
|
| 846 |
+
suggestions=[],
|
| 847 |
+
score=85.0
|
| 848 |
+
)
|
| 849 |
+
self.expert_feedbacks.append(feedback)
|
| 850 |
|
| 851 |
time.sleep(0.5)
|
| 852 |
|
| 853 |
# ์ต์ข
์ ์ฅ
|
| 854 |
+
title = self._extract_title(planning_data)
|
| 855 |
+
logline = self._extract_logline(planning_data)
|
| 856 |
|
| 857 |
ScreenplayDatabase.save_screenplay_content(session_id, screenplay_content, title, logline)
|
| 858 |
+
yield "โ
์ ๋ฌธ๊ฐ ํ์
์๋๋ฆฌ์ค ์์ฑ!", 100, screenplay_content, self.expert_feedbacks
|
| 859 |
|
| 860 |
except Exception as e:
|
| 861 |
+
yield f"โ ์ค๋ฅ ๋ฐ์: {str(e)}", 0, "", []
|
| 862 |
|
| 863 |
+
def _create_writer_prompt(self, act: str, planning_data: Dict,
|
| 864 |
+
previous_acts: Dict, stage_name: str) -> str:
|
| 865 |
+
"""์คํ ๋ฆฌ ์๊ฐ ํ๋กฌํํธ"""
|
| 866 |
|
| 867 |
+
if "์ด๊ณ " in stage_name:
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 868 |
min_lines = 800
|
| 869 |
+
elif "์์ฑ" in stage_name:
|
|
|
|
|
|
|
|
|
|
|
|
|
| 870 |
min_lines = 1200
|
| 871 |
+
else:
|
| 872 |
+
min_lines = 1000
|
| 873 |
|
| 874 |
+
return f"""ใ{act} ์์ฑใ
|
| 875 |
+
๋ชฉํ ๋ถ๋: {min_lines}์ค ์ด์
|
|
|
|
|
|
|
|
|
|
|
|
|
| 876 |
|
| 877 |
+
ใ๊ธฐํ์ ํต์ฌใ
|
| 878 |
+
{self._extract_planning_core(planning_data)}
|
|
|
|
| 879 |
|
| 880 |
+
ใ์ด์ ๋งใ
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 881 |
{previous_acts if previous_acts else "์ฒซ ๋ง์
๋๋ค"}
|
| 882 |
|
| 883 |
+
ใ์์ฑ ์๊ตฌ์ฌํญใ
|
| 884 |
+
1. ํ์ค ์๋๋ฆฌ์ค ํฌ๋งท
|
| 885 |
+
2. ์ถฉ๋ถํ ์ฌ ๋ถ๋ (๊ฐ 5-7ํ์ด์ง)
|
| 886 |
+
3. ์๊ฐ์ ์ก์
๋ฌ์ฌ
|
| 887 |
+
4. ์์ฐ์ค๋ฌ์ด ๋ํ
|
| 888 |
+
5. ์๋ณธ ์คํ ๋ฆฌ ์ถฉ์ค
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 889 |
|
| 890 |
+
๋ฐ๋์ {min_lines}์ค ์ด์ ์์ฑํ์ธ์."""
|
|
|
|
|
|
|
| 891 |
|
| 892 |
+
def _create_editor_prompt(self, act: str, content: str) -> str:
|
| 893 |
+
"""ํธ์ง์ ํ๋กฌํํธ"""
|
| 894 |
+
return f"""ใ{act} ํธ์งใ
|
|
|
|
| 895 |
|
| 896 |
+
ํ์ฌ ๋ด์ฉ:
|
| 897 |
+
{content[:2000]}...
|
| 898 |
|
| 899 |
+
ใํธ์ง ํฌ์ธํธใ
|
| 900 |
+
1. ํ์ด์ฑ ์กฐ์
|
| 901 |
+
- ๋๋ฆฐ ๋ถ๋ถ ๊ฐ์
|
| 902 |
+
- ๊ธํ ๋ถ๋ถ ์ํ
|
| 903 |
+
|
| 904 |
+
2. ๋ถํ์ํ ๋ถ๋ถ ์ ๊ฑฐ
|
| 905 |
+
- ์ค๋ณต ๋์ฌ/์ก์
|
| 906 |
+
- ๊ณผ๋ํ ์ค๋ช
|
| 907 |
+
|
| 908 |
+
3. ์ฌ ์ ํ ๊ฐ์
|
| 909 |
+
- ์์ฐ์ค๋ฌ์ด ์ฐ๊ฒฐ
|
| 910 |
+
- ์๊ฐ/๊ณต๊ฐ ์ด๋
|
| 911 |
+
|
| 912 |
+
4. ๊ธด์ฅ๊ฐ ์กฐ์
|
| 913 |
+
- ํด๋ผ์ด๋งฅ์ค ๋น๋์
|
| 914 |
+
- ์๊ธ ์กฐ์
|
| 915 |
+
|
| 916 |
+
๊ตฌ์ฒด์ ์ธ ํธ์ง ์ ์์ ํ์ธ์."""
|
| 917 |
+
|
| 918 |
+
def _create_director_prompt(self, act: str, content: str) -> str:
|
| 919 |
+
"""๊ฐ๋
ํ๋กฌํํธ"""
|
| 920 |
+
return f"""ใ{act} ์ฐ์ถ ๊ฐํใ
|
| 921 |
+
|
| 922 |
+
ํ์ฌ ์๋๋ฆฌ์ค:
|
| 923 |
{content[:2000]}...
|
| 924 |
|
| 925 |
+
ใ๋น์ฃผ์ผ ๊ฐํ ํฌ์ธํธใ
|
| 926 |
+
1. ์คํ๋ ์ฌ ๋น์ฃผ์ผ
|
| 927 |
+
2. ํต์ฌ ์ก์
์ํ์ค
|
| 928 |
+
3. ๊ฐ์ ์ ํด๋ก์ฆ์
|
| 929 |
+
4. ํ๊ฒฝ/๋ถ์๊ธฐ ๋ฌ์ฌ
|
| 930 |
+
5. ์์ง์ ์ด๋ฏธ์ง
|
| 931 |
|
| 932 |
+
๊ฐ ์ฌ์ ์๊ฐ์ ๋ํ
์ผ์ ์ถ๊ฐํ์ธ์."""
|
|
|
|
|
|
|
|
|
|
| 933 |
|
| 934 |
+
def _create_dialogue_prompt(self, act: str, content: str) -> str:
|
| 935 |
+
"""๋ํ ์ ๋ฌธ๊ฐ ํ๋กฌํํธ"""
|
| 936 |
+
return f"""ใ{act} ๋์ฌ ๊ฐ์ ใ
|
| 937 |
|
| 938 |
+
ํ์ฌ ๋์ฌ ๊ฒํ :
|
| 939 |
+
{content[:2000]}...
|
|
|
|
| 940 |
|
| 941 |
+
ใ๊ฐ์ ๋ฐฉํฅใ
|
| 942 |
+
1. ์บ๋ฆญํฐ๋ณ ๋งํฌ ์ฐจ๋ณํ
|
| 943 |
2. ์๋ธํ
์คํธ ์ถ๊ฐ
|
| 944 |
+
3. ๋ถํ์ํ ์ค๋ช
์ ๊ฑฐ
|
| 945 |
+
4. ๊ฐ์ ์ ๋์์ค ๊ฐํ
|
| 946 |
+
5. ๋ฆฌ๋ฌ๊ณผ ํ
ํฌ ์กฐ์
|
| 947 |
|
| 948 |
+
๊ฐ์ ๋ ๋์ฌ ์์๋ฅผ ์ ์ํ์ธ์."""
|
|
|
|
|
|
|
| 949 |
|
| 950 |
+
def _create_critic_prompt(self, act: str, content: str, original_query: str) -> str:
|
| 951 |
+
"""๋นํ๊ฐ ํ๋กฌํํธ"""
|
| 952 |
+
return f"""ใ{act} ์ข
ํฉ ๊ฒํ ใ
|
| 953 |
|
| 954 |
+
์๋ณธ ์์ฒญ: {original_query}
|
| 955 |
+
|
| 956 |
+
ํ์ฌ ๋ด์ฉ:
|
| 957 |
+
{content[:2000]}...
|
| 958 |
|
| 959 |
ใํ๊ฐ ํญ๋ชฉใ
|
| 960 |
+
1. ์๋ณธ ์ถฉ์ค๋ (40์ )
|
| 961 |
+
2. ์คํ ๋ฆฌ ์์ฑ๋ (20์ )
|
| 962 |
+
3. ์บ๋ฆญํฐ ๋งค๋ ฅ (20์ )
|
| 963 |
+
4. ๋์ฌ ํ์ง (10์ )
|
| 964 |
+
5. ๋น์ฃผ์ผ ์ํฉํธ (10์ )
|
| 965 |
|
| 966 |
+
์ด์ : /100
|
|
|
|
|
|
|
|
|
|
| 967 |
|
| 968 |
+
ใ๊ฐ์ ํ์์ฌํญใ
|
| 969 |
+
๊ตฌ์ฒด์ ์ด๊ณ ์คํ ๊ฐ๋ฅํ ์ ์์ ํ์ธ์."""
|
| 970 |
+
|
| 971 |
+
def _create_genre_expert_prompt(self, screenplay: str, planning_data: Dict) -> str:
|
| 972 |
+
"""์ฅ๋ฅด ์ ๋ฌธ๊ฐ ํ๋กฌํํธ"""
|
| 973 |
+
genre = planning_data.get("์ฅ๋ฅด", "๋๋ผ๋ง")
|
| 974 |
+
genre_template = GENRE_TEMPLATES.get(genre, GENRE_TEMPLATES["๋๋ผ๋ง"])
|
| 975 |
+
|
| 976 |
+
return f"""ใ{genre} ์ฅ๋ฅด ์ต์ ํใ
|
| 977 |
+
|
| 978 |
+
ใ์ฅ๋ฅด ํน์ฑใ
|
| 979 |
+
- ํต์ฌ ์์: {', '.join(genre_template['key_elements'])}
|
| 980 |
+
- ๊ตฌ์กฐ ๋นํธ: {', '.join(genre_template['structure_beats'])}
|
| 981 |
+
|
| 982 |
+
ใ์ต์ ํ ํฌ์ธํธใ
|
| 983 |
+
1. ์ฅ๋ฅด ๊ด์ต ์ถฉ์กฑ๋
|
| 984 |
+
2. ํ์ด์ฑ ์กฐ์
|
| 985 |
+
3. ํน์ ์์ ๊ฐํ
|
| 986 |
+
4. ํค ์ผ๊ด์ฑ
|
| 987 |
+
|
| 988 |
+
์ฅ๋ฅด ํน์ฑ์ ๊ทน๋ํํ๋ ์ ์์ ํ์ธ์."""
|
| 989 |
+
|
| 990 |
+
def _extract_suggestions(self, content: str) -> List[str]:
|
| 991 |
+
"""๋ด์ฉ์์ ์ ์์ฌํญ ์ถ์ถ"""
|
| 992 |
+
suggestions = []
|
| 993 |
+
lines = content.split('\n')
|
| 994 |
+
for line in lines:
|
| 995 |
+
if any(keyword in line for keyword in ['์ ์', '๊ฐ์ ', '์ถ์ฒ', '๊ถ์ฅ']):
|
| 996 |
+
suggestions.append(line.strip())
|
| 997 |
+
return suggestions[:5]
|
| 998 |
+
|
| 999 |
+
def _calculate_score(self, content: str, original_query: str) -> float:
|
| 1000 |
+
"""์ถฉ์ค๋ ์ ์ ๊ณ์ฐ"""
|
| 1001 |
+
score = 70.0 # ๊ธฐ๋ณธ ์ ์
|
| 1002 |
+
|
| 1003 |
+
# ์๋ณธ ํค์๋ ํฌํจ๋ ์ฒดํฌ
|
| 1004 |
+
keywords = original_query.split()
|
| 1005 |
+
matched = sum(1 for keyword in keywords if keyword in content)
|
| 1006 |
+
score += (matched / len(keywords)) * 20 if keywords else 0
|
| 1007 |
+
|
| 1008 |
+
# ๊ธธ์ด ์ฒดํฌ
|
| 1009 |
+
if len(content) > 500:
|
| 1010 |
+
score += 10
|
| 1011 |
+
|
| 1012 |
+
return min(score, 100.0)
|
| 1013 |
+
|
| 1014 |
+
def _extract_title(self, planning_data: Dict) -> str:
|
| 1015 |
+
"""๊ธฐํ์์์ ์ ๋ชฉ ์ถ์ถ"""
|
| 1016 |
+
for key, value in planning_data.items():
|
| 1017 |
+
if "์ ๋ชฉ" in value:
|
| 1018 |
+
match = re.search(r'์ ๋ชฉ[:\s]*([^\n]+)', value)
|
| 1019 |
+
if match:
|
| 1020 |
+
return match.group(1).strip()
|
| 1021 |
+
return "๋ฌด์ "
|
| 1022 |
+
|
| 1023 |
+
def _extract_logline(self, planning_data: Dict) -> str:
|
| 1024 |
+
"""๊ธฐํ์์์ ๋ก๊ทธ๋ผ์ธ ์ถ์ถ"""
|
| 1025 |
+
for key, value in planning_data.items():
|
| 1026 |
+
if "๋ก๊ทธ๋ผ์ธ" in value:
|
| 1027 |
+
match = re.search(r'๋ก๊ทธ๋ผ์ธ[:\s]*([^\n]+)', value)
|
| 1028 |
+
if match:
|
| 1029 |
+
return match.group(1).strip()
|
| 1030 |
+
return ""
|
| 1031 |
+
|
| 1032 |
+
def _extract_planning_core(self, planning_data: Dict) -> str:
|
| 1033 |
+
"""๊ธฐํ์ ํต์ฌ ๋ด์ฉ ์ถ์ถ"""
|
| 1034 |
+
core = ""
|
| 1035 |
+
for key, value in planning_data.items():
|
| 1036 |
+
if any(k in key for k in ["์คํ ๋ฆฌ", "์บ๋ฆญํฐ", "๊ตฌ์กฐ"]):
|
| 1037 |
+
core += f"\n[{key}]\n{value[:500]}...\n"
|
| 1038 |
+
return core
|
| 1039 |
|
| 1040 |
# ์ ํธ๋ฆฌํฐ ํจ์
|
| 1041 |
def format_planning_display(planning_data: Dict) -> str:
|
|
|
|
| 1043 |
if not planning_data:
|
| 1044 |
return "๊ธฐํ์์ด ์์ง ์์ฑ๋์ง ์์์ต๋๋ค."
|
| 1045 |
|
| 1046 |
+
formatted = "# ๐ ์๋๋ฆฌ์ค ๊ธฐํ์ (์ ๋ฌธ๊ฐ ํ์
)\n\n"
|
| 1047 |
|
| 1048 |
+
for key, content in planning_data.items():
|
| 1049 |
+
role = key.split('_')[0] if '_' in key else key
|
| 1050 |
+
emoji = EXPERT_ROLES.get(role, {}).get("emoji", "๐")
|
| 1051 |
+
formatted += f"## {emoji} {key}\n\n"
|
| 1052 |
formatted += content + "\n\n"
|
| 1053 |
formatted += "---\n\n"
|
| 1054 |
|
| 1055 |
return formatted
|
| 1056 |
|
| 1057 |
+
def format_expert_feedbacks(feedbacks: List[ExpertFeedback]) -> str:
|
| 1058 |
+
"""์ ๋ฌธ๊ฐ ํผ๋๋ฐฑ ํ์"""
|
| 1059 |
+
if not feedbacks:
|
| 1060 |
+
return ""
|
| 1061 |
+
|
| 1062 |
+
formatted = "## ๐ฏ ์ ๋ฌธ๊ฐ ํ๊ฐ\n\n"
|
| 1063 |
+
|
| 1064 |
+
for fb in feedbacks:
|
| 1065 |
+
emoji = EXPERT_ROLES.get(fb.role, {}).get("emoji", "๐")
|
| 1066 |
+
formatted += f"### {emoji} {fb.role}\n"
|
| 1067 |
+
formatted += f"**์ ์:** {fb.score:.1f}/100\n"
|
| 1068 |
+
formatted += f"**ํผ๋๋ฐฑ:** {fb.feedback[:200]}...\n"
|
| 1069 |
+
|
| 1070 |
+
if fb.suggestions:
|
| 1071 |
+
formatted += "**์ ์์ฌํญ:**\n"
|
| 1072 |
+
for suggestion in fb.suggestions[:3]:
|
| 1073 |
+
formatted += f"- {suggestion}\n"
|
| 1074 |
+
|
| 1075 |
+
formatted += "\n"
|
| 1076 |
+
|
| 1077 |
+
return formatted
|
| 1078 |
+
|
| 1079 |
def format_screenplay_display(screenplay_text: str) -> str:
|
| 1080 |
"""์๋๋ฆฌ์ค ํ์ ํฌ๋งท"""
|
| 1081 |
if not screenplay_text:
|
|
|
|
| 1111 |
'์ก์
': [
|
| 1112 |
"์ํดํ ํน์์์์ด ๋ฉ์น๋ ๋ธ์ ๊ตฌํ๊ธฐ ์ํด ๋ค์ ํ์ฅ์ผ๋ก ๋ณต๊ทํ๋ค",
|
| 1113 |
"ํ๋ฒํ ํ์๊ธฐ์ฌ๊ฐ ์ฐ์ฐํ ๊ตญ์ ํ
๋ฌ ์กฐ์ง์ ์๋ชจ์ ํ๋ง๋ฆฐ๋ค",
|
|
|
|
| 1114 |
],
|
| 1115 |
'์ค๋ฆด๋ฌ': [
|
| 1116 |
"๊ธฐ์ต์ ์์ ๋จ์๊ฐ ์์ ์ด ์ฐ์์ด์ธ๋ฒ์ด๋ผ๋ ์ฆ๊ฑฐ๋ฅผ ๋ฐ๊ฒฌํ๋ค",
|
| 1117 |
"์ค์ข
๋ ์์ด๋ฅผ ์ฐพ๋ ๊ณผ์ ์์ ๋ง์์ ๋์ฐํ ๋น๋ฐ์ด ๋๋ฌ๋๋ค",
|
|
|
|
| 1118 |
],
|
| 1119 |
'๋๋ผ๋ง': [
|
| 1120 |
"๋ง๊ธฐ ์ ํ์๊ฐ ๋ง์ง๋ง ์์์ผ๋ก ๊ฐ์กฑ๊ณผ์ ํํด๋ฅผ ์๋ํ๋ค",
|
| 1121 |
"์ฌ๊ฐ๋ฐ๋ก ์ฌ๋ผ์ง ๋๋ค๋ฅผ ์งํค๋ ค๋ ์ฃผ๋ฏผ๋ค์ ๋ง์ง๋ง ์ธ์",
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1122 |
],
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1123 |
}
|
| 1124 |
|
| 1125 |
genre_concepts = concepts.get(genre, concepts['๋๋ผ๋ง'])
|
|
|
|
| 1144 |
text-shadow: 2px 2px 4px rgba(0,0,0,0.3);
|
| 1145 |
}
|
| 1146 |
|
| 1147 |
+
.expert-panel {
|
| 1148 |
+
background: #f0f4f8;
|
| 1149 |
+
padding: 1rem;
|
| 1150 |
border-radius: 10px;
|
| 1151 |
margin: 1rem 0;
|
|
|
|
|
|
|
| 1152 |
}
|
| 1153 |
|
| 1154 |
+
.expert-badge {
|
| 1155 |
+
display: inline-block;
|
| 1156 |
+
padding: 0.3rem 0.8rem;
|
| 1157 |
background: white;
|
| 1158 |
+
border-radius: 20px;
|
| 1159 |
+
margin: 0.2rem;
|
| 1160 |
+
font-size: 0.9rem;
|
|
|
|
|
|
|
| 1161 |
}
|
| 1162 |
|
| 1163 |
.progress-bar {
|
|
|
|
| 1174 |
border-radius: 10px;
|
| 1175 |
transition: width 0.3s ease;
|
| 1176 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1177 |
"""
|
| 1178 |
|
| 1179 |
+
with gr.Blocks(theme=gr.themes.Soft(), css=css, title="AI ์๋๋ฆฌ์ค ์๊ฐ - ์ ๋ฌธ๊ฐ ํ์
์์คํ
") as interface:
|
| 1180 |
gr.HTML("""
|
| 1181 |
<div class="main-header">
|
| 1182 |
<h1 class="header-title">๐ฌ AI ์๋๋ฆฌ์ค ์๊ฐ</h1>
|
| 1183 |
<p style="font-size: 1.2rem; opacity: 0.95;">
|
| 1184 |
+
8๋ช
์ ์ ๋ฌธ๊ฐ๊ฐ ํ์
ํ์ฌ ์๋ฒฝํ ์๋๋ฆฌ์ค๋ฅผ ์์ฑํฉ๋๋ค<br>
|
| 1185 |
+
ํ๋ก๋์, ๊ฐ๋
, ์๊ฐ, ๋นํ๊ฐ๊ฐ ํจ๊ป ๋ง๋๋ ์ ๋ฌธ ์๋๋ฆฌ์ค
|
| 1186 |
</p>
|
| 1187 |
</div>
|
| 1188 |
""")
|
| 1189 |
|
| 1190 |
+
# ์ ๋ฌธ๊ฐ ์๊ฐ
|
| 1191 |
+
gr.HTML("""
|
| 1192 |
+
<div class="expert-panel">
|
| 1193 |
+
<h3>๐ฅ ์ฐธ์ฌ ์ ๋ฌธ๊ฐ</h3>
|
| 1194 |
+
<span class="expert-badge">๐ฌ ํ๋ก๋์</span>
|
| 1195 |
+
<span class="expert-badge">๐ ์คํ ๋ฆฌ์๊ฐ</span>
|
| 1196 |
+
<span class="expert-badge">๐ฅ ์บ๋ฆญํฐ๋์์ด๋</span>
|
| 1197 |
+
<span class="expert-badge">๐ญ ๊ฐ๋
</span>
|
| 1198 |
+
<span class="expert-badge">๐ ๋นํ๊ฐ</span>
|
| 1199 |
+
<span class="expert-badge">โ๏ธ ํธ์ง์</span>
|
| 1200 |
+
<span class="expert-badge">๐ฌ ๋ํ์ ๋ฌธ๊ฐ</span>
|
| 1201 |
+
<span class="expert-badge">๐ฏ ์ฅ๋ฅด์ ๋ฌธ๊ฐ</span>
|
| 1202 |
+
</div>
|
| 1203 |
+
""")
|
| 1204 |
+
|
| 1205 |
current_session_id = gr.State(None)
|
| 1206 |
current_planning_data = gr.State({})
|
| 1207 |
+
current_feedbacks = gr.State([])
|
| 1208 |
|
| 1209 |
with gr.Tabs():
|
| 1210 |
with gr.Tab("๐ ์ ์๋๋ฆฌ์ค"):
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1211 |
with gr.Row():
|
| 1212 |
with gr.Column(scale=2):
|
| 1213 |
query_input = gr.Textbox(
|
| 1214 |
+
label="๐ก ์๋๋ฆฌ์ค ์์ด๋์ด",
|
| 1215 |
+
placeholder="""๊ตฌ์ฒด์ ์ธ ์คํ ๋ฆฌ๋ฅผ ์
๋ ฅํ์ธ์. ์์:
|
| 1216 |
+
"2045๋
์์ธ, AI๊ฐ ์ธ๊ฐ ๊ฐ์ ์ ์๋ฒฝํ ๋ชจ๋ฐฉํ๋ ์๋.
|
| 1217 |
+
AI ์ค๋ฆฌํ์๊ฐ ์ฃฝ์ ์๋ด๋ฅผ ๋ณต์ ํ AI์ ์ด๋ค๊ฐ
|
| 1218 |
+
์ง์ง ์๋ด๊ฐ ์ด์์๋ค๋ ์ฆ๊ฑฐ๋ฅผ ๋ฐ๊ฒฌํ๊ณ ์ง์ค์ ์ฐพ์ ๋์ ๋ค."
|
| 1219 |
+
""",
|
| 1220 |
+
lines=6
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1221 |
)
|
| 1222 |
|
| 1223 |
with gr.Column(scale=1):
|
|
|
|
| 1236 |
with gr.Row():
|
| 1237 |
random_btn = gr.Button("๐ฒ ๋๋ค ์์ด๋์ด", scale=1)
|
| 1238 |
clear_btn = gr.Button("๐๏ธ ์ด๊ธฐํ", scale=1)
|
| 1239 |
+
planning_btn = gr.Button("๐ ์ ๋ฌธ๊ฐ ๊ธฐํ ํ์", variant="primary", scale=2)
|
| 1240 |
|
| 1241 |
# ์งํ ์ํ
|
| 1242 |
progress_bar = gr.HTML(
|
|
|
|
| 1245 |
status_text = gr.Textbox(
|
| 1246 |
label="๐ ์งํ ์ํ",
|
| 1247 |
interactive=False,
|
| 1248 |
+
value="์์ด๋์ด๋ฅผ ์
๋ ฅํ๊ณ ์ ๋ฌธ๊ฐ ๊ธฐํ ํ์๋ฅผ ์์ํ์ธ์"
|
| 1249 |
)
|
| 1250 |
|
| 1251 |
+
# ์ ๋ฌธ๊ฐ ํผ๋๋ฐฑ
|
| 1252 |
+
with gr.Group():
|
| 1253 |
+
gr.Markdown("### ๐ฏ ์ ๋ฌธ๊ฐ ํผ๋๋ฐฑ")
|
| 1254 |
+
expert_feedback_display = gr.Markdown(
|
| 1255 |
+
value="*์ ๋ฌธ๊ฐ๋ค์ ์๊ฒฌ์ด ์ฌ๊ธฐ์ ํ์๋ฉ๋๋ค...*"
|
| 1256 |
+
)
|
| 1257 |
+
|
| 1258 |
+
# ๊ธฐํ์
|
| 1259 |
with gr.Group():
|
| 1260 |
gr.Markdown("### ๐ ์๋๋ฆฌ์ค ๊ธฐํ์")
|
| 1261 |
planning_display = gr.Markdown(
|
| 1262 |
+
value="*๊ธฐํ์์ด ์ฌ๊ธฐ์ ํ์๋ฉ๋๋ค...*"
|
|
|
|
| 1263 |
)
|
| 1264 |
|
| 1265 |
with gr.Row():
|
|
|
|
| 1266 |
save_planning_btn = gr.Button("๐พ ๊ธฐํ์ ์ ์ฅ", scale=1)
|
| 1267 |
+
generate_screenplay_btn = gr.Button("๐ฌ ์ ๋ฌธ๊ฐ ์๋๋ฆฌ์ค ์์ฑ", variant="primary", scale=2)
|
| 1268 |
|
| 1269 |
# ์๋๋ฆฌ์ค ์ถ๋ ฅ
|
| 1270 |
with gr.Group():
|
| 1271 |
gr.Markdown("### ๐ ์์ฑ๋ ์๋๋ฆฌ์ค")
|
| 1272 |
screenplay_output = gr.Markdown(
|
| 1273 |
+
value="*์๋๋ฆฌ์ค๊ฐ ์ฌ๊ธฐ์ ํ์๋ฉ๋๋ค...*"
|
|
|
|
| 1274 |
)
|
| 1275 |
|
| 1276 |
+
download_btn = gr.Button("๐พ ๋ค์ด๋ก๋ (TXT)")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1277 |
|
| 1278 |
# ์ด๋ฒคํธ ํธ๋ค๋ฌ
|
| 1279 |
def handle_planning(query, s_type, genre):
|
| 1280 |
if not query:
|
| 1281 |
+
yield "", "โ ์์ด๋์ด๋ฅผ ์
๋ ฅํด์ฃผ์ธ์", "", "", {}
|
| 1282 |
return
|
| 1283 |
|
| 1284 |
system = ScreenplayGenerationSystem()
|
| 1285 |
|
| 1286 |
+
for status, progress, planning_data, feedbacks in system.generate_planning(query, s_type, genre):
|
| 1287 |
progress_html = f'<div class="progress-bar"><div class="progress-fill" style="width: {progress}%"></div></div>'
|
| 1288 |
planning_display = format_planning_display(planning_data)
|
| 1289 |
+
feedback_display = format_expert_feedbacks(feedbacks)
|
| 1290 |
|
| 1291 |
+
yield progress_html, status, feedback_display, planning_display, planning_data
|
| 1292 |
|
| 1293 |
def handle_screenplay_generation(session_id, planning_data):
|
| 1294 |
if not planning_data:
|
| 1295 |
+
yield "", "โ ๋จผ์ ๊ธฐํ์์ ์์ฑํด์ฃผ์ธ์", "", ""
|
| 1296 |
return
|
| 1297 |
|
| 1298 |
system = ScreenplayGenerationSystem()
|
| 1299 |
|
| 1300 |
+
for status, progress, screenplay, feedbacks in system.generate_screenplay(session_id, planning_data):
|
| 1301 |
progress_html = f'<div class="progress-bar"><div class="progress-fill" style="width: {progress}%"></div></div>'
|
| 1302 |
screenplay_display = format_screenplay_display(screenplay)
|
| 1303 |
+
feedback_display = format_expert_feedbacks(feedbacks)
|
| 1304 |
|
| 1305 |
+
yield progress_html, status, feedback_display, screenplay_display
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1306 |
|
| 1307 |
# ์ด๋ฒคํธ ์ฐ๊ฒฐ
|
| 1308 |
planning_btn.click(
|
| 1309 |
fn=handle_planning,
|
| 1310 |
inputs=[query_input, screenplay_type, genre_select],
|
| 1311 |
+
outputs=[progress_bar, status_text, expert_feedback_display, planning_display, current_planning_data]
|
| 1312 |
)
|
| 1313 |
|
| 1314 |
generate_screenplay_btn.click(
|
| 1315 |
fn=handle_screenplay_generation,
|
| 1316 |
inputs=[current_session_id, current_planning_data],
|
| 1317 |
+
outputs=[progress_bar, status_text, expert_feedback_display, screenplay_output]
|
| 1318 |
)
|
| 1319 |
|
| 1320 |
random_btn.click(
|
| 1321 |
+
fn=generate_random_concept,
|
| 1322 |
inputs=[screenplay_type, genre_select],
|
| 1323 |
outputs=[query_input]
|
| 1324 |
)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1325 |
|
| 1326 |
return interface
|
| 1327 |
|
| 1328 |
# ๋ฉ์ธ ์คํ
|
| 1329 |
if __name__ == "__main__":
|
| 1330 |
logger.info("=" * 60)
|
| 1331 |
+
logger.info("AI ์๋๋ฆฌ์ค ์๊ฐ - ์ ๋ฌธ๊ฐ ํ์
์์คํ
")
|
| 1332 |
+
logger.info("8๋ช
์ ์ ๋ฌธ๊ฐ๊ฐ ํ์
ํ์ฌ ์๋๋ฆฌ์ค๋ฅผ ์์ฑํฉ๋๋ค")
|
| 1333 |
logger.info("=" * 60)
|
| 1334 |
|
| 1335 |
if not FIREWORKS_API_KEY or FIREWORKS_API_KEY == "dummy_token_for_testing":
|
| 1336 |
logger.warning("โ ๏ธ FIREWORKS_API_KEY๋ฅผ ์ค์ ํด์ฃผ์ธ์!")
|
| 1337 |
logger.warning("export FIREWORKS_API_KEY='your-api-key'")
|
| 1338 |
|
| 1339 |
+
# ์ ๋ฌธ๊ฐ ์ญํ ์๊ฐ
|
| 1340 |
+
logger.info("\n์ฐธ์ฌ ์ ๋ฌธ๊ฐ:")
|
| 1341 |
+
for role, info in EXPERT_ROLES.items():
|
| 1342 |
+
logger.info(f" {info['emoji']} {role}: {info['description']}")
|
| 1343 |
+
|
| 1344 |
ScreenplayDatabase.init_db()
|
| 1345 |
|
| 1346 |
interface = create_interface()
|