banao-tech commited on
Commit
5c0475d
Β·
verified Β·
1 Parent(s): 75e5f3a

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +271 -75
app.py CHANGED
@@ -4,7 +4,6 @@ import anthropic
4
  from pypdf import PdfReader
5
 
6
  # ── Anthropic client ──────────────────────────────────────────────────────────
7
- # Set ANTHROPIC_API_KEY in your HuggingFace Space β†’ Settings β†’ Repository secrets
8
  client = anthropic.Anthropic(api_key=os.environ.get("ANTHROPIC_API_KEY"))
9
 
10
  SYSTEM_PROMPT = """You are an expert problem analyst and innovation coach helping interns understand real-world problems and brainstorm creative solutions.
@@ -21,7 +20,7 @@ When given a transcription or document, you will:
21
  - Identify underlying causes (technical, organizational, process-related)
22
 
23
  3. **BRAINSTORMING IDEAS**
24
- - Generate 5–8 diverse, creative solution directions
25
  - Include both quick wins and long-term strategic ideas
26
  - Think across technology, process, and people dimensions
27
 
@@ -32,7 +31,6 @@ Keep your tone encouraging, clear, and structured. Use headers and bullet points
32
 
33
 
34
  def extract_text_from_pdf(pdf_file) -> str:
35
- """Extract all text from an uploaded PDF file."""
36
  reader = PdfReader(pdf_file)
37
  pages_text = []
38
  for page in reader.pages:
@@ -43,11 +41,6 @@ def extract_text_from_pdf(pdf_file) -> str:
43
 
44
 
45
  def run_agent(text_input: str, pdf_file, progress=gr.Progress()):
46
- """
47
- Core agent function.
48
- Accepts either pasted text or a PDF upload, then streams the analysis.
49
- """
50
- # ── Determine input source ────────────────────────────────────────────────
51
  content = ""
52
 
53
  if pdf_file is not None:
@@ -55,24 +48,23 @@ def run_agent(text_input: str, pdf_file, progress=gr.Progress()):
55
  try:
56
  content = extract_text_from_pdf(pdf_file)
57
  if not content.strip():
58
- yield "⚠️ Could not extract text from this PDF. It may be scanned/image-based. Please paste the text manually."
59
  return
60
  except Exception as e:
61
- yield f"⚠️ Error reading PDF: {str(e)}"
62
  return
63
 
64
  elif text_input and text_input.strip():
65
  content = text_input.strip()
66
 
67
  else:
68
- yield "⚠️ Please paste a transcription or upload a PDF file to get started."
69
  return
70
 
71
  if len(content) < 50:
72
- yield "⚠️ The input seems too short. Please provide more context for a meaningful analysis."
73
  return
74
 
75
- # ── Truncate very long inputs to stay within token limits ─────────────────
76
  MAX_CHARS = 12000
77
  if len(content) > MAX_CHARS:
78
  content = content[:MAX_CHARS] + "\n\n[... content truncated for length ...]"
@@ -87,11 +79,10 @@ def run_agent(text_input: str, pdf_file, progress=gr.Progress()):
87
 
88
  Please provide a full problem analysis and brainstorming session based on this content."""
89
 
90
- # ── Stream from Anthropic API ─────────────────────────────────────────────
91
  output = ""
92
  try:
