Basementup commited on
Commit
31eb36e
·
verified ·
1 Parent(s): d3d175a

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +436 -223
app.py CHANGED
@@ -1,351 +1,564 @@
1
  import gradio as gr
2
- import anthropic
3
  import os
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
4
 
5
- # ── Anthropic client (key from HF Space secret) ──────────────────────────────
6
  def get_client():
7
- api_key = os.environ.get("ANTHROPIC_API_KEY", "")
8
- if not api_key:
9
- raise gr.Error("⚠️ ANTHROPIC_API_KEY secret not set. Add it in Space Settings → Secrets.")
10
- return anthropic.Anthropic(api_key=api_key)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
11
 
12
 
13
  # ── Technique runners ─────────────────────────────────────────────────────────
14
 
15
- def run_perspective(persona: str, task: str, metaphor: str) -> str:
16
- """Technique 1 – Perspective-Shifting Prompts"""
17
  if not persona.strip() or not task.strip():
18
- return "⚠️ Please fill in both the Persona and the Task fields."
19
- prompt = f"Be a {persona} who explains {task}"
20
  if metaphor.strip():
21
- prompt += f" using {metaphor} metaphors"
22
- prompt += ". Give a rich, vivid explanation."
23
- client = get_client()
24
- msg = client.messages.create(
25
- model="claude-sonnet-4-20250514",
26
- max_tokens=1000,
27
- messages=[{"role": "user", "content": prompt}],
28
- )
29
- return msg.content[0].text
30
 
31
 
32
- def run_constrained(ingredients: str, count: int) -> str:
33
- """Technique 2 – Constrained Creativity Boosters"""
34
  if not ingredients.strip():
35
- return "⚠️ Please enter at least one ingredient / constraint."
36
  items = [i.strip() for i in ingredients.split(",") if i.strip()]
37
  prompt = (
38
- f"Suggest {int(count)} innovative startup ideas that MUST incorporate ALL of the "
39
- f"following elements: {', '.join(items)}. "
40
- "Be wildly creative — find non-obvious connections. "
41
- "For each idea: name, one-line pitch, and why the combination is powerful."
42
  )
43
- client = get_client()
44
- msg = client.messages.create(
45
- model="claude-sonnet-4-20250514",
46
- max_tokens=1000,
47
- messages=[{"role": "user", "content": prompt}],
48
- )
49
- return msg.content[0].text
50
 
51
 
52
- def run_meta(goal: str, criteria: str) -> str:
53
- """Technique 3 – Meta-Prompts for Precision"""
54
  if not goal.strip():
55
- return "⚠️ Please enter a goal."
56
- ranking_criteria = criteria if criteria.strip() else "1. Effectiveness 2. Cost 3. Ease of implementation"
57
  prompt = (
58
- f"First, suggest 3 concrete ways to: {goal}\n\n"
59
- f"Then rank them by:\n{ranking_criteria}\n\n"
60
- "Finally, critically identify 2–3 potential flaws in your own ranking system and "
61
- "explain how those flaws could be mitigated."
62
- )
63
- client = get_client()
64
- msg = client.messages.create(
65
- model="claude-sonnet-4-20250514",
66
- max_tokens=1200,
67
- messages=[{"role": "user", "content": prompt}],
68
  )
69
- return msg.content[0].text
70
 
71
 
72
- def run_cot(problem: str, domain: str) -> str:
73
- """Technique 4 – Chain-of-Thought Templates"""
74
  if not problem.strip():
75
- return "⚠️ Please describe the problem."
76
- domain_hint = f" (domain: {domain})" if domain.strip() else ""
77
  prompt = (
78
- f"Let's solve this complex problem step by step{domain_hint}: {problem}\n\n"
79
  "Follow this exact structure:\n"
80
- "1. Break down the key components of the problem\n"
81
- "2. Identify 3 potential failure points\n"
82
- "3. Propose specific countermeasures for each failure point\n"
83
- "4. Create a phased implementation roadmap\n"
84
- "5. Predict 2 unexpected side effects (positive or negative) and how to handle them\n\n"
85
- "Be thorough and specific at every step."
86
  )
87
- client = get_client()
88
- msg = client.messages.create(
89
- model="claude-sonnet-4-20250514",
90
- max_tokens=1400,
91
- messages=[{"role": "user", "content": prompt}],
92
- )
93
- return msg.content[0].text
94
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
95
 
