DevTail0r commited on
Commit
cc25091
Β·
verified Β·
1 Parent(s): 9f58a37

Update pages/artifacts.py

Browse files
Files changed (1) hide show
  1. pages/artifacts.py +71 -21
pages/artifacts.py CHANGED
@@ -1,10 +1,35 @@
 
1
  """Artifacts tab: Summary, Podcast, and Quiz generation."""
2
 
 
 
3
  from datetime import datetime
4
  from state import UserData, get_active_notebook, get_all_artifacts, get_latest_artifact
5
  from mock_data import generate_mock_artifact
6
 
7
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
8
  def _format_time(iso_str: str) -> str:
9
  try:
10
  dt = datetime.fromisoformat(iso_str)
@@ -36,7 +61,8 @@ def _render_artifact_content(artifact) -> str:
36
  f'border-radius:10px;">'
37
  f'<div style="font-size:0.82rem; color:#8090d0; margin-bottom:8px;">🎧 Podcast Audio</div>'
38
  f'<audio controls style="width:100%; border-radius:6px;">'
39
- f'<source src="/file={artifact.audio_path}" type="audio/wav">'
 
40
  f'Your browser does not support the audio element.'
41
  f'</audio>'
42
  f'</div>'
@@ -47,10 +73,10 @@ def _render_artifact_content(artifact) -> str:
47
  'background:rgba(102,126,234,0.06); border:1px solid rgba(102,126,234,0.15); '
48
  'border-radius:10px; margin-top:8px;">'
49
  '<span style="font-size:1.3rem;">πŸ”‡</span>'
50
- '<span style="font-size:0.85rem; color:#8888aa;">Audio generation failed or Bark is not installed. '
51
- 'Install <code>bark</code> and <code>scipy</code> to enable TTS.</span>'
52
  '</div>'
53
  )
 
54
  return html
55
 
56
 
@@ -96,17 +122,20 @@ def render_conv_summary_section(state: UserData) -> str:
96
  if not nb:
97
  return ""
98
  if not nb.messages:
99
- return "*No conversation yet. Start chatting in the **Chat** tab first.*"
 
 
 
 
 
100
  summaries = get_all_artifacts(nb, "conversation_summary")
101
  if not summaries:
102
- return "*Click Generate to create a conversation summary.*"
103
- latest = summaries[0]
104
- time_str = _format_time(latest.created_at)
105
- md = "*Generated " + time_str + "*\n\n---\n\n" + latest.content
106
- if len(summaries) > 1:
107
- prev = ", ".join(_format_time(a.created_at) for a in summaries[1:])
108
- md += "\n\n---\n\n**Previous summaries:** " + prev
109
- return md
110
  def handle_gen_conv_summary(style: str, state: UserData) -> tuple[UserData, str]:
111
  nb = get_active_notebook(state)
112
  if not nb or not nb.messages:
@@ -125,14 +154,12 @@ def render_doc_summary_section(state: UserData) -> str:
125
  return ""
126
  summaries = get_all_artifacts(nb, "document_summary")
127
  if not summaries:
128
- return "*Click Generate to create a document summary.*"
129
- latest = summaries[0]
130
- time_str = _format_time(latest.created_at)
131
- md = "*Generated " + time_str + "*\n\n---\n\n" + latest.content
132
- if len(summaries) > 1:
133
- prev = ", ".join(_format_time(a.created_at) for a in summaries[1:])
134
- md += "\n\n---\n\n**Previous summaries:** " + prev
135
- return md
136
  def handle_gen_doc_summary(style: str, state: UserData) -> tuple[UserData, str]:
137
  nb = get_active_notebook(state)
138
  if not nb:
@@ -263,4 +290,27 @@ def handle_gen_quiz(num_questions: int, state: UserData) -> tuple[UserData, str]
263
  num_q = int(num_questions) if num_questions else 5
264
  artifact = generate_quiz(nb, num_q)
265
  nb.artifacts.append(artifact)
266
- return state, render_quiz_section(state)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+
2
  """Artifacts tab: Summary, Podcast, and Quiz generation."""
3
 
4
+ import os
5
+ import tempfile
6
  from datetime import datetime
7
  from state import UserData, get_active_notebook, get_all_artifacts, get_latest_artifact
8
  from mock_data import generate_mock_artifact
9
 
10
 
