seawolf2357 commited on
Commit
8dfd117
ยท
verified ยท
1 Parent(s): 55f7aeb

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +386 -51
app.py CHANGED
@@ -1,6 +1,7 @@
1
  """
2
  DNA-Diffusion Gradio Application
3
  Interactive DNA sequence generation with slot machine visualization and protein analysis
 
4
  """
5
 
6
  import gradio as gr
@@ -22,7 +23,6 @@ try:
22
  SPACES_AVAILABLE = True
23
  except ImportError:
24
  SPACES_AVAILABLE = False
25
- # Create a dummy decorator if spaces is not available
26
  class spaces:
27
  @staticmethod
28
  def GPU(duration=60):
@@ -47,6 +47,7 @@ if not os.path.exists(HTML_FILE):
47
  with open(HTML_FILE, "r") as f:
48
  SLOT_MACHINE_HTML = f.read()
49
 
 
50
  class ProteinAnalyzer:
51
  """Handles protein translation and analysis using LLM"""
52
 
@@ -73,19 +74,15 @@ class ProteinAnalyzer:
73
  @staticmethod
74
  def dna_to_protein(dna_sequence: str) -> str:
75
  """Translate DNA sequence to protein sequence"""
76
- # Ensure sequence is uppercase
77
  dna_sequence = dna_sequence.upper()
78
-
79
- # Remove any non-DNA characters
80
  dna_sequence = ''.join(c for c in dna_sequence if c in 'ATCG')
81
 
82
- # Translate to protein
83
  protein = []
84
  for i in range(0, len(dna_sequence) - 2, 3):
85
  codon = dna_sequence[i:i+3]
86
  if len(codon) == 3:
87
  amino_acid = ProteinAnalyzer.CODON_TABLE.get(codon, 'X')
88
- if amino_acid == '*': # Stop codon
89
  break
90
  protein.append(amino_acid)
91
 
@@ -93,25 +90,22 @@ class ProteinAnalyzer:
93
 
94
  @staticmethod
95
  def analyze_protein_with_llm(protein_sequence: str, cell_type: str, language: str = "en") -> str:
96
- """Analyze protein structure and function using Friendli LLM API"""
97
 
98
  # Get API token from environment
99
- token = os.getenv("FRIENDLI_TOKEN")
100
- if not token:
101
- logger.warning("FRIENDLI_TOKEN not found in environment variables")
102
  if language == "ko":
103
  return "๋‹จ๋ฐฑ์งˆ ๋ถ„์„ ๋ถˆ๊ฐ€: API ํ† ํฐ์ด ์„ค์ •๋˜์ง€ ์•Š์•˜์Šต๋‹ˆ๋‹ค"
104
  return "Protein analysis unavailable: API token not configured"
105
 
106
  try:
107
- url = "https://api.friendli.ai/dedicated/v1/chat/completions"
108
- headers = {
109
- "Authorization": f"Bearer {token}",
110
- "Content-Type": "application/json"
111
- }
112
 
113
  # Create prompt for protein analysis based on language
114
  if language == "ko":
 
115
  prompt = f"""๋‹น์‹ ์€ ์ƒ๋ฌผ์ •๋ณดํ•™ ์ „๋ฌธ๊ฐ€์ž…๋‹ˆ๋‹ค. ๋‹ค์Œ ๋‹จ๋ฐฑ์งˆ ์„œ์—ด์„ ๋ถ„์„ํ•˜๊ณ  ์ž ์žฌ์ ์ธ ๊ตฌ์กฐ์™€ ๊ธฐ๋Šฅ์— ๋Œ€ํ•œ ํ†ต์ฐฐ๋ ฅ์„ ์ œ๊ณตํ•ด์ฃผ์„ธ์š”.
116
 
117
  ๋‹จ๋ฐฑ์งˆ ์„œ์—ด: {protein_sequence}
@@ -126,6 +120,7 @@ class ProteinAnalyzer:
126
 
127
  ๊ณผํ•™ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์— ํ‘œ์‹œํ•˜๊ธฐ์— ์ ํ•ฉํ•˜๋„๋ก ๊ฐ„๊ฒฐํ•˜๋ฉด์„œ๋„ ์œ ์ตํ•œ ์‘๋‹ต์„ ์ž‘์„ฑํ•ด์ฃผ์„ธ์š”."""
128
  else:
 
129
  prompt = f"""You are a bioinformatics expert. Analyze the following protein sequence and provide insights about its potential structure and function.
130
 
131
  Protein sequence: {protein_sequence}
@@ -141,24 +136,32 @@ Please provide:
141
  Keep the response concise but informative, suitable for display in a scientific application."""