93
  with client.messages.stream(
94
- model="claude-sonnet-4-20250514",
95
  max_tokens=2048,
96
  system=SYSTEM_PROMPT,
97
  messages=[{"role": "user", "content": user_message}],
@@ -102,99 +93,306 @@ Please provide a full problem analysis and brainstorming session based on this c
102
  yield output
103
 
104
  except anthropic.AuthenticationError:
105
- yield "⚠️ Invalid API key. Please set your ANTHROPIC_API_KEY in the Space secrets."
106
  except anthropic.RateLimitError:
107
- yield "⚠️ Rate limit hit. Please wait a moment and try again."
108
  except Exception as e:
109
- yield f"⚠️ Unexpected error: {str(e)}"
110
-
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
111
 
112
  # ── Gradio UI ─────────────────────────────────────────────────────────────────
113
  with gr.Blocks(
114
- title="Intern Problem Brainstormer",
115
- theme=gr.themes.Soft(
116
- primary_hue="violet",
117
- secondary_hue="slate",
118
- font=[gr.themes.GoogleFont("Inter"), "sans-serif"],
119
- ),
120
- css="""
121
- #header { text-align: center; padding: 1.5rem 0 0.5rem; }
122
- #header h1 { font-size: 2rem; font-weight: 700; margin-bottom: 0.25rem; }
123
- #header p { color: #6b7280; font-size: 0.95rem; }
124
- .input-box textarea { font-size: 14px !important; line-height: 1.6 !important; }
125
- #run-btn { font-size: 1rem !important; padding: 0.65rem 2rem !important; }
126
- #output-box { font-size: 14px; line-height: 1.75; }
127
- .gr-tab-item { font-weight: 500; }
128
- """,
129
  ) as demo:
130
 
131
- # Header
132
  gr.HTML("""
133
- <div id="header">
134
- <h1>🧠 Intern Problem Brainstormer</h1>
135
- <p>Paste a meeting transcription or upload a PDF β€” get instant problem analysis & brainstormed solutions.</p>
 
136
  </div>
137
  """)
138
 
139
- gr.Markdown("---")
140
-
141
  with gr.Row(equal_height=False):
142
- # ── Left column: Input ────────────────────────────────────────────────
143
- with gr.Column(scale=1):
144
- gr.Markdown("### πŸ“₯ Input")
145
 
146
  with gr.Tabs():
147
- with gr.Tab("πŸ“ Paste Text"):
148
  text_input = gr.Textbox(
149
- label="Transcription or problem description",
150
- placeholder="Paste your meeting notes, transcription, or problem statement here...",
151
- lines=14,
152
- max_lines=30,
153
- elem_classes=["input-box"],
154
  )
155
 
156
- with gr.Tab("πŸ“„ Upload PDF"):
157
  pdf_input = gr.File(
158
- label="Upload a PDF document",
159
  file_types=[".pdf"],
160
  file_count="single",
161
  )
162
- gr.Markdown(
163
- "<small style='color:#6b7280'>Text-based PDFs only. Scanned PDFs won't work.</small>"
164
- )
165
 
166
  run_btn = gr.Button(
167
- "⚑ Analyse & Brainstorm",
168
  variant="primary",
169
  elem_id="run-btn",
170
  )
171
 
172
- with gr.Accordion("ℹ️ How to use", open=False):
173
  gr.Markdown("""
174
- **Option 1 – Paste Text**
175
- Copy-paste any transcription, meeting notes, or problem description into the text box.
176
 
177
- **Option 2 – Upload PDF**
178
- Upload a text-based PDF (e.g. a research doc, spec, or report).
179
 
180
- **Then click ⚑ Analyse & Brainstorm** to get:
181
  - A clear articulation of the core problem
182
- - Root cause analysis
183
- - 5–8 creative brainstormed ideas
184
- - Concrete next steps for you as an intern
185
  """)
186
 
187
- # ── Right column: Output ──────────────────────────────────────────────
188
- with gr.Column(scale=1):
189
- gr.Markdown("### πŸ’‘ Analysis & Ideas")
190
  output = gr.Markdown(
191
- value="*Your analysis will appear here...*",
192
- elem_id="output-box",
193
  )
194
 
195
- # ── Examples ──────────────────────────────────────────────────────────────
196
- gr.Markdown("---")
197
- gr.Markdown("### πŸ§ͺ Try an example")
198
  gr.Examples(
199
  examples=[
200
  [
@@ -207,10 +405,9 @@ Upload a text-based PDF (e.g. a research doc, spec, or report).
207
  ],
208
  ],
209
  inputs=[text_input, pdf_input],
210
- label="Click an example to load it, then hit Analyse & Brainstorm",
211
  )
212
 
213
- # ── Wire up ───────────────────────────────────────────────────────────────
214
  run_btn.click(
215
  fn=run_agent,
216
  inputs=[text_input, pdf_input],
@@ -218,7 +415,6 @@ Upload a text-based PDF (e.g. a research doc, spec, or report).
218
  show_progress="full",
219
  )
220
 
221
- # Also allow Ctrl+Enter from the text box
222
  text_input.submit(
223
  fn=run_agent,
224
  inputs=[text_input, pdf_input],
 
4
  from pypdf import PdfReader
5
 
6
  # ── Anthropic client ──────────────────────────────────────────────────────────
 
7
  client = anthropic.Anthropic(api_key=os.environ.get("ANTHROPIC_API_KEY"))
8
 
9
  SYSTEM_PROMPT = """You are an expert problem analyst and innovation coach helping interns understand real-world problems and brainstorm creative solutions.
 
20
  - Identify underlying causes (technical, organizational, process-related)
21
 
22
  3. **BRAINSTORMING IDEAS**
23
+ - Generate 5-8 diverse, creative solution directions
24
  - Include both quick wins and long-term strategic ideas
25
  - Think across technology, process, and people dimensions
26
 
 
31
 
32
 
33
  def extract_text_from_pdf(pdf_file) -> str:
 
34
  reader = PdfReader(pdf_file)
35
  pages_text = []
36
  for page in reader.pages:
 
41
 
42
 
43
  def run_agent(text_input: str, pdf_file, progress=gr.Progress()):
 
 
 
 
 
44
  content = ""
45
 
46
  if pdf_file is not None:
 
48
  try:
49
  content = extract_text_from_pdf(pdf_file)
50
  if not content.strip():
51
+ yield "Could not extract text from this PDF. It may be scanned or image-based. Please paste the text manually."
52
  return
53
  except Exception as e:
54
+ yield f"Error reading PDF: {str(e)}"
55
  return
56
 
57
  elif text_input and text_input.strip():
58
  content = text_input.strip()
59
 
60
  else:
61
+ yield "Please paste a transcription or upload a PDF file to get started."
62
  return
63
 
64
  if len(content) < 50:
65
+ yield "The input seems too short. Please provide more context for a meaningful analysis."
66
  return
67
 
 
68
  MAX_CHARS = 12000
69
  if len(content) > MAX_CHARS:
70
  content = content[:MAX_CHARS] + "\n\n[... content truncated for length ...]"
 
79
 
80
  Please provide a full problem analysis and brainstorming session based on this content."""
81
 
 
82
  output = ""
83
  try:
84
  with client.messages.stream(
85
+ model="claude-sonnet-4-6",
86
  max_tokens=2048,
87
  system=SYSTEM_PROMPT,
88
  messages=[{"role": "user", "content": user_message}],
 
93
  yield output
94
 
95
  except anthropic.AuthenticationError:
96
+ yield "Invalid API key. Please set your ANTHROPIC_API_KEY in the Space secrets."
97
  except anthropic.RateLimitError:
98
+ yield "Rate limit reached. Please wait a moment and try again."
99
  except Exception as e:
100
+ yield f"Unexpected error: {str(e)}"
101
+
102
+
103
+ # ── Custom CSS ────────────────────────────────────────────────────────────────
104
+ custom_css = """
105
+ @import url('https://fonts.googleapis.com/css2?family=Sora:wght@300;400;500;600&family=JetBrains+Mono:wght@400;500&display=swap');
106
+
107
+ * {
108
+ font-family: 'Sora', sans-serif !important;
109
+ }
110
+
111
+ body, .gradio-container {
112
+ background-color: #0d1117 !important;
113
+ color: #e2e8f0 !important;
114
+ }
115
+
116
+ .gradio-container {
117
+ max-width: 1200px !important;
118
+ margin: 0 auto !important;
119
+ }
120
+
121
+ /* Header */
122
+ #app-header {
123
+ padding: 2.5rem 0 1.5rem;
124
+ border-bottom: 1px solid #1e2d3d;
125
+ margin-bottom: 2rem;
126
+ }
127
+
128
+ #app-header h1 {
129
+ font-size: 1.6rem;
130
+ font-weight: 600;
131
+ color: #e2e8f0;
132
+ letter-spacing: -0.02em;
133
+ margin: 0 0 0.3rem 0;
134
+ }
135
+
136
+ #app-header p {
137
+ font-size: 0.875rem;
138
+ color: #64748b;
139
+ margin: 0;
140
+ font-weight: 300;
141
+ }
142
+
143
+ /* Accent bar */
144
+ #accent-bar {
145
+ width: 2.5rem;
146
+ height: 2px;
147
+ background: linear-gradient(90deg, #0ea5e9, #38bdf8);
148
+ margin-bottom: 0.75rem;
149
+ border-radius: 2px;
150
+ }
151
+
152
+ /* Section labels */
153
+ .section-label {
154
+ font-size: 0.7rem !important;
155
+ font-weight: 500 !important;
156
+ text-transform: uppercase !important;
157
+ letter-spacing: 0.12em !important;
158
+ color: #475569 !important;
159
+ margin-bottom: 0.75rem !important;
160
+ }
161
+
162
+ /* Input panels */
163
+ .input-panel, .output-panel {
164
+ background: #111827 !important;
165
+ border: 1px solid #1e2d3d !important;
166
+ border-radius: 8px !important;
167
+ padding: 1.25rem !important;
168
+ }
169
+
170
+ /* Textbox */
171
+ textarea, .gr-textbox textarea {
172
+ background: #0d1117 !important;
173
+ border: 1px solid #1e2d3d !important;
174
+ border-radius: 6px !important;
175
+ color: #cbd5e1 !important;
176
+ font-size: 0.875rem !important;
177
+ line-height: 1.7 !important;
178
+ font-family: 'JetBrains Mono', monospace !important;
179
+ padding: 0.875rem !important;
180
+ resize: vertical !important;
181
+ }
182
+
183
+ textarea:focus {
184
+ border-color: #0ea5e9 !important;
185
+ outline: none !important;
186
+ box-shadow: 0 0 0 3px rgba(14, 165, 233, 0.08) !important;
187
+ }
188
+
189
+ textarea::placeholder {
190
+ color: #334155 !important;
191
+ }
192
+
193
+ /* File upload */
194
+ .gr-file, .upload-button {
195
+ background: #0d1117 !important;
196
+ border: 1px dashed #1e2d3d !important;
197
+ border-radius: 6px !important;
198
+ color: #64748b !important;
199
+ }
200
+
201
+ /* Tabs */
202
+ .tab-nav {
203
+ border-bottom: 1px solid #1e2d3d !important;
204
+ margin-bottom: 1rem !important;
205
+ }
206
+
207
+ .tab-nav button {
208
+ font-size: 0.8rem !important;
209
+ font-weight: 500 !important;
210
+ color: #475569 !important;
211
+ border: none !important;
212
+ background: transparent !important;
213
+ padding: 0.5rem 1rem !important;
214
+ border-radius: 0 !important;
215
+ letter-spacing: 0.02em !important;
216
+ }
217
+
218
+ .tab-nav button.selected {
219
+ color: #0ea5e9 !important;
220
+ border-bottom: 2px solid #0ea5e9 !important;
221
+ }
222
+
223
+ /* Run button */
224
+ #run-btn {
225
+ background: #0ea5e9 !important;
226
+ color: #0d1117 !important;
227
+ border: none !important;
228
+ border-radius: 6px !important;
229
+ font-size: 0.875rem !important;
230
+ font-weight: 600 !important;
231
+ padding: 0.7rem 1.75rem !important;
232
+ letter-spacing: 0.01em !important;
233
+ transition: background 0.15s ease !important;
234
+ margin-top: 0.75rem !important;
235
+ width: 100% !important;
236
+ }
237
+
238
+ #run-btn:hover {
239
+ background: #38bdf8 !important;
240
+ }
241
+
242
+ /* Output markdown */
243
+ #output-area {
244
+ background: #0d1117 !important;
245
+ border: 1px solid #1e2d3d !important;
246
+ border-radius: 6px !important;
247
+ padding: 1.25rem !important;
248
+ min-height: 400px !important;
249
+ font-size: 0.875rem !important;
250
+ line-height: 1.8 !important;
251
+ color: #cbd5e1 !important;
252
+ }
253
+
254
+ #output-area h1, #output-area h2, #output-area h3 {
255
+ color: #e2e8f0 !important;
256
+ font-weight: 600 !important;
257
+ letter-spacing: -0.01em !important;
258
+ margin-top: 1.25rem !important;
259
+ }
260
+
261
+ #output-area strong {
262
+ color: #0ea5e9 !important;
263
+ font-weight: 500 !important;
264
+ }
265
+
266
+ #output-area ul, #output-area ol {
267
+ padding-left: 1.25rem !important;
268
+ color: #94a3b8 !important;
269
+ }
270
+
271
+ #output-area code {
272
+ background: #1e2d3d !important;
273
+ border-radius: 3px !important;
274
+ padding: 0.1em 0.35em !important;
275
+ font-family: 'JetBrains Mono', monospace !important;
276
+ font-size: 0.82em !important;
277
+ color: #38bdf8 !important;
278
+ }
279
+
280
+ /* Accordion */
281
+ .gr-accordion {
282
+ background: #111827 !important;
283
+ border: 1px solid #1e2d3d !important;
284
+ border-radius: 6px !important;
285
+ margin-top: 0.75rem !important;
286
+ }
287
+
288
+ .gr-accordion .label-wrap {
289
+ font-size: 0.8rem !important;
290
+ color: #475569 !important;
291
+ font-weight: 500 !important;
292
+ }
293
+
294
+ /* Examples */
295
+ .gr-examples {
296
+ margin-top: 1.5rem !important;
297
+ }
298
+
299
+ .gr-examples table {
300
+ background: #111827 !important;
301
+ border: 1px solid #1e2d3d !important;
302
+ border-radius: 6px !important;
303
+ }
304
+
305
+ .gr-examples td {
306
+ color: #64748b !important;
307
+ font-size: 0.8rem !important;
308
+ }
309
+
310
+ /* Divider */
311
+ .divider {
312
+ border: none !important;
313
+ border-top: 1px solid #1e2d3d !important;
314
+ margin: 1.5rem 0 !important;
315
+ }
316
+
317
+ /* Labels */
318
+ label, .gr-label {
319
+ color: #475569 !important;
320
+ font-size: 0.8rem !important;
321
+ font-weight: 500 !important;
322
+ letter-spacing: 0.03em !important;
323
+ }
324
+
325
+ /* Scrollbar */
326
+ ::-webkit-scrollbar { width: 5px; }
327
+ ::-webkit-scrollbar-track { background: #0d1117; }
328
+ ::-webkit-scrollbar-thumb { background: #1e2d3d; border-radius: 3px; }
329
+ ::-webkit-scrollbar-thumb:hover { background: #0ea5e9; }
330
+ """
331
 
332
  # ── Gradio UI ─────────────────────────────────────────────────────────────────
333
  with gr.Blocks(
334
+ title="Problem Brainstormer",
335
+ css=custom_css,
 
 
 
 
 
 
 
 
 
 
 
 
 
336
  ) as demo:
337
 
 
338
  gr.HTML("""
339
+ <div id="app-header">
340
+ <div id="accent-bar"></div>
341
+ <h1>Problem Brainstormer</h1>
342
+ <p>Paste a meeting transcript or upload a document to get structured problem analysis and solution ideas.</p>
343
  </div>
344
  """)
345
 
 
 
346
  with gr.Row(equal_height=False):
347
+ with gr.Column(scale=1, elem_classes=["input-panel"]):
348
+ gr.HTML('<p class="section-label">Input</p>')
 
349
 
350
  with gr.Tabs():
351
+ with gr.Tab("Paste Text"):
352
  text_input = gr.Textbox(
353
+ label="",
354
+ placeholder="Paste your meeting notes, transcript, or problem description here...",
355
+ lines=16,
356
+ max_lines=32,
 
357
  )
358
 
359
+ with gr.Tab("Upload PDF"):
360
  pdf_input = gr.File(
361
+ label="",
362
  file_types=[".pdf"],
363
  file_count="single",
364
  )
365
+ gr.HTML('<p style="font-size:0.75rem;color:#334155;margin-top:0.5rem">Text-based PDFs only. Scanned documents are not supported.</p>')
 
 
366
 
367
  run_btn = gr.Button(
368
+ "Analyze and Brainstorm",
369
  variant="primary",
370
  elem_id="run-btn",
371
  )
372
 
373
+ with gr.Accordion("How to use", open=False):
374
  gr.Markdown("""
375
+ **Paste Text** β€” Copy any transcript, meeting notes, or problem statement into the text field.
 
376
 
377
+ **Upload PDF** β€” Upload a text-based PDF such as a spec, report, or research document.
 
378
 
379
+ **Click Analyze** to receive:
380
  - A clear articulation of the core problem
381
+ - Root cause breakdown
382
+ - 5–8 creative solution directions
383
+ - Concrete next steps you can take as an intern
384
  """)
385
 
386
+ with gr.Column(scale=1, elem_classes=["output-panel"]):
387
+ gr.HTML('<p class="section-label">Analysis</p>')
 
388
  output = gr.Markdown(
389
+ value="*Your analysis will appear here once you submit.*",
390
+ elem_id="output-area",
391
  )
392
 
393
+ gr.HTML('<hr class="divider">')
394
+ gr.HTML('<p class="section-label">Example Inputs</p>')
395
+
396
  gr.Examples(
397
  examples=[
398
  [
 
405
  ],
406
  ],
407
  inputs=[text_input, pdf_input],
408
+ label="",
409
  )
410
 
 
411
  run_btn.click(
412
  fn=run_agent,
413
  inputs=[text_input, pdf_input],
 
415
  show_progress="full",
416
  )
417
 
 
418
  text_input.submit(
419
  fn=run_agent,
420
  inputs=[text_input, pdf_input],