96
- # ── Custom CSS ────────────────────────────────────────────────────────────────
97
  CSS = """
98
- @import url('https://fonts.googleapis.com/css2?family=Syne:wght@400;700;800&family=DM+Mono:wght@400;500&display=swap');
99
 
100
  :root {
101
- --bg: #0b0c10;
102
- --surface: #13151a;
103
- --border: #1e2230;
104
- --accent: #c8f135;
105
- --accent2: #35f1c8;
106
- --text: #e8eaf0;
107
- --muted: #6b7280;
108
- --radius: 10px;
 
 
 
109
  }
110
 
 
 
111
  body, .gradio-container {
112
  background: var(--bg) !important;
113
  font-family: 'Syne', sans-serif !important;
114
  color: var(--text) !important;
115
  }
116
 
117
- /* Header */
118
- .header-wrap {
119
  text-align: center;
120
- padding: 2.5rem 1rem 1.5rem;
121
  border-bottom: 1px solid var(--border);
122
- margin-bottom: 1.5rem;
 
123
  }
124
- .header-wrap h1 {
125
- font-size: 2.6rem;
126
  font-weight: 800;
127
- letter-spacing: -0.03em;
128
  background: linear-gradient(135deg, var(--accent) 0%, var(--accent2) 100%);
129
  -webkit-background-clip: text;
130
  -webkit-text-fill-color: transparent;
131
- margin: 0;
132
- }
133
- .header-wrap p {
134
- color: var(--muted);
135
- font-family: 'DM Mono', monospace;
136
- font-size: 0.85rem;
137
- margin-top: 0.5rem;
138
  }
 
139
 
140
- /* Tabs */
141
  .tab-nav button {
142
  background: transparent !important;
143
  color: var(--muted) !important;
144
  border: 1px solid transparent !important;
145
- border-radius: var(--radius) !important;
146
  font-family: 'Syne', sans-serif !important;
147
  font-weight: 700 !important;
148
- font-size: 0.9rem !important;
149
- padding: 0.5rem 1.2rem !important;
150
- transition: all 0.2s !important;
151
  }
152
  .tab-nav button.selected {
153
- background: var(--surface) !important;
154
  color: var(--accent) !important;
155
  border-color: var(--border) !important;
156
  }
157
- .tab-nav button:hover {
158
- color: var(--text) !important;
159
- }
160
 
161
- /* Inputs & Textareas */
162
- input[type="text"], textarea, .gr-textbox textarea {
163
  background: var(--surface) !important;
164
  border: 1px solid var(--border) !important;
165
- border-radius: var(--radius) !important;
166
  color: var(--text) !important;
167
  font-family: 'DM Mono', monospace !important;
168
- font-size: 0.88rem !important;
169
- padding: 0.65rem 0.9rem !important;
170
- transition: border-color 0.2s !important;
 
171
  }
172
- input[type="text"]:focus, textarea:focus {
173
  border-color: var(--accent) !important;
174
  outline: none !important;
175
- box-shadow: 0 0 0 2px rgba(200, 241, 53, 0.12) !important;
176
  }
177
 
178
- /* Labels */
179
- label span, .gr-form label {
180
- color: var(--text) !important;
181
- font-family: 'Syne', sans-serif !important;
182
- font-weight: 700 !important;
183
- font-size: 0.82rem !important;
184
- letter-spacing: 0.06em !important;
185
  text-transform: uppercase !important;
186
  }
187
 
188
- /* Buttons */
189
  button.primary {
190
  background: var(--accent) !important;
191
- color: #0b0c10 !important;
192
  border: none !important;
193
- border-radius: var(--radius) !important;
194
  font-family: 'Syne', sans-serif !important;
195
  font-weight: 800 !important;
196
- font-size: 0.9rem !important;
197
- letter-spacing: 0.05em !important;
198
- padding: 0.65rem 1.6rem !important;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
199
  cursor: pointer !important;
200
- transition: transform 0.15s, opacity 0.15s !important;
201
  }
202
- button.primary:hover { opacity: 0.88 !important; transform: translateY(-1px) !important; }
203
 
204
- /* Output box */
205
- .output-box textarea {
206
- background: #0f1117 !important;
207
  border: 1px solid var(--border) !important;
208
- border-left: 3px solid var(--accent2) !important;
 
209
  font-family: 'DM Mono', monospace !important;
210
- font-size: 0.86rem !important;
211
- line-height: 1.65 !important;
212
  color: var(--text) !important;
213
- border-radius: 0 var(--radius) var(--radius) 0 !important;
214
  }
215
 
216
- /* Technique cards */
217
- .technique-card {
218
  background: var(--surface);
219
  border: 1px solid var(--border);
220
- border-radius: var(--radius);
221
- padding: 1rem 1.2rem;
222
  margin-bottom: 1rem;
223
  }
224
- .technique-card .badge {
225
  display: inline-block;
226
  font-family: 'DM Mono', monospace;
227
- font-size: 0.72rem;
228
- font-weight: 500;
229
- letter-spacing: 0.08em;
230
  text-transform: uppercase;
231
- padding: 0.2rem 0.6rem;
232
  border-radius: 4px;
233
- background: rgba(200,241,53,0.12);
234
  color: var(--accent);
235
- margin-bottom: 0.4rem;
236
  }
237
- .technique-card p {
238
- color: var(--muted);
239
- font-size: 0.84rem;
240
- margin: 0;
241
- line-height: 1.55;
 
 
 
 
 
 
 
 
242
  }
243
 
244
- /* Slider */
245
- input[type="range"] { accent-color: var(--accent) !important; }
246
 
247
- /* Footer */
248
- .footer-note {
 
 
 
 
 
 
 
 
 
249
  text-align: center;
250
  padding: 1.5rem;
251
  color: var(--muted);
252
  font-family: 'DM Mono', monospace;
253
- font-size: 0.75rem;
254
  border-top: 1px solid var(--border);
255
  margin-top: 2rem;
256
  }
257
  """
258
 
259
  # ── UI ────────────────────────────────────────────────────────────────────────
 
260
  with gr.Blocks(css=CSS, title="Prompt Engineering Toolkit") as demo:
261
 
 
 
262
  gr.HTML("""
263
- <div class="header-wrap">
264
  <h1>⚡ Prompt Engineering Toolkit</h1>
265
- <p>4 battle-tested techniques for precision AI output</p>
266
  </div>
267
  """)
268
 
269
  with gr.Tabs():
270
 
271
- # ── Tab 1: Perspective-Shifting ───────────────────────────────────────
272
  with gr.Tab("01 · Perspective Shift"):
273
- gr.HTML("""
274
- <div class="technique-card">
275
- <div class="badge">Formula: Be X who does Y using Z metaphors</div>
276
- <p>Forces the AI to adopt a specific expert persona, shaping tone, depth, and vocabulary.
277
- The metaphor layer bridges complex domains into intuitive language.</p>
278
- </div>
279
- """)
280
  with gr.Row():