142
 
143
  payload = {
144
- "model": "dep86pjolcjjnv8",
 
 
 
 
 
 
145
  "messages": [
146
  {
147
  "role": "system",
148
- "content": "You are a knowledgeable bioinformatics assistant specializing in protein structure and function prediction." if language == "en" else "๋‹น์‹ ์€ ๋‹จ๋ฐฑ์งˆ ๊ตฌ์กฐ์™€ ๊ธฐ๋Šฅ ์˜ˆ์ธก์„ ์ „๋ฌธ์œผ๋กœ ํ•˜๋Š” ์ง€์‹์ด ํ’๋ถ€ํ•œ ์ƒ๋ฌผ์ •๋ณดํ•™ ์–ด์‹œ์Šคํ„ดํŠธ์ž…๋‹ˆ๋‹ค."
149
  },
150
  {
151
  "role": "user",
152
  "content": prompt
153
  }
154
- ],
155
- "max_tokens": 1000,
156
- "temperature": 0.7,
157
- "top_p": 0.8,
158
- "stream": False # Disable streaming for simplicity
159
  }
160
 
161
- response = requests.post(url, json=payload, headers=headers, timeout=30)
 
 
 
 
 
 
162
  response.raise_for_status()
163
 
164
  result = response.json()
@@ -168,11 +171,16 @@ Keep the response concise but informative, suitable for display in a scientific
168
 
169
  except requests.exceptions.RequestException as e:
170
  logger.error(f"Failed to analyze protein with LLM: {e}")
 
 
171
  return f"Protein analysis failed: {str(e)}"
172
  except Exception as e:
173
  logger.error(f"Unexpected error during protein analysis: {e}")
 
 
174
  return "Protein analysis unavailable due to an error"
175
 
 
176
  class DNADiffusionApp:
177
  """Main application class for DNA-Diffusion Gradio interface"""
178
 
@@ -208,7 +216,6 @@ class DNADiffusionApp:
208
  def generate_sequence(self, cell_type: str, guidance_scale: float = 1.0) -> Tuple[str, Dict[str, Any]]:
209
  """Generate a DNA sequence using the model or mock data"""
210
 
211
- # Use mock generation if model is not available
212
  if not MODEL_AVAILABLE or self.model is None:
213
  logger.warning("Using mock sequence generation")
214
  import random
@@ -219,11 +226,9 @@ class DNADiffusionApp:
219
  'generation_time': 2.0,
220
  'mock': True
221
  }
222
- # Simulate generation time
223
  time.sleep(2.0)
224
  return sequence, metadata
225
 
226
- # Use real model
227
  try:
228
  result = self.model.generate(cell_type, guidance_scale)
229
  return result['sequence'], result['metadata']
@@ -236,24 +241,19 @@ class DNADiffusionApp:
236
  try:
237
  logger.info(f"Generating sequence for cell type: {cell_type}, language: {language}")
238
 
239
- # Generate DNA sequence
240
  sequence, metadata = self.generate_sequence(cell_type, guidance_scale)
241
 
242
- # Translate to protein
243
  logger.info("Translating DNA to protein sequence...")
244
  protein_sequence = self.protein_analyzer.dna_to_protein(sequence)
245
 
246
- # Add protein sequence to metadata
247
  metadata['protein_sequence'] = protein_sequence
248
  metadata['protein_length'] = len(protein_sequence)
249
 
250
- # Analyze protein with LLM
251
  logger.info("Analyzing protein structure and function...")
252
  protein_analysis = self.protein_analyzer.analyze_protein_with_llm(
253
  protein_sequence, cell_type, language
254
  )
255
 
256
- # Add analysis to metadata
257
  metadata['protein_analysis'] = protein_analysis
258
 
259
  logger.info("Generation and analysis complete")
@@ -264,24 +264,342 @@ class DNADiffusionApp:
264
  logger.error(f"Generation request failed: {error_msg}")
265
  return "", json.dumps({"error": error_msg})
266
 
 
267
  # Create single app instance
268
  app = DNADiffusionApp()
269
 
270
- def create_demo():
271
- """Create the Gradio demo interface"""
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
272
 
273
- # CSS to hide backend controls and prevent scrolling
274
- css = """
275
- #hidden-controls { display: none !important; }
276
- .gradio-container {
277
- overflow: hidden;
278
- background-color: #000000 !important;
279
  }
280
- #dna-frame { overflow: hidden; position: relative; }
281
- body {
282
- background-color: #000000 !important;
 
283
  }
284
- """
 
 
 
 
 
 
 
 
 
 
 
 
285
 
