Curious-PM commited on
Commit
eef18b5
Β·
verified Β·
1 Parent(s): 8b4cfbd

Upload folder using huggingface_hub

Browse files
Files changed (1) hide show
  1. app.py +290 -31
app.py CHANGED
@@ -52,56 +52,315 @@ def generate_reply(question):
52
  return reply
53
 
54
 
55
- CSS = """
56
- .gradio-container { max-width: 980px !important; }
57
- #title { text-align: center; margin-bottom: 4px; font-weight: 800; letter-spacing: -1px; }
58
- #sub { text-align: center; color: #6B6B6B; margin-bottom: 24px; font-size: 14.5px; }
59
- .output-box textarea, .output-box .markdown {
60
- font-size: 14.5px !important; line-height: 1.6 !important;
61
- }
62
- """
63
-
64
  EXAMPLES = [
65
  "Our SaaS vendor wants us to sign: 'Customer grants Vendor a perpetual, irrevocable license to use Customer Data for any purpose, including ML training.' Is this normal?",
66
  "Their non-compete is 2 years, all of California. Is that enforceable on a new hire?",
67
  "Our enterprise customer wants source code escrow with release on bankruptcy, material breach, or product discontinuation. Push back?",
68
  "We're hiring our first UK employee. Should we use an Employer of Record service or set up a UK subsidiary?",
69
- "A vendor's MSA caps liability at $1M for any claim. Our annual fees are $500K and they hold our customer database. Is the cap reasonable?",
 
70
  ]
71
 
72
- with gr.Blocks(title="Junior Associate Β· Contract Review (IRAC)", css=CSS, theme=gr.themes.Soft()) as demo:
73
- gr.HTML('<h1 id="title">Junior Associate</h1>')
74
- gr.HTML(
75
- '<div id="sub">Qwen 2.5-3B fine-tuned on 80 hand-crafted contract-review memos. '
76
- 'Every reply: top disclaimer, IRAC analysis, numbered redlines, bottom disclaimer, sign-off. '
77
- 'Built with the <a href="https://huggingface.co/blog/hf-skills-training" target="_blank">'
78
- '<code>hf-llm-trainer</code></a> Claude skill on Hugging Face Jobs.</div>'
79
- )
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
80
 
81
  question = gr.Textbox(
82
- label="Paste a contract clause or ask a question",
83
  placeholder="e.g. Their MSA caps liability at $1M. Is that reasonable?",
84
  lines=3,
 
 
85
  )
86
- submit = gr.Button("Ask the Junior Associate β–Έ", variant="primary", size="lg")
 
 
 
 
 
 
 
 
 
 
 
87
 
88
  output = gr.Markdown(
89
- value="_The reply will appear here. First request takes ~10s as the GPU warms up._",
90
- elem_classes="output-box",
91
  )
92
 
 
 
 
93
  submit.click(fn=generate_reply, inputs=question, outputs=output)
94
  question.submit(fn=generate_reply, inputs=question, outputs=output)
95
 
96
- gr.Examples(examples=EXAMPLES, inputs=question, label="Try one of these")
97
-
98
- gr.HTML(
99
- '<div style="text-align: center; color: #888; font-size: 12px; margin-top: 32px;">'
100
- 'Model: <a href="https://huggingface.co/Curious-PM/lexwell-contract-irac-qwen2.5-3b-lora" target="_blank">'
101
- 'Curious-PM/lexwell-contract-irac-qwen2.5-3b-lora</a>'
102
- ' Β· Built for <a href="https://curious.pm" target="_blank">Curious PM</a> &middot; Stay Curious session on fine-tuning'
103
- '</div>'
104
- )
105
 
106
 
107
  if __name__ == "__main__":
 
52
  return reply
53
 
54
 
 
 
 
 
 
 
 
 
 
55
  EXAMPLES = [
56
  "Our SaaS vendor wants us to sign: 'Customer grants Vendor a perpetual, irrevocable license to use Customer Data for any purpose, including ML training.' Is this normal?",
57
  "Their non-compete is 2 years, all of California. Is that enforceable on a new hire?",
58
  "Our enterprise customer wants source code escrow with release on bankruptcy, material breach, or product discontinuation. Push back?",
59
  "We're hiring our first UK employee. Should we use an Employer of Record service or set up a UK subsidiary?",
60
+ "A vendor's MSA caps liability at $1M. Our annual fees are $500K and they hold our customer database. Reasonable?",
61
+ "Standard force majeure language β€” anything to flag?",
62
  ]