281
  ps_persona = gr.Textbox(label="Persona (X)", placeholder="e.g. neurosurgeon, jazz musician, Viking navigator")
282
- ps_task = gr.Textbox(label="Explain / Do (Y)", placeholder="e.g. quantum entanglement, blockchain consensus")
283
- ps_meta = gr.Textbox(label="Metaphor Domain (Z) — optional", placeholder="e.g. cooking, football, carpentry")
284
- ps_btn = gr.Button("Generate →", variant="primary")
285
- ps_out = gr.Textbox(label="Output", lines=14, elem_classes=["output-box"])
286
- ps_btn.click(run_perspective, inputs=[ps_persona, ps_task, ps_meta], outputs=ps_out)
 
 
 
 
 
 
 
287
 
288
- # ── Tab 2: Constrained Creativity ────────────────────────────────────
289
  with gr.Tab("02 · Constrained Creativity"):
290
- gr.HTML("""
291
- <div class="technique-card">
292
- <div class="badge">Concept: Limitations force innovation</div>
293
- <p>By restricting the solution space to specific ingredients, the AI must find
294
- non-obvious creative bridges — generating ideas that would never emerge from open-ended prompts.</p>
295
- </div>
296
- """)
297
- cc_items = gr.Textbox(
298
- label="Ingredients / Constraints (comma-separated)",
299
- placeholder="e.g. 3D printers, seaweed, TikTok",
300
- )
301
- cc_count = gr.Slider(label="Number of Ideas", minimum=2, maximum=8, value=5, step=1)
302
- cc_btn = gr.Button("Generate →", variant="primary")
303
- cc_out = gr.Textbox(label="Output", lines=18, elem_classes=["output-box"])
304
- cc_btn.click(run_constrained, inputs=[cc_items, cc_count], outputs=cc_out)
305
-
306
- # ── Tab 3: Meta-Prompts ───────────────────────────────────────────────
307
  with gr.Tab("03 · Meta-Prompts"):
308
- gr.HTML("""
309
- <div class="technique-card">
310
- <div class="badge">Concept: Make the AI critique its own output</div>
311
- <p>Multi-stage prompting forces the model to suggest, rank, then audit its own
312
- reasoning — surfacing blind spots and producing genuinely reliable recommendations.</p>
313
- </div>
314
- """)
315
- mp_goal = gr.Textbox(label="Goal / Challenge", placeholder="e.g. improve sleep quality, reduce team meeting time")
316
- mp_criteria = gr.Textbox(
317
- label="Ranking Criteria (one per line) — leave blank for defaults",
318
- placeholder="1. Scientific validity\n2. Cost-effectiveness\n3. Ease of implementation",
319
- lines=3,
320
- )
321
- mp_btn = gr.Button("Generate →", variant="primary")
322
- mp_out = gr.Textbox(label="Output", lines=18, elem_classes=["output-box"])
323
- mp_btn.click(run_meta, inputs=[mp_goal, mp_criteria], outputs=mp_out)
324
-
325
- # ── Tab 4: Chain-of-Thought ───────────────────────────────────────────
326
- with gr.Tab("04 · Chain of Thought"):
327
- gr.HTML("""
328
- <div class="technique-card">
329
- <div class="badge">Concept: Force systematic reasoning</div>
330
- <p>Breaks the problem into obligatory reasoning stages: decomposition → failure analysis →
331
- countermeasures → roadmap → side-effect prediction. Prevents shallow or hand-wavy answers.</p>
332
- </div>
333
- """)
334
- cot_problem = gr.Textbox(
335
- label="Complex Problem",
336
- placeholder="e.g. launching a product in a saturated market with zero ad budget",
337
- lines=3,
338
- )
339
- cot_domain = gr.Textbox(label="Domain Hint — optional", placeholder="e.g. B2B SaaS, healthcare, fintech")
340
- cot_btn = gr.Button("Generate →", variant="primary")
341
- cot_out = gr.Textbox(label="Output", lines=22, elem_classes=["output-box"])
342
- cot_btn.click(run_cot, inputs=[cot_problem, cot_domain], outputs=cot_out)
343
 
344
- gr.HTML("""
345
- <div class="footer-note">
346
- Powered by Claude · claude-sonnet-4-20250514 · Requires ANTHROPIC_API_KEY secret
347
- </div>
348
- """)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
349
 
350
  if __name__ == "__main__":
351
  demo.launch()
 
1
  import gradio as gr
2
+ import json
3
  import os
4
+ import time
5
+ from pathlib import Path
6
+ from huggingface_hub import InferenceClient
7
+
8
+ try:
9
+ from pypdf import PdfReader
10
+ except ImportError:
11
+ PdfReader = None
12
+
13
+ SAVE_DIR = Path("/tmp/saved_docs")
14
+ SAVE_DIR.mkdir(exist_ok=True)
15
+
16
+ MODEL = "meta-llama/Llama-3.3-70B-Instruct"
17
+
18
+ # ── Client ────────────────────────────────────────────────────────────────────
19
 
 
20
  def get_client():