286
  # JavaScript for handling communication between iframe and Gradio
287
  js = """
@@ -301,7 +619,6 @@ def create_demo():
301
  radioInputs.forEach(input => {
302
  if (input.value === event.data.cellType) {
303
  input.checked = true;
304
- // Trigger change event
305
  input.dispatchEvent(new Event('change'));
306
  }
307
  });
@@ -343,7 +660,6 @@ def create_demo():
343
  }
344
  } catch (e) {
345
  console.error('Failed to parse metadata:', e);
346
- // If parsing fails, still send the sequence
347
  iframe.contentWindow.postMessage({
348
  type: 'sequence_generated',
349
  sequence: sequence,
@@ -357,7 +673,30 @@ def create_demo():
357
  }
358
  """
359
 
360
- with gr.Blocks(css=css, js=js, theme=gr.themes.Base()) as demo:
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
361
 
362
  # Hidden controls for backend processing
363
  with gr.Column(elem_id="hidden-controls", visible=False):
@@ -387,7 +726,6 @@ def create_demo():
387
  metadata_output = gr.Textbox(label="Metadata", elem_id="metadata-output")
388
 
389
  # Main interface - the slot machine in an iframe
390
- # Escape the HTML content for srcdoc
391
  escaped_html = html.escape(SLOT_MACHINE_HTML, quote=True)
392
  iframe_html = f'<iframe srcdoc="{escaped_html}" style="width: 100%; height: 800px; border: none; display: block;"></iframe>'
393
 
@@ -417,11 +755,11 @@ def create_demo():
417
 
418
  return demo
419
 
 
420
  # Launch the app
421
  if __name__ == "__main__":
422
  demo = create_demo()
423
 
424
- # Parse any command line arguments
425
  import argparse
426
  parser = argparse.ArgumentParser(description="DNA-Diffusion Gradio App")
427
  parser.add_argument("--share", action="store_true", help="Create a public shareable link")
@@ -429,10 +767,7 @@ if __name__ == "__main__":
429
  parser.add_argument("--host", type=str, default="0.0.0.0", help="Host to run the app on")
430
  args = parser.parse_args()
431
 
432
- # For Hugging Face Spaces deployment
433
- import os
434
  if os.getenv("SPACE_ID"):
435
- # Running on Hugging Face Spaces
436
  args.host = "0.0.0.0"
437
  args.port = 7860
438
  args.share = False
 
1
  """
2
  DNA-Diffusion Gradio Application
3
  Interactive DNA sequence generation with slot machine visualization and protein analysis
4
+ Comic Classic Theme Applied
5
  """
6
 
7
  import gradio as gr
 
23
  SPACES_AVAILABLE = True
24
  except ImportError:
25
  SPACES_AVAILABLE = False
 
26
  class spaces:
27
  @staticmethod
28
  def GPU(duration=60):
 
47
  with open(HTML_FILE, "r") as f:
48
  SLOT_MACHINE_HTML = f.read()
49
 
50
+
51
  class ProteinAnalyzer:
52
  """Handles protein translation and analysis using LLM"""
53
 
 
74
  @staticmethod
75
  def dna_to_protein(dna_sequence: str) -> str:
76
  """Translate DNA sequence to protein sequence"""
 
77
  dna_sequence = dna_sequence.upper()
 
 
78
  dna_sequence = ''.join(c for c in dna_sequence if c in 'ATCG')
79
 
 
80
  protein = []
81
  for i in range(0, len(dna_sequence) - 2, 3):
82
  codon = dna_sequence[i:i+3]
83
  if len(codon) == 3:
84
  amino_acid = ProteinAnalyzer.CODON_TABLE.get(codon, 'X')
85
+ if amino_acid == '*':
86
  break
87
  protein.append(amino_acid)
88
 
 
90
 
91
  @staticmethod
92
  def analyze_protein_with_llm(protein_sequence: str, cell_type: str, language: str = "en") -> str:
93
+ """Analyze protein structure and function using Fireworks AI LLM API"""
94
 
95
  # Get API token from environment
96
+ api_key = os.getenv("FIREWORKS_API_KEY")
97
+ if not api_key:
98
+ logger.warning("FIREWORKS_API_KEY not found in environment variables")
99
  if language == "ko":