63
 
64
+ EXAMPLE_LABELS = [
65
+ "Vendor wants perpetual ML training rights",
66
+ "California non-compete (2 years)",
67
+ "Source code escrow on bankruptcy",
68
+ "EOR vs UK subsidiary for first hire",
69
+ "MSA liability cap at $1M",
70
+ "Standard force majeure clause",
71
+ ]
72
+
73
+
74
+ CSS = """
75
+ @import url('https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700;800;900&family=JetBrains+Mono:wght@400;600&display=swap');
76
+
77
+ :root {
78
+ --ink: #0A0A0A;
79
+ --paper: #FFFFFF;
80
+ --off: #FAFAFA;
81
+ --tint: #F4F4F5;
82
+ --hair: #E4E4E4;
83
+ --rule: #C8C8C8;
84
+ --muted: #6B6B6B;
85
+ --subtle: #A1A1AA;
86
+ }
87
+
88
+ * { box-sizing: border-box; }
89
+
90
+ body, .gradio-container {
91
+ background: var(--off) !important;
92
+ font-family: 'Inter', -apple-system, BlinkMacSystemFont, sans-serif !important;
93
+ color: var(--ink) !important;
94
+ }
95
+
96
+ .gradio-container {
97
+ max-width: 920px !important;
98
+ margin: 0 auto !important;
99
+ padding: 56px 32px 80px !important;
100
+ }
101
+
102
+ /* Hide Gradio chrome */
103
+ footer { display: none !important; }
104
+ .show-api, .built-with { display: none !important; }
105
+ button.lg.secondary[aria-label*="Use via API"] { display: none !important; }
106
+
107
+ /* ── HEADER ───────────────────────────────────────────── */
108
+ #header-block {
109
+ text-align: center;
110
+ margin-bottom: 48px;
111
+ }
112
+ #header-block .title-row {
113
+ font-family: 'Inter', sans-serif;
114
+ font-size: 11px;
115
+ font-weight: 700;
116
+ letter-spacing: 2.4px;
117
+ color: var(--muted);
118
+ text-transform: uppercase;
119
+ margin-bottom: 12px;
120
+ }
121
+ #header-block h1 {
122
+ font-family: 'Inter', sans-serif !important;
123
+ font-size: 64px !important;
124
+ font-weight: 900 !important;
125
+ letter-spacing: -2.4px !important;
126
+ margin: 0 0 16px 0 !important;
127
+ line-height: 0.98 !important;
128
+ color: var(--ink) !important;
129
+ }
130
+ #header-block .subtitle {
131
+ font-size: 16px;
132
+ color: var(--muted);
133
+ max-width: 560px;
134
+ margin: 0 auto;
135
+ line-height: 1.6;
136
+ font-weight: 500;
137
+ }
138
+ #header-block .meta-pill {
139
+ display: inline-flex;
140
+ align-items: center;
141
+ gap: 10px;
142
+ margin-top: 22px;
143
+ padding: 8px 16px;
144
+ background: var(--tint);
145
+ border-radius: 999px;
146
+ font-family: 'JetBrains Mono', ui-monospace, monospace;
147
+ font-size: 11.5px;
148
+ color: var(--ink);
149
+ }
150
+ #header-block .meta-pill .label {
151
+ font-family: 'Inter', sans-serif;
152
+ font-size: 10px;
153
+ font-weight: 700;
154
+ letter-spacing: 1.6px;
155
+ color: var(--muted);
156
+ }
157
+ #header-block .meta-pill a {
158
+ color: var(--ink);
159
+ text-decoration: underline;
160
+ text-underline-offset: 2px;
161
+ }
162
+
163
+ /* ── SECTION LABELS ────────────────────────────────────── */
164
+ .section-label {
165
+ font-family: 'Inter', sans-serif;
166
+ font-size: 11px;
167
+ font-weight: 700;
168
+ letter-spacing: 2px;
169
+ color: var(--muted);
170
+ text-transform: uppercase;
171
+ margin: 32px 0 12px;
172
+ }
173
+
174
+ /* ── INPUT ─────────────────────────────────────────────── */
175
+ .gr-textbox, textarea {
176
+ border-radius: 10px !important;
177
+ border: 1px solid var(--hair) !important;
178
+ background: var(--paper) !important;
179
+ padding: 16px 18px !important;
180
+ font-family: 'Inter', sans-serif !important;
181
+ font-size: 15px !important;
182
+ line-height: 1.5 !important;
183
+ color: var(--ink) !important;
184
+ box-shadow: none !important;
185
+ }
186
+ textarea:focus {
187
+ border-color: var(--ink) !important;
188
+ outline: none !important;
189
+ }
190
+ .gr-textbox label, label[for] {
191
+ display: none !important;
192
+ }
193
+
194
+ /* ── BUTTONS ────────────────────────────────────────────── */
195
+ button.primary, .primary-button button {
196
+ background: var(--ink) !important;
197
+ color: var(--paper) !important;
198
+ border: none !important;
199
+ border-radius: 10px !important;
200
+ font-family: 'Inter', sans-serif !important;
201
+ font-weight: 700 !important;
202
+ font-size: 15px !important;
203
+ letter-spacing: 0.2px !important;
204
+ padding: 14px 28px !important;
205
+ cursor: pointer !important;
206
+ width: 100% !important;
207
+ transition: background 0.15s ease !important;
208
+ margin-top: 16px !important;
209
+ }
210
+ button.primary:hover, .primary-button button:hover {
211
+ background: #2A2A2A !important;
212
+ }
213
+
214
+ /* Example buttons β€” small cards */
215
+ .examples-row {
216
+ display: grid !important;
217
+ grid-template-columns: 1fr 1fr !important;
218
+ gap: 10px !important;
219
+ margin-top: 8px !important;
220
+ }
221
+ .example-btn button {
222
+ background: var(--paper) !important;
223
+ border: 1px solid var(--hair) !important;
224
+ border-radius: 8px !important;
225
+ padding: 12px 16px !important;
226
+ font-family: 'Inter', sans-serif !important;
227
+ font-size: 13px !important;
228
+ font-weight: 500 !important;
229
+ color: var(--ink) !important;
230
+ text-align: left !important;
231
+ cursor: pointer !important;
232
+ width: 100% !important;
233
+ height: auto !important;
234
+ min-height: 48px !important;
235
+ line-height: 1.45 !important;
236
+ white-space: normal !important;
237
+ transition: all 0.15s ease !important;
238
+ }
239
+ .example-btn button:hover {
240
+ background: var(--tint) !important;
241
+ border-color: var(--rule) !important;
242
+ }
243
+
244
+ /* ── OUTPUT MEMO ────────────────────────────────────────── */
245
+ #output-block {
246
+ background: var(--paper);
247
+ border: 1px solid var(--hair);
248
+ border-left: 3px solid var(--ink);
249
+ border-radius: 8px;
250
+ padding: 28px 32px;
251
+ margin-top: 12px;
252
+ min-height: 100px;
253
+ }
254
+ #output-block .prose, #output-block * {
255
+ font-family: 'Inter', sans-serif !important;
256
+ font-size: 14.5px !important;
257
+ line-height: 1.7 !important;
258
+ color: var(--ink) !important;
259
+ }
260
+ #output-block strong {
261
+ font-weight: 700 !important;
262
+ color: var(--ink) !important;
263
+ }
264
+ #output-block em {
265
+ color: var(--muted) !important;
266
+ font-style: italic !important;
267
+ }
268
+ #output-block p { margin: 0 0 12px 0 !important; }
269
+ #output-block ol, #output-block ul {
270
+ padding-left: 22px !important;
271
+ margin: 8px 0 12px 0 !important;
272
+ }
273
+ #output-block li { margin-bottom: 6px !important; }
274
+
275
+ /* ── FOOTER ────────────────────────────────────────────── */
276
+ #footer-block {
277
+ text-align: center;
278
+ margin-top: 48px;
279
+ padding-top: 24px;
280
+ border-top: 1px solid var(--hair);
281
+ font-size: 12px;
282
+ color: var(--muted);
283
+ line-height: 1.6;
284
+ }
285
+ #footer-block a {
286
+ color: var(--ink);
287
+ text-decoration: underline;
288
+ text-underline-offset: 2px;
289
+ }
290
+ """
291
+
292
+
293
+ HEADER_HTML = """
294
+ <div id="header-block">
295
+ <div class="title-row">Demo 03 Β· Stay Curious Β· Fine-Tuning LLMs</div>
296
+ <h1>Junior Associate</h1>
297
+ <div class="subtitle">
298
+ Qwen 2.5-3B fine-tuned on 80 hand-crafted contract-review memos.
299
+ Every reply ships with the firm's IRAC structure and disclaimers β€” automatically.
300
+ </div>
301
+ <div class="meta-pill">
302
+ <span class="label">MODEL</span>
303
+ <a href="https://huggingface.co/Curious-PM/lexwell-contract-irac-qwen2.5-3b-lora" target="_blank">Curious-PM/lexwell-contract-irac-qwen2.5-3b-lora</a>
304
+ <span style="color: var(--rule);">Β·</span>
305
+ <span style="color: var(--muted);">trained via</span>
306
+ <a href="https://huggingface.co/blog/hf-skills-training" target="_blank">hf-llm-trainer</a>
307
+ </div>
308
+ </div>
309
+ """
310
+
311
+ FOOTER_HTML = """
312
+ <div id="footer-block">
313
+ Built for <a href="https://curious.pm" target="_blank">Curious PM</a> &middot; Stay Curious session on fine-tuning &middot; <a href="https://huggingface.co/Curious-PM/lexwell-contract-irac-qwen2.5-3b-lora" target="_blank">View the model</a>
314
+ </div>
315
+ """
316
+
317
+ INITIAL_OUTPUT = (
318
+ "_The reply will appear here. First request takes ~10 seconds while the GPU warms up; "
319
+ "subsequent requests are faster._"
320
+ )
321
+
322
+
323
+ with gr.Blocks(title="Junior Associate Β· Stay Curious", css=CSS, theme=gr.themes.Base()) as demo:
324
+
325
+ gr.HTML(HEADER_HTML)
326
+
327
+ gr.HTML('<div class="section-label">Ask the model a contract question</div>')
328
 