21
+ token = os.environ.get("HF_TOKEN", "")
22
+ return InferenceClient(model=MODEL, token=token if token else None)
23
+
24
+
25
+ # ── PDF extraction ────────────────────────────────────────────────────────────
26
+
27
+ def extract_pdf_text(path: str) -> str:
28
+ if not path:
29
+ return ""
30
+ if PdfReader is None:
31
+ return "[pypdf not installed — PDF text unavailable]"
32
+ reader = PdfReader(path)
33
+ pages = []
34
+ for i, page in enumerate(reader.pages):
35
+ text = page.extract_text() or ""
36
+ if text.strip():
37
+ pages.append(f"--- Page {i+1} ---\n{text.strip()}")
38
+ full = "\n\n".join(pages)
39
+ # Truncate to ~12000 chars to stay within context limits
40
+ if len(full) > 12000:
41
+ full = full[:12000] + "\n\n[...document truncated for context limit...]"
42
+ return full
43
+
44
+
45
+ def inject_pdf(prompt: str, pdf_path: str | None) -> str:
46
+ if not pdf_path:
47
+ return prompt
48
+ text = extract_pdf_text(pdf_path)
49
+ if not text.strip():
50
+ return prompt
51
+ return f"Use the following document as your primary reference:\n\n{text}\n\n---\n\n{prompt}"
52
+
53
+
54
+ # ── Llama call ────────────────────────────────────────────────────────────────
55
+
56
+ def call_llama(prompt: str, pdf_path: str | None = None, max_tokens: int = 1200) -> str:
57
+ full_prompt = inject_pdf(prompt, pdf_path)
58
+ client = get_client()
59
+ response = client.chat_completion(
60
+ messages=[{"role": "user", "content": full_prompt}],
61
+ max_tokens=max_tokens,
62
+ temperature=0.7,
63
+ )
64
+ return response.choices[0].message.content
65
+
66
+
67
+ # ── Document library ──────────────────────────────────────────────────────────
68
+
69
+ def load_library() -> list:
70
+ p = SAVE_DIR / "index.json"
71
+ return json.loads(p.read_text()) if p.exists() else []
72
+
73
+
74
+ def save_to_library(title: str, technique: str, content: str) -> list:
75
+ if not title.strip():
76
+ title = f"{technique} — {time.strftime('%H:%M:%S')}"
77
+ library = load_library()
78
+ entry = {
79
+ "id": str(int(time.time() * 1000)),
80
+ "title": title.strip(),
81
+ "technique": technique,
82
+ "content": content,
83
+ "saved_at": time.strftime("%Y-%m-%d %H:%M"),
84
+ }
85
+ library.insert(0, entry)
86
+ (SAVE_DIR / "index.json").write_text(json.dumps(library, indent=2))
87
+ (SAVE_DIR / f"{entry['id']}.txt").write_text(
88
+ f"Title: {entry['title']}\nTechnique: {technique}\nSaved: {entry['saved_at']}\n\n{content}"
89
+ )
90
+ return library
91
+
92
+
93
+ def delete_from_library(doc_id: str) -> list:
94
+ library = [e for e in load_library() if e["id"] != doc_id]
95
+ (SAVE_DIR / "index.json").write_text(json.dumps(library, indent=2))
96
+ fp = SAVE_DIR / f"{doc_id}.txt"
97
+ if fp.exists():
98
+ fp.unlink()
99
+ return library
100
+
101
+
102
+ def lib_choices(library: list) -> list:
103
+ return [f"[{e['technique']}] {e['title']} ({e['saved_at']})" for e in library]
104
+
105
+
106
+ def choice_to_entry(library: list, choice: str) -> dict | None:
107
+ choices = lib_choices(library)
108
+ try:
109
+ return library[choices.index(choice)]
110
+ except (ValueError, IndexError):
111
+ return None
112
 
113
 
114
  # ── Technique runners ─────────────────────────────────────────────────────────
115
 
116
+ def run_perspective(persona, task, metaphor, pdf_path):
 
117
  if not persona.strip() or not task.strip():
118
+ return "⚠️ Please fill in both Persona and Task."
119
+ prompt = f"You are a {persona}. Explain {task}"
120
  if metaphor.strip():
121
+ prompt += f" using {metaphor} metaphors exclusively"
122
+ prompt += ". Be vivid, specific, and deeply expert in your framing."
123
+ return call_llama(prompt, pdf_path)
 
 
 
 
 
 
124
 
125
 
126
+ def run_constrained(ingredients, count, pdf_path):
 
127
  if not ingredients.strip():
128
+ return "⚠️ Please enter at least one ingredient."
129
  items = [i.strip() for i in ingredients.split(",") if i.strip()]
130
  prompt = (
131
+ f"Generate {int(count)} innovative startup ideas that MUST incorporate ALL of these elements: "
132
+ f"{', '.join(items)}.\n\n"
133
+ "Rules: Be wildly creative. Find non-obvious connections. "
134
+ "For each idea provide: (1) Company name, (2) One-line pitch, (3) Why this combination is uniquely powerful."
135
  )
136
+ return call_llama(prompt, pdf_path)
 
 
 
 
 
 
137
 
138
 
139
+ def run_meta(goal, criteria, pdf_path):
 
140
  if not goal.strip():
141
+ return "⚠️ Please enter a goal."
142
+ ranking = criteria.strip() if criteria.strip() else "1. Scientific validity\n2. Cost-effectiveness\n3. Ease of implementation"
143
  prompt = (
144
+ f"Step 1: Suggest 3 concrete ways to achieve this goal: {goal}\n\n"
145
+ f"Step 2: Rank them by these criteria:\n{ranking}\n\n"
146
+ "Step 3: Critically identify 2-3 flaws in your own ranking system above, "
147
+ "and explain specifically how each flaw could be mitigated."
 
 
 
 
 
 
148
  )
149
+ return call_llama(prompt, pdf_path, max_tokens=1400)
150
 
151
 
152
+ def run_cot(problem, domain, pdf_path):
 
153
  if not problem.strip():