100
  return "๋‹จ๋ฐฑ์งˆ ๋ถ„์„ ๋ถˆ๊ฐ€: API ํ† ํฐ์ด ์„ค์ •๋˜์ง€ ์•Š์•˜์Šต๋‹ˆ๋‹ค"
101
  return "Protein analysis unavailable: API token not configured"
102
 
103
  try:
104
+ url = "https://api.fireworks.ai/inference/v1/chat/completions"
 
 
 
 
105
 
106
  # Create prompt for protein analysis based on language
107
  if language == "ko":
108
+ system_content = "๋‹น์‹ ์€ ๋‹จ๋ฐฑ์งˆ ๊ตฌ์กฐ์™€ ๊ธฐ๋Šฅ ์˜ˆ์ธก์„ ์ „๋ฌธ์œผ๋กœ ํ•˜๋Š” ์ง€์‹์ด ํ’๋ถ€ํ•œ ์ƒ๋ฌผ์ •๋ณดํ•™ ์–ด์‹œ์Šคํ„ดํŠธ์ž…๋‹ˆ๋‹ค."
109
  prompt = f"""๋‹น์‹ ์€ ์ƒ๋ฌผ์ •๋ณดํ•™ ์ „๋ฌธ๊ฐ€์ž…๋‹ˆ๋‹ค. ๋‹ค์Œ ๋‹จ๋ฐฑ์งˆ ์„œ์—ด์„ ๋ถ„์„ํ•˜๊ณ  ์ž ์žฌ์ ์ธ ๊ตฌ์กฐ์™€ ๊ธฐ๋Šฅ์— ๋Œ€ํ•œ ํ†ต์ฐฐ๋ ฅ์„ ์ œ๊ณตํ•ด์ฃผ์„ธ์š”.
110
 
111
  ๋‹จ๋ฐฑ์งˆ ์„œ์—ด: {protein_sequence}
 
120
 
121
  ๊ณผํ•™ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์— ํ‘œ์‹œํ•˜๊ธฐ์— ์ ํ•ฉํ•˜๋„๋ก ๊ฐ„๊ฒฐํ•˜๋ฉด์„œ๋„ ์œ ์ตํ•œ ์‘๋‹ต์„ ์ž‘์„ฑํ•ด์ฃผ์„ธ์š”."""
122
  else:
123
+ system_content = "You are a knowledgeable bioinformatics assistant specializing in protein structure and function prediction."
124
  prompt = f"""You are a bioinformatics expert. Analyze the following protein sequence and provide insights about its potential structure and function.
125
 
126
  Protein sequence: {protein_sequence}
 
136
  Keep the response concise but informative, suitable for display in a scientific application."""
137
 
138
  payload = {
139
+ "model": "accounts/fireworks/models/gpt-oss-120b",
140
+ "max_tokens": 4096,
141
+ "top_p": 1,
142
+ "top_k": 40,
143
+ "presence_penalty": 0,
144
+ "frequency_penalty": 0,
145
+ "temperature": 0.6,
146
  "messages": [
147
  {
148
  "role": "system",
149
+ "content": system_content
150
  },
151
  {
152
  "role": "user",
153
  "content": prompt
154
  }
155
+ ]
 
 
 
 
156
  }
157
 
158
+ headers = {
159
+ "Accept": "application/json",
160
+ "Content-Type": "application/json",
161
+ "Authorization": f"Bearer {api_key}"
162
+ }
163
+
164
+ response = requests.post(url, headers=headers, data=json.dumps(payload), timeout=60)
165
  response.raise_for_status()
166
 
167
  result = response.json()
 
171
 
172
  except requests.exceptions.RequestException as e:
173
  logger.error(f"Failed to analyze protein with LLM: {e}")
174
+ if language == "ko":
175
+ return f"๋‹จ๋ฐฑ์งˆ ๋ถ„์„ ์‹คํŒจ: {str(e)}"
176
  return f"Protein analysis failed: {str(e)}"
177
  except Exception as e:
178
  logger.error(f"Unexpected error during protein analysis: {e}")
179
+ if language == "ko":
180
+ return "์˜ค๋ฅ˜๋กœ ์ธํ•ด ๋‹จ๋ฐฑ์งˆ ๋ถ„์„์„ ์‚ฌ์šฉํ•  ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค"
181
  return "Protein analysis unavailable due to an error"
182
 
183
+
184
  class DNADiffusionApp:
185
  """Main application class for DNA-Diffusion Gradio interface"""
186
 
 
216
  def generate_sequence(self, cell_type: str, guidance_scale: float = 1.0) -> Tuple[str, Dict[str, Any]]:
