openfree commited on
Commit
f72a781
·
verified ·
1 Parent(s): 219c0fa

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +45 -52
app.py CHANGED
@@ -1,3 +1,5 @@
 
 
1
  import os
2
  import re
3
  import random
@@ -10,6 +12,8 @@ import asyncio
10
  import requests
11
  import anthropic
12
  import openai
 
 
13
 
14
  from http import HTTPStatus
15
  from typing import Dict, List, Optional, Tuple
@@ -20,11 +24,23 @@ import modelscope_studio.components.base as ms
20
  import modelscope_studio.components.legacy as legacy
21
  import modelscope_studio.components.antd as antd
22
 
 
 
 
 
 
 
 
 
 
 
 
23
 
24
  # ------------------------
25
  # 1) DEMO_LIST 및 SystemPrompt
26
  # ------------------------
27
 
 
28
  DEMO_LIST = [
29
  {"description": "블록이 위에서 떨어지는 클래식 테트리스 게임을 개발해주세요. 화살표 키로 조작하며, 가로줄이 채워지면 해당 줄이 제거되고 점수가 올라가는 메커니즘이 필요합니다. 난이도는 시간이 지날수록 블록이 빨라지도록 구현하고, 게임오버 조건과 점수 표시 기능을 포함해주세요."},
30
  {"description": "두 명이 번갈아가며 플레이할 수 있는 체스 게임을 만들어주세요. 기본적인 체스 규칙(킹, 퀸, 룩, 비숍, 나이트, 폰의 이동 규칙)을 구현하고, 체크와 체크메이트 감지 기능이 필요합니다. 드래그 앤 드롭으로 말을 움직일 수 있게 하며, 이동 기록도 표시해주세요."},
@@ -62,6 +78,7 @@ DEMO_LIST = [
62
  {"description": "탑다운 뷰의 간단한 액션 RPG 게임을 개발해주세요. WASD로 이동하고, 마우스 클릭으로 기본 공격, 1-4 키로 특수 스킬을 사용합니다. 플레이어는 몬스터를 처치하며 경험치와 아이템을 획득하고, 레벨업 시 능력치(공격력, 체력, 속도 등)를 향상시킵니다. 다양한 무기와 방어구를 착용할 수 있으며, 스킬 트리 시스템으로 캐릭터를 특화시킬 수 있습니다. 여러 지역과 보스 몬스터, 간단한 퀘스트 시스템도 구현해주세요."},
63
  ]
64
 
 
65
  SystemPrompt = """
66
  # GameCraft 시스템 프롬프트
67
 
@@ -203,11 +220,10 @@ openai_client = openai.OpenAI(api_key=YOUR_OPENAI_TOKEN)
203
 
204
  async def try_claude_api(system_message, claude_messages, timeout=15):
205
  """
206
- Claude API 호출 (스트리밍) - 간결한 코드 생성 요청 추가
207
  """
208
  try:
209
- # 시스템 프롬프트에 코드 길이 제한 강화
210
- system_message_with_limit = system_message + "\n\n추가 중요 지침: 생성하는 코드는 절대로 200줄을 넘지 마세요. 코드 간결성이 최우선입니다. 주석을 최소화하고, 핵심 기능만 구현하세요. 불필요한 UI 요소나 장식은 제외하세요. 모든 코드는 하나의 HTML 파일에 포함되어야 합니다."
211
 
212
  start_time = time.time()
213
  with claude_client.messages.stream(
@@ -215,7 +231,7 @@ async def try_claude_api(system_message, claude_messages, timeout=15):
215
  max_tokens=19800,
216
  system=system_message_with_limit,
217
  messages=claude_messages,
218
- temperature=0.3, # 온도 낮게 설정하여 일관된 결과 유도
219
  ) as stream:
220
  collected_content = ""
221
  for chunk in stream:
@@ -235,16 +251,15 @@ async def try_openai_api(openai_messages):
235
  OpenAI API 호출 (스트리밍) - 코드 길이 제한 강화
236
  """
237
  try:
238
- # 첫 번째 시스템 메시지에 코드 길이 제한 추가
239
  if openai_messages and openai_messages[0]["role"] == "system":
240
- openai_messages[0]["content"] += "\n\n추가 중요 지침: 생성하는 코드는 절대로 200줄을 넘지 마세요. 코드 간결성이 최우선입니다. 주석을 최소화하고, 핵심 기능만 구현하세요. 불필요한 UI 요소나 장식은 제외하세요. 모든 코드는 하나의 HTML 파일에 포함되어야 합니다."
241
-
242
  stream = openai_client.chat.completions.create(
243
  model="o3",
244
  messages=openai_messages,
245
  stream=True,
246
  max_tokens=19800,
247
- temperature=0.2 # 온도 낮게 설정
248
  )
249
  collected_content = ""
250
  for chunk in stream:
@@ -253,14 +268,13 @@ async def try_openai_api(openai_messages):
253
  yield collected_content
254
  except Exception as e:
255
  raise e
256
-
257
 
258
  # ------------------------
259
  # 4) 템플릿(하나로 통합)
260
  # ------------------------
261
 
262
  def load_json_data():
263
- # 실제 DEMO_LIST의 모든 항목을 반환하도록 수정
264
  data_list = []
265
  for item in DEMO_LIST:
266
  data_list.append({
@@ -343,9 +357,6 @@ function copyToInput(card) {
343
  return gr.HTML(value=html_content)
344
 
345
  def load_all_templates():
346
- """
347
- 모든 템플릿을 하나로 보여주는 함수
348
- """
349
  return create_template_html("🎮 모든 게임 템플릿", load_json_data())
350
 
351
 
@@ -354,10 +365,6 @@ def load_all_templates():
354
  # ------------------------
355
 
356
  def remove_code_block(text):
357
- """
358
- More robust function to extract code from markdown code blocks
359
- 텍스트에서 ```html 및 ``` 태그를 완전히 제거하는 함수
360
- """
361
  pattern = r'```html\s*([\s\S]+?)\s*```'
362
  match = re.search(pattern, text, re.DOTALL)
363
  if match:
@@ -373,10 +380,6 @@ def remove_code_block(text):
373
  return text.strip()
374
 
375
  def optimize_code(code: str) -> str:
376
- """
377
- AI가 생성한 코드를 최적화하여 크기를 줄이는 함수
378
- 불필요한 주석, 공백, 장황한 코드 등을 제거
379
- """
380
  if not code or len(code.strip()) == 0:
381
  return code
382
 
@@ -410,9 +413,6 @@ def optimize_code(code: str) -> str:
410
  return cleaned_code
411
 
412
  def send_to_sandbox(code):
413
- """
414
- iframe으로 렌더링
415
- """
416
  clean_code = remove_code_block(code)
417
  clean_code = optimize_code(clean_code)
418
 
@@ -457,7 +457,6 @@ def boost_prompt(prompt: str) -> str:
457
  - 최소한의 필수 게임 요소만 포함
458
  """
459
  try:
460
- # Claude API
461
  try:
462
  response = claude_client.messages.create(
463
  model="claude-3-7-sonnet-20250219",
@@ -645,20 +644,23 @@ class Demo:
645
  def clear_history(self):
646
  return []
647
 
 
648
  ####################################################
649
  # 1) deploy_to_vercel 함수
650
  ####################################################
651
  def deploy_to_vercel(code: str):
652
- print("[DEBUG] deploy_to_vercel() 시작. code 길이:", len(code) if code else 0)
653
  try:
654
  if not code or len(code.strip()) < 10:
 
655
  return "No code to deploy. (Code is empty)"
656
 
657
  token = "A8IFZmgW2cqA4yUNlLPnci0N"
658
  if not token:
 
659
  return "Vercel token is not set."
660
 
661
- project_name = ''.join(random.choice(string.ascii_lowercase) for i in range(6))
662
  deploy_url = "https://api.vercel.com/v13/deployments"
663
  headers = {
664
  "Authorization": f"Bearer {token}",
@@ -693,18 +695,18 @@ def deploy_to_vercel(code: str):
693
  "projectSettings": project_settings
694
  }
695
 
696
- print("[DEBUG] Vercel API 요청 전송중...")
697
  deploy_response = requests.post(deploy_url, headers=headers, json=deploy_data)
698
- print("[DEBUG] 응답 status_code:", deploy_response.status_code)
699
 
700
  if deploy_response.status_code != 200:
 
701
  return f"Deployment failed: {deploy_response.text}"
702
 
703
  deployment_url = f"https://{project_name}.vercel.app"
704
- print("[DEBUG] 배포 성공, 배포 URL:", deployment_url)
705
  time.sleep(3)
706
 
707
- # ★ 원본 코드 스타일: 심플 HTML (스크립트/iframe 없이)
708
  return f"""
709
  <div style="border:1px solid #34c759; padding:15px; border-radius:8px;">
710
  <h3 style="margin:0; color:#34c759;">✅ Deployment complete!</h3>
@@ -718,44 +720,38 @@ def deploy_to_vercel(code: str):
718
  """
719
 
720
  except Exception as e:
721
- print("[ERROR] deploy_to_vercel() 예외:", e)
722
  return f"Error during deployment: {str(e)}"
723
 
724
- ####################################################
725
- # 2) handle_deploy_legacy: iframe으로 결과 표시
726
- ####################################################
 
727
  def handle_deploy_legacy(code):
728
- print("[DEBUG] handle_deploy_legacy() 호출됨. code 길이:", len(code) if code else 0)
729
  if not code or len(code.strip()) < 10:
730
- print("[DEBUG] 유효하지 않은 코드 (None or 너무 짧음).")
731
  return "<div style='color:red;'>배포할 코드가 없습니다.</div>"
732
 
733
- # remove_code_block 처리
734
  clean_code = remove_code_block(code)
735
- print("[DEBUG] remove_code_block 후 clean_code 길이:", len(clean_code))
736
 
737
- # 실제 배포
738
  result_html = deploy_to_vercel(clean_code)
739
- print("[DEBUG] 배포 결과 HTML 길이:", len(result_html))
740
 
741
- # iframe에 표시하기 위해 Base64로 인코딩
742
  encoded_html = base64.b64encode(result_html.encode('utf-8')).decode()
743
  data_uri = f"data:text/html;charset=utf-8;base64,{encoded_html}"
744
 
745
- # Gradio 3.29 이하 버전에서는 unsafe_allow_html 미지원이므로,
746
- # iframe 태그만 반환하여 script 실행.
747
  iframe_html = f"""
748
  <iframe src="{data_uri}"
749
  style="width:100%; height:600px; border:none;"
750
  sandbox="allow-scripts allow-same-origin allow-popups">
751
  </iframe>
752
  """
753
- print("[DEBUG] iframe_html 반환")
754
  return iframe_html
755
 
756
 
757
-
758
-
759
  # ------------------------
760
  # 8) Gradio / Modelscope UI 빌드
761
  # ------------------------
@@ -1023,10 +1019,6 @@ with gr.Blocks(css_paths=["app.css"], theme=theme) as demo:
1023
  gr.HTML('<div class="help-text">💡 원하는 게임의 설명을 입력하세요. 예: "테트리스 게임 제작해줘."</div>')
1024
 
1025
 
1026
- ##################################################################
1027
- # 3) deploy_result_container = gr.HTML() (unsafe_allow_html = False)
1028
- ##################################################################
1029
- # unsafe_allow_html 없이도 iframe 자체는 표시됨
1030
  deploy_result_container = gr.HTML(
1031
  """
1032
  <div class="deploy-section">
@@ -1039,6 +1031,7 @@ with gr.Blocks(css_paths=["app.css"], theme=theme) as demo:
1039
  label="Deployment Result"
1040
  )
1041
 
 
1042
  # 이벤트 / 콜백
1043
  # Code Drawer
1044
  codeBtn.click(
@@ -1107,7 +1100,7 @@ with gr.Blocks(css_paths=["app.css"], theme=theme) as demo:
1107
  outputs=[sandbox, state_tab]
1108
  )
1109
 
1110
- # 수정된 부분: outputs=[deploy_result_container]
1111
  deploy_btn.click(
1112
  fn=lambda code: deploy_to_vercel(remove_code_block(code)) if code else "No code generated.",
1113
  inputs=[code_output],
 
1
+
2
+
3
  import os
4
  import re
5
  import random
 
12
  import requests
13
  import anthropic
14
  import openai
15
+ import io
16
+ import logging
17
 
18
  from http import HTTPStatus
19
  from typing import Dict, List, Optional, Tuple
 
24
  import modelscope_studio.components.legacy as legacy
25
  import modelscope_studio.components.antd as antd
26
 
27
+ # === [1] 로거 설정 ===
28
+ log_stream = io.StringIO()
29
+ handler = logging.StreamHandler(log_stream)
30
+ logger = logging.getLogger()
31
+ logger.setLevel(logging.DEBUG) # 원하는 레벨로 설정
32
+ logger.addHandler(handler)
33
+
34
+ def get_logs():
35
+ """StringIO에 쌓인 로그를 문자열로 반환"""
36
+ return log_stream.getvalue()
37
+
38
 
39
  # ------------------------
40
  # 1) DEMO_LIST 및 SystemPrompt
41
  # ------------------------
42
 
43
+
44
  DEMO_LIST = [
45
  {"description": "블록이 위에서 떨어지는 클래식 테트리스 게임을 개발해주세요. 화살표 키로 조작하며, 가로줄이 채워지면 해당 줄이 제거되고 점수가 올라가는 메커니즘이 필요합니다. 난이도는 시간이 지날수록 블록이 빨라지도록 구현하고, 게임오버 조건과 점수 표시 기능을 포함해주세요."},
46
  {"description": "두 명이 번갈아가며 플레이할 수 있는 체스 게임을 만들어주세요. 기본적인 체스 규칙(킹, 퀸, 룩, 비숍, 나이트, 폰의 이동 규칙)을 구현하고, 체크와 체크메이트 감지 기능이 필요합니다. 드래그 앤 드롭으로 말을 움직일 수 있게 하며, 이동 기록도 표시해주세요."},
 
78
  {"description": "탑다운 뷰의 간단한 액션 RPG 게임을 개발해주세요. WASD로 이동하고, 마우스 클릭으로 기본 공격, 1-4 키로 특수 스킬을 사용합니다. 플레이어는 몬스터를 처치하며 경험치와 아이템을 획득하고, 레벨업 시 능력치(공격력, 체력, 속도 등)를 향상시킵니다. 다양한 무기와 방어구를 착용할 수 있으며, 스킬 트리 시스템으로 캐릭터를 특화시킬 수 있습니다. 여러 지역과 보스 몬스터, 간단한 퀘스트 시스템도 구현해주세요."},
79
  ]
80
 
81
+
82
  SystemPrompt = """
83
  # GameCraft 시스템 프롬프트
84
 
 
220
 
221
  async def try_claude_api(system_message, claude_messages, timeout=15):
222
  """
223
+ Claude API 호출 (스트리밍)
224
  """
225
  try:
226
+ system_message_with_limit = system_message + "\n\n추가 중요 지침: 생성하는 코드는 절대로 200줄을 넘지 마세요. 코드 간결성이 최우선입니다. 주석을 최소화하고, 핵심 기능만 구현하세요."
 
227
 
228
  start_time = time.time()
229
  with claude_client.messages.stream(
 
231
  max_tokens=19800,
232
  system=system_message_with_limit,
233
  messages=claude_messages,
234
+ temperature=0.3,
235
  ) as stream:
236
  collected_content = ""
237
  for chunk in stream:
 
251
  OpenAI API 호출 (스트리밍) - 코드 길이 제한 강화
252
  """
253
  try:
 
254
  if openai_messages and openai_messages[0]["role"] == "system":
255
+ openai_messages[0]["content"] += "\n\n추가 중요 지침: 생성하는 코드는 절대로 200줄을 넘지 마세요. 코드 간결성이 최우선입니다. 주석은 최소화하고, 핵심 기능만 구현하세요."
256
+
257
  stream = openai_client.chat.completions.create(
258
  model="o3",
259
  messages=openai_messages,
260
  stream=True,
261
  max_tokens=19800,
262
+ temperature=0.2
263
  )
264
  collected_content = ""
265
  for chunk in stream:
 
268
  yield collected_content
269
  except Exception as e:
270
  raise e
271
+
272
 
273
  # ------------------------
274
  # 4) 템플릿(하나로 통합)
275
  # ------------------------
276
 
277
  def load_json_data():
 
278
  data_list = []
279
  for item in DEMO_LIST:
280
  data_list.append({
 
357
  return gr.HTML(value=html_content)
358
 
359
  def load_all_templates():
 
 
 
360
  return create_template_html("🎮 모든 게임 템플릿", load_json_data())
361
 
362
 
 
365
  # ------------------------
366
 
367
  def remove_code_block(text):
 
 
 
 
368
  pattern = r'```html\s*([\s\S]+?)\s*```'
369
  match = re.search(pattern, text, re.DOTALL)
370
  if match:
 
380
  return text.strip()
381
 
382
  def optimize_code(code: str) -> str:
 
 
 
 
383
  if not code or len(code.strip()) == 0:
384
  return code
385
 
 
413
  return cleaned_code
414
 
415
  def send_to_sandbox(code):
 
 
 
416
  clean_code = remove_code_block(code)
417
  clean_code = optimize_code(clean_code)
418
 
 
457
  - 최소한의 필수 게임 요소만 포함
458
  """
459
  try:
 
460
  try:
461
  response = claude_client.messages.create(
462
  model="claude-3-7-sonnet-20250219",
 
644
  def clear_history(self):
645
  return []
646
 
647
+
648
  ####################################################
649
  # 1) deploy_to_vercel 함수
650
  ####################################################
651
  def deploy_to_vercel(code: str):
652
+ logger.debug(f"[deploy_to_vercel] code 길이: {len(code) if code else 0}")
653
  try:
654
  if not code or len(code.strip()) < 10:
655
+ logger.info("[deploy_to_vercel] code가 짧아서 배포 불가")
656
  return "No code to deploy. (Code is empty)"
657
 
658
  token = "A8IFZmgW2cqA4yUNlLPnci0N"
659
  if not token:
660
+ logger.error("[deploy_to_vercel] Vercel 토큰이 없습니다.")
661
  return "Vercel token is not set."
662
 
663
+ project_name = ''.join(random.choice(string.ascii_lowercase) for _ in range(6))
664
  deploy_url = "https://api.vercel.com/v13/deployments"
665
  headers = {
666
  "Authorization": f"Bearer {token}",
 
695
  "projectSettings": project_settings
696
  }
697
 
698
+ logger.debug("[deploy_to_vercel] Vercel API 요청 전송중...")
699
  deploy_response = requests.post(deploy_url, headers=headers, json=deploy_data)
700
+ logger.debug(f"[deploy_to_vercel] 응답 status_code: {deploy_response.status_code}")
701
 
702
  if deploy_response.status_code != 200:
703
+ logger.warning(f"[deploy_to_vercel] 배포 실패: {deploy_response.text}")
704
  return f"Deployment failed: {deploy_response.text}"
705
 
706
  deployment_url = f"https://{project_name}.vercel.app"
707
+ logger.info(f"[deploy_to_vercel] 배포 성공, URL={deployment_url}")
708
  time.sleep(3)
709
 
 
710
  return f"""
711
  <div style="border:1px solid #34c759; padding:15px; border-radius:8px;">
712
  <h3 style="margin:0; color:#34c759;">✅ Deployment complete!</h3>
 
720
  """
721
 
722
  except Exception as e:
723
+ logger.exception("[deploy_to_vercel] 예외 발생")
724
  return f"Error during deployment: {str(e)}"
725
 
726
+
727
+ # ------------------------
728
+ # (3) handle_deploy_legacy
729
+ # ------------------------
730
  def handle_deploy_legacy(code):
731
+ logger.debug(f"[handle_deploy_legacy] code 길이: {len(code) if code else 0}")
732
  if not code or len(code.strip()) < 10:
733
+ logger.info("[handle_deploy_legacy] 코드가 짧음.")
734
  return "<div style='color:red;'>배포할 코드가 없습니다.</div>"
735
 
 
736
  clean_code = remove_code_block(code)
737
+ logger.debug(f"[handle_deploy_legacy] remove_code_block 후 길이: {len(clean_code)}")
738
 
 
739
  result_html = deploy_to_vercel(clean_code)
740
+ logger.debug(f"[handle_deploy_legacy] 배포 결과 HTML 길이: {len(result_html)}")
741
 
 
742
  encoded_html = base64.b64encode(result_html.encode('utf-8')).decode()
743
  data_uri = f"data:text/html;charset=utf-8;base64,{encoded_html}"
744
 
 
 
745
  iframe_html = f"""
746
  <iframe src="{data_uri}"
747
  style="width:100%; height:600px; border:none;"
748
  sandbox="allow-scripts allow-same-origin allow-popups">
749
  </iframe>
750
  """
751
+ logger.debug("[handle_deploy_legacy] iframe_html 반환")
752
  return iframe_html
753
 
754
 
 
 
755
  # ------------------------
756
  # 8) Gradio / Modelscope UI 빌드
757
  # ------------------------
 
1019
  gr.HTML('<div class="help-text">💡 원하는 게임의 설명을 입력하세요. 예: "테트리스 게임 제작해줘."</div>')
1020
 
1021
 
 
 
 
 
1022
  deploy_result_container = gr.HTML(
1023
  """
1024
  <div class="deploy-section">
 
1031
  label="Deployment Result"
1032
  )
1033
 
1034
+
1035
  # 이벤트 / 콜백
1036
  # Code Drawer
1037
  codeBtn.click(
 
1100
  outputs=[sandbox, state_tab]
1101
  )
1102
 
1103
+ # 여기서 outputs=[deploy_result_container]가 핵심 수정
1104
  deploy_btn.click(
1105
  fn=lambda code: deploy_to_vercel(remove_code_block(code)) if code else "No code generated.",
1106
  inputs=[code_output],