154
+ return "⚠️ Please describe the problem."
155
+ domain_hint = f" in the {domain} domain" if domain.strip() else ""
156
  prompt = (
157
+ f"Solve this problem step by step{domain_hint}: {problem}\n\n"
158
  "Follow this exact structure:\n"
159
+ "1. COMPONENTS — Break down the key components of the problem\n"
160
+ "2. FAILURE POINTS — Identify 3 specific ways this could fail\n"
161
+ "3. COUNTERMEASURES — Propose a countermeasure for each failure point\n"
162
+ "4. ROADMAP — Create a phased implementation roadmap\n"
163
+ "5. SIDE EFFECTS — Predict 2 unexpected consequences (positive or negative) and how to handle them"
 
164
  )
165
+ return call_llama(prompt, pdf_path, max_tokens=1600)
166
+
 
 
 
 
 
167
 
168
+ def run_pdf_qa(question, pdf_path):
169
+ if not question.strip():
170
+ return "⚠��� Please enter a question."
171
+ if not pdf_path:
172
+ return "⚠️ Please upload a PDF first."
173
+ prompt = (
174
+ f"{question}\n\n"
175
+ "Answer thoroughly. Cite specific sections or page numbers from the document where relevant. "
176
+ "If the answer is not in the document, say so clearly."
177
+ )
178
+ return call_llama(prompt, pdf_path, max_tokens=1600)
179
+
180
+
181
+ # ── Save handlers (one per tab — no lambdas to avoid closure bugs) ────────────
182
+
183
+ def save_ps(title, content, lib):
184
+ if not content or not content.strip():
185
+ return lib, "⚠️ Nothing to save yet."
186
+ lib = save_to_library(title, "Perspective Shift", content)
187
+ return lib, "✅ Saved to library."
188
+
189
+ def save_cc(title, content, lib):
190
+ if not content or not content.strip():
191
+ return lib, "⚠️ Nothing to save yet."
192
+ lib = save_to_library(title, "Constrained Creativity", content)
193
+ return lib, "✅ Saved to library."
194
+
195
+ def save_mp(title, content, lib):
196
+ if not content or not content.strip():
197
+ return lib, "⚠️ Nothing to save yet."
198
+ lib = save_to_library(title, "Meta-Prompt", content)
199
+ return lib, "✅ Saved to library."
200
+
201
+ def save_cot(title, content, lib):
202
+ if not content or not content.strip():
203
+ return lib, "⚠️ Nothing to save yet."
204
+ lib = save_to_library(title, "Chain of Thought", content)
205
+ return lib, "✅ Saved to library."
206
+
207
+ def save_qa(title, content, lib):
208
+ if not content or not content.strip():
209
+ return lib, "⚠️ Nothing to save yet."
210
+ lib = save_to_library(title, "PDF Q&A", content)
211
+ return lib, "✅ Saved to library."
212
+
213
+
214
+ # ── Library handlers ──────────────────────────────────────────────────────────
215
+
216
+ def lib_refresh(lib):
217
+ return gr.update(choices=lib_choices(lib), value=None), ""
218
+
219
+ def lib_view(sel, lib):
220
+ if not sel:
221
+ return ""
222
+ e = choice_to_entry(lib, sel)
223
+ if not e:
224
+ return "Document not found."
225
+ return f"TITLE: {e['title']}\nTECHNIQUE: {e['technique']}\nSAVED: {e['saved_at']}\n\n{'='*60}\n\n{e['content']}"
226
+
227
+ def lib_download(sel, lib):
228
+ if not sel:
229
+ return gr.update(visible=False), "⚠️ Select a document first."
230
+ e = choice_to_entry(lib, sel)
231
+ if not e:
232
+ return gr.update(visible=False), "Not found."
233
+ fp = SAVE_DIR / f"{e['id']}.txt"
234
+ if fp.exists():
235
+ return gr.update(value=str(fp), visible=True), "✅ Ready to download."
236
+ return gr.update(visible=False), "File missing."
237
+
238
+ def lib_delete(sel, lib):
239
+ if not sel:
240
+ return lib, gr.update(choices=lib_choices(lib), value=None), "⚠️ Select a document."
241
+ e = choice_to_entry(lib, sel)
242
+ if not e:
243
+ return lib, gr.update(choices=lib_choices(lib), value=None), "Not found."
244
+ lib = delete_from_library(e["id"])
245
+ return lib, gr.update(choices=lib_choices(lib), value=None), "🗑️ Deleted."
246
+
247
+ def lib_sync(lib):
248
+ return gr.update(choices=lib_choices(lib))
249
+
250
+
251
+ # ── CSS ───────────────────────────────────────────────────────────────────────
252
 
 
253
  CSS = """
254
+ @import url('https://fonts.googleapis.com/css2?family=Syne:wght@400;700;800&family=DM+Mono:ital,wght@0,400;0,500;1,400&display=swap');
255
 
256
  :root {
257
+ --bg: #0d0f14;
258
+ --surface: #151820;
259
+ --surface2: #1c1f2b;
260
+ --border: #252836;
261
+ --accent: #7DF9AA;
262
+ --accent2: #7DB3F9;
263
+ --accent3: #F9C97D;
264
+ --red: #F97D7D;
265
+ --text: #E2E6F0;
266
+ --muted: #5A6070;
267
+ --r: 8px;
268
  }
269
 
270
+ *, *::before, *::after { box-sizing: border-box; }
271
+
272
  body, .gradio-container {
273
  background: var(--bg) !important;
274
  font-family: 'Syne', sans-serif !important;
275
  color: var(--text) !important;
276
  }
277
 
278
+ /* ── Header ── */
279
+ .hdr {
280
  text-align: center;
281
+ padding: 2.8rem 1rem 1.8rem;
282
  border-bottom: 1px solid var(--border);
283
+ margin-bottom: 1.8rem;
284
+ background: radial-gradient(ellipse at 50% 0%, rgba(125,249,170,0.06) 0%, transparent 70%);
285
  }
286
+ .hdr h1 {
287
+ font-size: 2.4rem;
288
  font-weight: 800;
289
+ letter-spacing: -0.04em;
290
  background: linear-gradient(135deg, var(--accent) 0%, var(--accent2) 100%);
291
  -webkit-background-clip: text;
292
  -webkit-text-fill-color: transparent;
293
+ margin: 0 0 0.4rem;
 
 
 
 
 
 
294
  }
295
+ .hdr p { color: var(--muted); font-family: 'DM Mono', monospace; font-size: 0.82rem; margin: 0; }
296
 
297
+ /* ── Tabs ── */
298
  .tab-nav button {
299
  background: transparent !important;
300
  color: var(--muted) !important;
301
  border: 1px solid transparent !important;
302
+ border-radius: var(--r) !important;
303
  font-family: 'Syne', sans-serif !important;
304
  font-weight: 700 !important;
305
+ font-size: 0.82rem !important;
306
+ padding: 0.45rem 0.9rem !important;
307
+ transition: all 0.18s !important;
308
  }
309
  .tab-nav button.selected {
310
+ background: var(--surface2) !important;
311
  color: var(--accent) !important;
312
  border-color: var(--border) !important;
313
  }
314
+ .tab-nav button:hover { color: var(--text) !important; }
 
 
315
 
316
+ /* ── Inputs ── */
317
+ input[type=text], textarea {
318
  background: var(--surface) !important;
319
  border: 1px solid var(--border) !important;
320
+ border-radius: var(--r) !important;
321
  color: var(--text) !important;
322
  font-family: 'DM Mono', monospace !important;
323
+ font-size: 0.86rem !important;
324
+ padding: 0.6rem 0.85rem !important;
325
+ transition: border-color 0.18s, box-shadow 0.18s !important;
326
+ resize: vertical !important;
327
  }
328
+ input[type=text]:focus, textarea:focus {
329
  border-color: var(--accent) !important;
330
  outline: none !important;
331
+ box-shadow: 0 0 0 3px rgba(125,249,170,0.1) !important;
332
  }
333
 
334
+ /* ── Labels ── */
335
+ label > span:first-child {
336
+ color: var(--muted) !important;
337
+ font-family: 'DM Mono', monospace !important;
338
+ font-weight: 500 !important;
339
+ font-size: 0.75rem !important;
340
+ letter-spacing: 0.08em !important;
341
  text-transform: uppercase !important;
342
  }
343
 
344
+ /* ── Buttons ── */
345
  button.primary {
346
  background: var(--accent) !important;
347
+ color: #0d0f14 !important;
348
  border: none !important;
349
+ border-radius: var(--r) !important;
350
  font-family: 'Syne', sans-serif !important;
351
  font-weight: 800 !important;
352
+ font-size: 0.88rem !important;
353
+ letter-spacing: 0.04em !important;
354
+ padding: 0.6rem 1.5rem !important;
355
+ cursor: pointer !important;
356
+ transition: opacity 0.15s, transform 0.15s !important;
357
+ }
358
+ button.primary:hover { opacity: 0.85 !important; transform: translateY(-1px) !important; }
359
+
360
+ button.secondary {
361
+ background: var(--surface2) !important;
362
+ color: var(--accent2) !important;
363
+ border: 1px solid var(--border) !important;
364
+ border-radius: var(--r) !important;
365
+ font-family: 'Syne', sans-serif !important;
366
+ font-weight: 700 !important;
367
+ font-size: 0.8rem !important;
368
+ padding: 0.5rem 1rem !important;
369
  cursor: pointer !important;
370
+ transition: border-color 0.15s !important;
371
  }
372
+ button.secondary:hover { border-color: var(--accent2) !important; }
373
 
374
+ /* ── Output box ── */
375
+ .out textarea {
376
+ background: #0a0c10 !important;
377
  border: 1px solid var(--border) !important;
378
+ border-left: 3px solid var(--accent) !important;
379
+ border-radius: 0 var(--r) var(--r) 0 !important;
380
  font-family: 'DM Mono', monospace !important;
381
+ font-size: 0.85rem !important;
382
+ line-height: 1.7 !important;
383
  color: var(--text) !important;
 
384
  }
385
 
386
+ /* ── Cards ── */
387
+ .card {
388
  background: var(--surface);
389
  border: 1px solid var(--border);
390
+ border-radius: var(--r);
391
+ padding: 0.85rem 1rem;
392
  margin-bottom: 1rem;
393
  }
394
+ .badge {
395
  display: inline-block;
396
  font-family: 'DM Mono', monospace;
397
+ font-size: 0.68rem;
398
+ letter-spacing: 0.09em;
 
399
  text-transform: uppercase;
400
+ padding: 0.18rem 0.55rem;
401
  border-radius: 4px;
402
+ background: rgba(125,249,170,0.1);
403
  color: var(--accent);
404
+ margin-bottom: 0.3rem;
405
  }
406
+ .card p { color: var(--muted); font-size: 0.82rem; margin: 0; line-height: 1.55; }
407
+
408
+ .pdf-note {
409
+ display: inline-block;
410
+ font-family: 'DM Mono', monospace;
411
+ font-size: 0.68rem;
412
+ letter-spacing: 0.08em;
413
+ text-transform: uppercase;
414
+ padding: 0.18rem 0.55rem;
415
+ border-radius: 4px;
416
+ background: rgba(249,201,125,0.1);
417
+ color: var(--accent3);
418
+ margin: 0.6rem 0 0.3rem;
419
  }
420
 
421
+ /* ── Slider ── */
422
+ input[type=range] { accent-color: var(--accent) !important; }
423
 
424
+ /* ── File upload ── */
425
+ .file-upload {
426
+ background: var(--surface) !important;
427
+ border: 1.5px dashed var(--border) !important;
428
+ border-radius: var(--r) !important;
429
+ transition: border-color 0.18s !important;
430
+ }
431
+ .file-upload:hover { border-color: var(--accent3) !important; }
432
+
433
+ /* ── Footer ── */
434
+ .foot {
435
  text-align: center;
436
  padding: 1.5rem;
437
  color: var(--muted);
438
  font-family: 'DM Mono', monospace;
439
+ font-size: 0.7rem;
440
  border-top: 1px solid var(--border);
441
  margin-top: 2rem;
442
  }
443
  """