217
  """Generate a DNA sequence using the model or mock data"""
218
 
 
219
  if not MODEL_AVAILABLE or self.model is None:
220
  logger.warning("Using mock sequence generation")
221
  import random
 
226
  'generation_time': 2.0,
227
  'mock': True
228
  }
 
229
  time.sleep(2.0)
230
  return sequence, metadata
231
 
 
232
  try:
233
  result = self.model.generate(cell_type, guidance_scale)
234
  return result['sequence'], result['metadata']
 
241
  try:
242
  logger.info(f"Generating sequence for cell type: {cell_type}, language: {language}")
243
 
 
244
  sequence, metadata = self.generate_sequence(cell_type, guidance_scale)
245
 
 
246
  logger.info("Translating DNA to protein sequence...")
247
  protein_sequence = self.protein_analyzer.dna_to_protein(sequence)
248
 
 
249
  metadata['protein_sequence'] = protein_sequence
250
  metadata['protein_length'] = len(protein_sequence)
251
 
 
252
  logger.info("Analyzing protein structure and function...")
253
  protein_analysis = self.protein_analyzer.analyze_protein_with_llm(
254
  protein_sequence, cell_type, language
255
  )
256
 
 
257
  metadata['protein_analysis'] = protein_analysis
258
 
259
  logger.info("Generation and analysis complete")
 
264
  logger.error(f"Generation request failed: {error_msg}")
265
  return "", json.dumps({"error": error_msg})
266
 
267
+
268
  # Create single app instance
269
  app = DNADiffusionApp()
270
 