11
+ def _save_artifact_to_temp_file(artifact) -> str | None:
12
+ """Save artifact content to a temporary file. Returns the file path."""
13
+ try:
14
+ if artifact.type == "podcast" and artifact.audio_path:
15
+ if os.path.exists(artifact.audio_path):
16
+ return artifact.audio_path
17
+ else:
18
+ safe_title = artifact.title.replace(" ", "_")[:50]
19
+ # Save quizzes as HTML (they contain styled HTML), others as markdown
20
+ suffix = '.html' if artifact.type == "quiz" else '.md'
21
+ with tempfile.NamedTemporaryFile(
22
+ mode='w', suffix=suffix, prefix=f'{safe_title}_',
23
+ delete=False, encoding='utf-8'
24
+ ) as f:
25
+ f.write(artifact.content)
26
+ return f.name
27
+ except Exception as e:
28
+ import logging
29
+ logging.error(f"Failed to save artifact: {e}")
30
+ return None
31
+
32
+
33
  def _format_time(iso_str: str) -> str:
34
  try:
35
  dt = datetime.fromisoformat(iso_str)
 
61
  f'border-radius:10px;">'
62
  f'<div style="font-size:0.82rem; color:#8090d0; margin-bottom:8px;">🎧 Podcast Audio</div>'
63
  f'<audio controls style="width:100%; border-radius:6px;">'
64
+ f'<source src="/gradio_api/file={artifact.audio_path}" type="audio/mpeg">'
65
+ f'<source src="/file={artifact.audio_path}" type="audio/mpeg">'
66
  f'Your browser does not support the audio element.'
67
  f'</audio>'
68
  f'</div>'
 
73
  'background:rgba(102,126,234,0.06); border:1px solid rgba(102,126,234,0.15); '
74
  'border-radius:10px; margin-top:8px;">'
75
  '<span style="font-size:1.3rem;">πŸ”‡</span>'
76
+ '<span style="font-size:0.85rem; color:#8888aa;">Audio generation failed. Verify <code>OPENAI_API_KEY</code> is set in Space secrets and your account has credits.</span>'
 
77
  '</div>'
78
  )
79
+
80
  return html
81
 
82
 
 
122
  if not nb:
123
  return ""
124
  if not nb.messages:
125
+ return (
126
+ '<div style="text-align:center; padding:30px 20px; color:#606078; '
127
+ 'border:1px dashed rgba(255,255,255,0.08); border-radius:14px;">'
128
+ '<p style="margin:0;">No conversation yet. Start chatting in the <strong>Chat</strong> tab first.</p>'
129
+ '</div>'
130
+ )
131
  summaries = get_all_artifacts(nb, "conversation_summary")
132
  if not summaries:
133
+ return '<p style="color:#606078; text-align:center; padding:20px;">Click "Generate" to create a conversation summary.</p>'
134
+ html = _render_artifact_content(summaries[0])
135
+ html += _render_history(summaries, "conversation summaries")
136
+ return html
137
+
138
+
 
 
139
  def handle_gen_conv_summary(style: str, state: UserData) -> tuple[UserData, str]:
140
  nb = get_active_notebook(state)
141
  if not nb or not nb.messages:
 
154
  return ""
155
  summaries = get_all_artifacts(nb, "document_summary")
156
  if not summaries:
157
+ return '<p style="color:#606078; text-align:center; padding:20px;">Click "Generate" to create a document summary.</p>'
158
+ html = _render_artifact_content(summaries[0])
159
+ html += _render_history(summaries, "document summaries")
160
+ return html
161
+
162
+
 
 
163
  def handle_gen_doc_summary(style: str, state: UserData) -> tuple[UserData, str]:
164
  nb = get_active_notebook(state)
165
  if not nb:
 
290
  num_q = int(num_questions) if num_questions else 5
291
  artifact = generate_quiz(nb, num_q)
292
  nb.artifacts.append(artifact)
293
+ return state, render_quiz_section(state)
294
+
295
+
296
+ # ── Download Handlers ────────────────────────────────────────────────────────
297
+
298
+ def _find_artifact_by_id(state: UserData, artifact_id: str):
299
+ """Find an artifact by ID across all notebooks (current one only for now)."""
300
+ nb = get_active_notebook(state)
301
+ if not nb:
302
+ return None
303
+ for artifact in nb.artifacts:
304
+ if artifact.id == artifact_id:
305
+ return artifact
306
+ return None
307
+
308
+
309
+ def download_artifact(artifact_id: str, state: UserData) -> str | None:
310
+ """Download an artifact and return the file path for Gradio download."""
311
+ artifact = _find_artifact_by_id(state, artifact_id)
312
+ if not artifact:
313
+ return None
314
+
315
+ file_path = _save_artifact_to_temp_file(artifact)
316
+ return file_path