444
 
445
  # ── UI ────────────────────────────────────────────────────────────────────────
446
+
447
  with gr.Blocks(css=CSS, title="Prompt Engineering Toolkit") as demo:
448
 
449
+ lib_state = gr.State(load_library())
450
+
451
  gr.HTML("""
452
+ <div class="hdr">
453
  <h1>⚡ Prompt Engineering Toolkit</h1>
454
+ <p>llama-3.3-70b-instruct &nbsp;·&nbsp; 4 techniques &nbsp;·&nbsp; PDF intake &nbsp;·&nbsp; document library</p>
455
  </div>
456
  """)
457
 
458
  with gr.Tabs():
459
 
460
+ # ── 01 Perspective Shift ──────────────────────────────────────────────
461
  with gr.Tab("01 · Perspective Shift"):
462
+ gr.HTML('<div class="card"><div class="badge">Be X · explain Y · using Z metaphors</div><p>Forces the model into an expert persona. Add a PDF to ground the response in your document.</p></div>')
 
 
 
 
 
 
463
  with gr.Row():
464
  ps_persona = gr.Textbox(label="Persona (X)", placeholder="e.g. neurosurgeon, jazz musician, Viking navigator")
465
+ ps_task = gr.Textbox(label="Explain / Do (Y)", placeholder="e.g. quantum entanglement, compound interest")
466
+ ps_meta = gr.Textbox(label="Metaphor Domain (Z) — optional", placeholder="e.g. cooking, football, carpentry")
467
+ gr.HTML('<div class="pdf-note">📄 optional pdf context</div>')
468
+ ps_pdf = gr.File(label="Upload PDF", file_types=[".pdf"], type="filepath", elem_classes=["file-upload"])
469
+ ps_btn = gr.Button("Generate →", variant="primary")
470
+ ps_out = gr.Textbox(label="Output", lines=14, elem_classes=["out"])
471
+ with gr.Row():
472
+ ps_title = gr.Textbox(label="Save as (title) — blank = auto", scale=4)
473
+ ps_save = gr.Button("💾 Save", variant="secondary", scale=1)
474
+ ps_msg = gr.Markdown("")
475
+ ps_btn.click(run_perspective, inputs=[ps_persona, ps_task, ps_meta, ps_pdf], outputs=ps_out)
476
+ ps_save.click(save_ps, inputs=[ps_title, ps_out, lib_state], outputs=[lib_state, ps_msg])
477
 