271
+
272
+ # ============================================
273
+ # ๐ŸŽจ Comic Classic Theme - Toon Playground
274
+ # ============================================
275
+
276
+ css = """
277
+ /* ===== ๐ŸŽจ Google Fonts Import ===== */
278
+ @import url('https://fonts.googleapis.com/css2?family=Bangers&family=Comic+Neue:wght@400;700&display=swap');
279
+
280
+ /* ===== ๐ŸŽจ Comic Classic ๋ฐฐ๊ฒฝ - ๋นˆํ‹ฐ์ง€ ํŽ˜์ดํผ + ๋„ํŠธ ํŒจํ„ด ===== */
281
+ .gradio-container {
282
+ background-color: #FEF9C3 !important;
283
+ background-image:
284
+ radial-gradient(#1F2937 1px, transparent 1px) !important;
285
+ background-size: 20px 20px !important;
286
+ min-height: 100vh !important;
287
+ font-family: 'Comic Neue', cursive, sans-serif !important;
288
+ }
289
+
290
+ /* ===== ํ—ˆ๊น…ํŽ˜์ด์Šค ์ƒ๋‹จ ์š”์†Œ ์ˆจ๊น€ ===== */
291
+ .huggingface-space-header,
292
+ #space-header,
293
+ .space-header,
294
+ [class*="space-header"],
295
+ .svelte-1ed2p3z,
296
+ .space-header-badge,
297
+ .header-badge,
298
+ [data-testid="space-header"],
299
+ .svelte-kqij2n,
300
+ .svelte-1ax1toq,
301
+ .embed-container > div:first-child {
302
+ display: none !important;
303
+ visibility: hidden !important;
304
+ height: 0 !important;
305
+ width: 0 !important;
306
+ overflow: hidden !important;
307
+ opacity: 0 !important;
308
+ pointer-events: none !important;
309
+ }
310
+
311
+ /* ===== Footer ์™„์ „ ์ˆจ๊น€ ===== */
312
+ footer,
313
+ .footer,
314
+ .gradio-container footer,
315
+ .built-with,
316
+ [class*="footer"],
317
+ .gradio-footer,
318
+ .main-footer,
319
+ div[class*="footer"],
320
+ .show-api,
321
+ .built-with-gradio,
322
+ a[href*="gradio.app"],
323
+ a[href*="huggingface.co/spaces"] {
324
+ display: none !important;
325
+ visibility: hidden !important;
326
+ height: 0 !important;
327
+ padding: 0 !important;
328
+ margin: 0 !important;
329
+ }
330
+
331
+ /* ===== Hidden controls ์ˆจ๊น€ ===== */
332
+ #hidden-controls {
333
+ display: none !important;
334
+ }
335
+
336
+ /* ===== ๋ฉ”์ธ ์ปจํ…Œ์ด๋„ˆ ===== */
337
+ #col-container {
338
+ max-width: 1200px;
339
+ margin: 0 auto;
340
+ }
341
+
342
+ /* ===== ๐ŸŽจ ํ—ค๋” ํƒ€์ดํ‹€ - ์ฝ”๋ฏน ์Šคํƒ€์ผ ===== */
343
+ .header-text h1 {
344
+ font-family: 'Bangers', cursive !important;
345
+ color: #1F2937 !important;
346
+ font-size: 3.5rem !important;
347
+ font-weight: 400 !important;
348
+ text-align: center !important;
349
+ margin-bottom: 0.5rem !important;
350
+ text-shadow:
351
+ 4px 4px 0px #FACC15,
352
+ 6px 6px 0px #1F2937 !important;
353
+ letter-spacing: 3px !important;
354
+ -webkit-text-stroke: 2px #1F2937 !important;
355
+ }
356
+
357
+ /* ===== ๐ŸŽจ ์„œ๋ธŒํƒ€์ดํ‹€ ===== */
358
+ .subtitle {
359
+ text-align: center !important;
360
+ font-family: 'Comic Neue', cursive !important;
361
+ font-size: 1.2rem !important;
362
+ color: #1F2937 !important;
363
+ margin-bottom: 1.5rem !important;
364
+ font-weight: 700 !important;
365
+ }
366
+
367
+ /* ===== ๐ŸŽจ ์นด๋“œ/ํŒจ๋„ - ๋งŒํ™” ํ”„๋ ˆ์ž„ ์Šคํƒ€์ผ ===== */
368
+ .gr-panel,
369
+ .gr-box,
370
+ .gr-form,
371
+ .block,
372
+ .gr-group {
373
+ background: #FFFFFF !important;
374
+ border: 3px solid #1F2937 !important;
375
+ border-radius: 8px !important;
376
+ box-shadow: 6px 6px 0px #1F2937 !important;
377
+ transition: all 0.2s ease !important;
378
+ }
379
+
380
+ .gr-panel:hover,
381
+ .block:hover {
382
+ transform: translate(-2px, -2px) !important;
383
+ box-shadow: 8px 8px 0px #1F2937 !important;
384
+ }
385
+
386
+ /* ===== ๐ŸŽจ ์ž…๋ ฅ ํ•„๋“œ (Textbox) ===== */
387
+ textarea,
388
+ input[type="text"],
389
+ input[type="number"] {
390
+ background: #FFFFFF !important;
391
+ border: 3px solid #1F2937 !important;
392
+ border-radius: 8px !important;
393
+ color: #1F2937 !important;
394
+ font-family: 'Comic Neue', cursive !important;
395
+ font-size: 1rem !important;
396
+ font-weight: 700 !important;
397
+ transition: all 0.2s ease !important;
398
+ }
399
+
400
+ textarea:focus,
401
+ input[type="text"]:focus,
402
+ input[type="number"]:focus {
403
+ border-color: #3B82F6 !important;
404
+ box-shadow: 4px 4px 0px #3B82F6 !important;
405
+ outline: none !important;
406
+ }
407
+
408
+ /* ===== ๐ŸŽจ ๋ผ๋””์˜ค ๋ฒ„ํŠผ ์Šคํƒ€์ผ ===== */
409
+ .gr-radio {
410
+ background: #FFFFFF !important;
411
+ border: 3px solid #1F2937 !important;
412
+ border-radius: 8px !important;
413
+ box-shadow: 4px 4px 0px #1F2937 !important;
414
+ padding: 10px !important;
415
+ }
416
+
417
+ .gr-radio label {
418
+ font-family: 'Comic Neue', cursive !important;
419
+ font-weight: 700 !important;
420
+ color: #1F2937 !important;
421
+ }
422
+
423
+ .gr-radio input[type="radio"]:checked + label {
424
+ color: #3B82F6 !important;
425
+ }
426
+
427
+ /* ===== ๐ŸŽจ Primary ๋ฒ„ํŠผ - ์ฝ”๋ฏน ๋ธ”๋ฃจ ===== */
428
+ .gr-button-primary,
429
+ button.primary,
430
+ .gr-button.primary,
431
+ .generate-btn {
432
+ background: #3B82F6 !important;
433
+ border: 3px solid #1F2937 !important;
434
+ border-radius: 8px !important;
435
+ color: #FFFFFF !important;
436
+ font-family: 'Bangers', cursive !important;
437
+ font-weight: 400 !important;
438
+ font-size: 1.3rem !important;
439
+ letter-spacing: 2px !important;
440
+ padding: 14px 28px !important;
441
+ box-shadow: 5px 5px 0px #1F2937 !important;
442
+ transition: all 0.1s ease !important;
443
+ text-shadow: 1px 1px 0px #1F2937 !important;
444
+ }
445
+
446
+ .gr-button-primary:hover,
447
+ button.primary:hover,
448
+ .gr-button.primary:hover,
449
+ .generate-btn:hover {
450
+ background: #2563EB !important;
451
+ transform: translate(-2px, -2px) !important;
452
+ box-shadow: 7px 7px 0px #1F2937 !important;
453
+ }
454
+
455
+ .gr-button-primary:active,
456
+ button.primary:active,
457
+ .gr-button.primary:active,
458
+ .generate-btn:active {
459
+ transform: translate(3px, 3px) !important;
460
+ box-shadow: 2px 2px 0px #1F2937 !important;
461
+ }
462
+
463
+ /* ===== ๐ŸŽจ Secondary ๋ฒ„ํŠผ - ์ฝ”๋ฏน ๋ ˆ๋“œ ===== */
464
+ .gr-button-secondary,
465
+ button.secondary {
466
+ background: #EF4444 !important;
467
+ border: 3px solid #1F2937 !important;
468
+ border-radius: 8px !important;
469
+ color: #FFFFFF !important;
470
+ font-family: 'Bangers', cursive !important;
471
+ font-weight: 400 !important;
472
+ font-size: 1.1rem !important;
473
+ letter-spacing: 1px !important;
474
+ box-shadow: 4px 4px 0px #1F2937 !important;
475
+ transition: all 0.1s ease !important;
476
+ text-shadow: 1px 1px 0px #1F2937 !important;
477
+ }
478
+
479
+ .gr-button-secondary:hover,
480
+ button.secondary:hover {
481
+ background: #DC2626 !important;
482
+ transform: translate(-2px, -2px) !important;
483
+ box-shadow: 6px 6px 0px #1F2937 !important;
484
+ }
485
+
486
+ /* ===== ๐ŸŽจ ์Šฌ๋ผ์ด๋” ์Šคํƒ€์ผ ===== */
487
+ input[type="range"] {
488
+ accent-color: #3B82F6 !important;
489
+ }
490
+
491
+ .gr-slider {
492
+ background: #FFFFFF !important;
493
+ }
494
+
495
+ /* ===== ๐ŸŽจ ๋ผ๋ฒจ ์Šคํƒ€์ผ ===== */
496
+ label,
497
+ .gr-input-label,
498
+ .gr-block-label {
499
+ color: #1F2937 !important;
500
+ font-family: 'Comic Neue', cursive !important;
501
+ font-weight: 700 !important;
502
+ font-size: 1rem !important;
503
+ }
504
+
505
+ span.gr-label {
506
+ color: #1F2937 !important;
507
+ }
508
+
509
+ /* ===== ๐ŸŽจ DNA Slot Machine iframe ํ”„๋ ˆ์ž„ ===== */
510
+ #dna-frame {
511
+ overflow: hidden;
512
+ position: relative;
513
+ border: 4px solid #1F2937 !important;
514
+ border-radius: 12px !important;
515
+ box-shadow: 8px 8px 0px #1F2937 !important;
516
+ background: #000000 !important;
517
+ }
518
+
519
+ #dna-frame iframe {
520
+ border: none !important;
521
+ display: block !important;
522
+ }
523
+
524
+ /* ===== ๐ŸŽจ ์Šคํฌ๋กค๋ฐ” - ์ฝ”๋ฏน ์Šคํƒ€์ผ ===== */
525
+ ::-webkit-scrollbar {
526
+ width: 12px;
527
+ height: 12px;
528
+ }
529
+
530
+ ::-webkit-scrollbar-track {
531
+ background: #FEF9C3;
532
+ border: 2px solid #1F2937;
533
+ }
534
+
535
+ ::-webkit-scrollbar-thumb {
536
+ background: #3B82F6;
537
+ border: 2px solid #1F2937;
538
+ border-radius: 0px;
539
+ }
540
+
541
+ ::-webkit-scrollbar-thumb:hover {
542
+ background: #EF4444;
543
+ }
544
+
545
+ /* ===== ๐ŸŽจ ์„ ํƒ ํ•˜์ด๋ผ์ดํŠธ ===== */
546
+ ::selection {
547
+ background: #FACC15;
548
+ color: #1F2937;
549
+ }
550
+
551
+ /* ===== ๐ŸŽจ ๋งํฌ ์Šคํƒ€์ผ ===== */
552
+ a {
553
+ color: #3B82F6 !important;
554
+ text-decoration: none !important;
555
+ font-weight: 700 !important;
556
+ }
557
+
558
+ a:hover {
559
+ color: #EF4444 !important;
560
+ }
561
+
562
+ /* ===== ๐ŸŽจ Row/Column ๊ฐ„๊ฒฉ ===== */
563
+ .gr-row {
564
+ gap: 1.5rem !important;
565
+ }
566
+
567
+ .gr-column {
568
+ gap: 1rem !important;
569
+ }
570
+
571
+ /* ===== ๋ฐ˜์‘ํ˜• ์กฐ์ • ===== */
572
+ @media (max-width: 768px) {
573
+ .header-text h1 {
574
+ font-size: 2.2rem !important;
575
+ text-shadow:
576
+ 3px 3px 0px #FACC15,
577
+ 4px 4px 0px #1F2937 !important;
578
+ }
579
 
580
+ .gr-button-primary,
581
+ button.primary {
582
+ padding: 12px 20px !important;
583
+ font-size: 1.1rem !important;
 
 
584
  }
585
+
586
+ .gr-panel,
587
+ .block {
588
+ box-shadow: 4px 4px 0px #1F2937 !important;
589
  }
590
+ }
591
+
592
+ /* ===== ๐ŸŽจ ๋‹คํฌ๋ชจ๋“œ ๋น„ํ™œ์„ฑํ™” ===== */
593
+ @media (prefers-color-scheme: dark) {
594
+ .gradio-container {
595
+ background-color: #FEF9C3 !important;
596
+ }
597
+ }
598
+ """
599
+
600
+
601
+ def create_demo():
602
+ """Create the Gradio demo interface"""
603
 