329
  question = gr.Textbox(
 
330
  placeholder="e.g. Their MSA caps liability at $1M. Is that reasonable?",
331
  lines=3,
332
+ show_label=False,
333
+ container=False,
334
  )
335
+
336
+ submit = gr.Button("Ask the Junior Associate β†’", elem_classes="primary-button", variant="primary")
337
+
338
+ gr.HTML('<div class="section-label">Try one of these</div>')
339
+
340
+ with gr.Row(elem_classes="examples-row"):
341
+ example_buttons = []
342
+ for label, full_text in zip(EXAMPLE_LABELS, EXAMPLES):
343
+ btn = gr.Button(label, elem_classes="example-btn")
344
+ example_buttons.append((btn, full_text))
345
+
346
+ gr.HTML('<div class="section-label">The reply</div>')
347
 
348
  output = gr.Markdown(
349
+ value=INITIAL_OUTPUT,
350
+ elem_id="output-block",
351
  )
352
 
353
+ gr.HTML(FOOTER_HTML)
354
+
355
+ # Wire up
356
  submit.click(fn=generate_reply, inputs=question, outputs=output)
357
  question.submit(fn=generate_reply, inputs=question, outputs=output)
358
 
359
+ # Example clicks: populate the textbox and immediately run inference
360
+ for btn, full_text in example_buttons:
361
+ btn.click(fn=lambda t=full_text: t, inputs=None, outputs=question).then(
362
+ fn=generate_reply, inputs=question, outputs=output
363
+ )
 
 
 
 
364
 
365
 
366
  if __name__ == "__main__":