478
+ # ── 02 Constrained Creativity ─────────────────────────────────────────
479
  with gr.Tab("02 · Constrained Creativity"):
480
+ gr.HTML('<div class="card"><div class="badge">Limitations force innovation</div><p>Locks the model to specific ingredients — forcing non-obvious creative bridges between concepts.</p></div>')
481
+ cc_items = gr.Textbox(label="Ingredients (comma-separated)", placeholder="e.g. 3D printers, seaweed, TikTok")
482
+ cc_count = gr.Slider(label="Number of ideas", minimum=2, maximum=8, value=5, step=1)
483
+ gr.HTML('<div class="pdf-note">📄 optional pdf context</div>')
484
+ cc_pdf = gr.File(label="Upload PDF", file_types=[".pdf"], type="filepath", elem_classes=["file-upload"])
485
+ cc_btn = gr.Button("Generate →", variant="primary")
486
+ cc_out = gr.Textbox(label="Output", lines=16, elem_classes=["out"])
487
+ with gr.Row():
488
+ cc_title = gr.Textbox(label="Save as (title) — blank = auto", scale=4)
489
+ cc_save = gr.Button("💾 Save", variant="secondary", scale=1)
490
+ cc_msg = gr.Markdown("")
491
+ cc_btn.click(run_constrained, inputs=[cc_items, cc_count, cc_pdf], outputs=cc_out)
492
+ cc_save.click(save_cc, inputs=[cc_title, cc_out, lib_state], outputs=[lib_state, cc_msg])
493
+
494
+ # ── 03 Meta-Prompts ───────────────────────────────────────────────────
 
 
495
  with gr.Tab("03 · Meta-Prompts"):