604
  # JavaScript for handling communication between iframe and Gradio
605
  js = """
 
619
  radioInputs.forEach(input => {
620
  if (input.value === event.data.cellType) {
621
  input.checked = true;
 
622
  input.dispatchEvent(new Event('change'));
623
  }
624
  });
 
660
  }
661
  } catch (e) {
662
  console.error('Failed to parse metadata:', e);
 
663
  iframe.contentWindow.postMessage({
664
  type: 'sequence_generated',
665
  sequence: sequence,
 
673
  }
674
  """
675
 
676
+ with gr.Blocks(css=css, js=js, fill_height=True) as demo:
677
+
678
+ # HOME Badge
679
+ gr.HTML("""
680
+ <div style="text-align: center; margin: 20px 0 10px 0;">
681
+ <a href="https://www.humangen.ai" target="_blank" style="text-decoration: none;">
682
+ <img src="https://img.shields.io/static/v1?label=๐Ÿ  HOME&message=HUMANGEN.AI&color=0000ff&labelColor=ffcc00&style=for-the-badge" alt="HOME">
683
+ </a>
684
+ </div>
685
+ """)
686
+
687
+ # Header Title
688
+ gr.Markdown(
689
+ """
690
+ # ๐Ÿงฌ DNA DIFFUSION GENERATOR ๐ŸŽฐ
691
+ """,
692
+ elem_classes="header-text"
693
+ )
694
+
695
+ gr.Markdown(
696
+ """
697
+ <p class="subtitle">๐Ÿ”ฌ Generate DNA sequences with AI-powered diffusion model! Translate to protein & analyze! ๐Ÿงช</p>
698
+ """,
699
+ )
700
 
701
  # Hidden controls for backend processing
702
  with gr.Column(elem_id="hidden-controls", visible=False):
 
726
  metadata_output = gr.Textbox(label="Metadata", elem_id="metadata-output")
727
 
728
  # Main interface - the slot machine in an iframe
 
729
  escaped_html = html.escape(SLOT_MACHINE_HTML, quote=True)
730
  iframe_html = f'<iframe srcdoc="{escaped_html}" style="width: 100%; height: 800px; border: none; display: block;"></iframe>'
731
 
 
755
 
756
  return demo
757
 
758
+
759
  # Launch the app
760
  if __name__ == "__main__":
761
  demo = create_demo()
762
 
 
763
  import argparse
764
  parser = argparse.ArgumentParser(description="DNA-Diffusion Gradio App")
765
  parser.add_argument("--share", action="store_true", help="Create a public shareable link")
 
767
  parser.add_argument("--host", type=str, default="0.0.0.0", help="Host to run the app on")
768
  args = parser.parse_args()
769
 
 
 
770
  if os.getenv("SPACE_ID"):
 
771
  args.host = "0.0.0.0"
772
  args.port = 7860
773
  args.share = False