496
+ gr.HTML('<div class="card"><div class="badge">Make the model critique its own output</div><p>Three-stage: suggest → rank → audit own reasoning. Surfaces blind spots automatically.</p></div>')
497
+ mp_goal = gr.Textbox(label="Goal / Challenge", placeholder="e.g. improve sleep quality, reduce team meeting time")
498
+ mp_crit = gr.Textbox(label="Ranking criteria (one per line) — blank = defaults", lines=3,
499
+ placeholder="1. Scientific validity\n2. Cost-effectiveness\n3. Ease of implementation")
500
+ gr.HTML('<div class="pdf-note">📄 optional pdf context</div>')
501
+ mp_pdf = gr.File(label="Upload PDF", file_types=[".pdf"], type="filepath", elem_classes=["file-upload"])
502
+ mp_btn = gr.Button("Generate →", variant="primary")
503
+ mp_out = gr.Textbox(label="Output", lines=16, elem_classes=["out"])
504
+ with gr.Row():
505
+ mp_title = gr.Textbox(label="Save as (title) — blank = auto", scale=4)
506
+ mp_save = gr.Button("💾 Save", variant="secondary", scale=1)
507
+ mp_msg = gr.Markdown("")
508
+ mp_btn.click(run_meta, inputs=[mp_goal, mp_crit, mp_pdf], outputs=mp_out)
509
+ mp_save.click(save_mp, inputs=[mp_title, mp_out, lib_state], outputs=[lib_state, mp_msg])
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
510
 
511
+ # ── 04 Chain of Thought ───────────────────────────────────────────────
512
+ with gr.Tab("04 · Chain of Thought"):
513
+ gr.HTML('<div class="card"><div class="badge">Force systematic reasoning</div><p>Decompose → failure analysis → countermeasures → roadmap → side-effect prediction. No hand-waving.</p></div>')
514
+ cot_prob = gr.Textbox(label="Complex Problem", lines=3, placeholder="e.g. launching a product in a saturated market with zero ad budget")
515
+ cot_dom = gr.Textbox(label="Domain hint — optional", placeholder="e.g. B2B SaaS, healthcare, fintech")
516
+ gr.HTML('<div class="pdf-note">📄 optional pdf context</div>')
517
+ cot_pdf = gr.File(label="Upload PDF", file_types=[".pdf"], type="filepath", elem_classes=["file-upload"])
518
+ cot_btn = gr.Button("Generate →", variant="primary")
519
+ cot_out = gr.Textbox(label="Output", lines=20, elem_classes=["out"])
520
+ with gr.Row():
521
+ cot_title = gr.Textbox(label="Save as (title) — blank = auto", scale=4)
522
+ cot_save = gr.Button("💾 Save", variant="secondary", scale=1)
523
+ cot_msg = gr.Markdown("")
524
+ cot_btn.click(run_cot, inputs=[cot_prob, cot_dom, cot_pdf], outputs=cot_out)
525
+ cot_save.click(save_cot, inputs=[cot_title, cot_out, lib_state], outputs=[lib_state, cot_msg])
526
+
527
+ # ── 05 PDF Q&A ────────────────────────────────────────────────────────
528
+ with gr.Tab("05 · PDF Q&A"):
529
+ gr.HTML('<div class="card"><div class="badge">Direct document interrogation</div><p>Upload any PDF — contract, paper, report, case file. Ask anything; the model cites specific sections.</p></div>')
530
+ qa_pdf = gr.File(label="Upload PDF", file_types=[".pdf"], type="filepath", elem_classes=["file-upload"])
531
+ qa_q = gr.Textbox(label="Question", lines=3,
532
+ placeholder="e.g. What are the key obligations in section 4?\nSummarise the main argument.\nWhat risks does the author identify?")
533
+ qa_btn = gr.Button("Ask →", variant="primary")
534
+ qa_out = gr.Textbox(label="Answer", lines=20, elem_classes=["out"])
535
+ with gr.Row():
536
+ qa_title = gr.Textbox(label="Save as (title) — blank = auto", scale=4)
537
+ qa_save = gr.Button("💾 Save", variant="secondary", scale=1)
538
+ qa_msg = gr.Markdown("")
539
+ qa_btn.click(run_pdf_qa, inputs=[qa_q, qa_pdf], outputs=qa_out)
540
+ qa_save.click(save_qa, inputs=[qa_title, qa_out, lib_state], outputs=[lib_state, qa_msg])
541
+
542
+ # ── 06 Library ────────────────────────────────────────────────────────
543
+ with gr.Tab("📚 Library"):
544
+ gr.HTML('<div class="card"><div class="badge">Session document library</div><p>Every output you\'ve saved this session. View, download as .txt, or delete.</p></div>')
545
+
546
+ lib_refresh_btn = gr.Button("���� Refresh list", variant="secondary")
547
+ lib_dd = gr.Dropdown(label="Saved documents", choices=lib_choices(load_library()), value=None, interactive=True)
548
+ lib_viewer = gr.Textbox(label="Content", lines=20, elem_classes=["out"], interactive=False)
549
+ with gr.Row():
550
+ lib_dl_btn = gr.Button("⬇️ Download .txt", variant="secondary")
551
+ lib_del_btn = gr.Button("🗑️ Delete", variant="secondary")
552
+ lib_dl_file = gr.File(label="Your file", visible=False)
553
+ lib_msg = gr.Markdown("")
554
+
555
+ lib_refresh_btn.click(lib_refresh, inputs=[lib_state], outputs=[lib_dd, lib_msg])
556
+ lib_dd.change(lib_view, inputs=[lib_dd, lib_state], outputs=lib_viewer)
557
+ lib_dl_btn.click(lib_download, inputs=[lib_dd, lib_state], outputs=[lib_dl_file, lib_msg])
558
+ lib_del_btn.click(lib_delete, inputs=[lib_dd, lib_state], outputs=[lib_state, lib_dd, lib_msg])
559
+ lib_state.change(lib_sync, inputs=[lib_state], outputs=lib_dd)
560
+
561
+ gr.HTML('<div class="foot">Llama-3.3-70B-Instruct via HF Inference API &nbsp;·&nbsp; Set HF_TOKEN secret for private/gated models &nbsp;·&nbsp; PDFs extracted with pypdf</div>')
562
 
563
  if __name__ == "__main__":
564
  demo.launch()