Karu-chan commited on
Commit
36f5372
·
verified ·
1 Parent(s): 179dbdf

Delete 4_lab4.ipynb

Browse files
Files changed (1) hide show
  1. 4_lab4.ipynb +0 -588
4_lab4.ipynb DELETED
@@ -1,588 +0,0 @@
1
- {
2
- "cells": [
3
- {
4
- "cell_type": "markdown",
5
- "metadata": {},
6
- "source": [
7
- "## The first big project - Professionally You!\n",
8
- "\n",
9
- "### And, Tool use.\n",
10
- "\n",
11
- "### But first: introducing Pushover\n",
12
- "\n",
13
- "Pushover is a nifty tool for sending Push Notifications to your phone.\n",
14
- "\n",
15
- "It's super easy to set up and install!\n",
16
- "\n",
17
- "Simply visit https://pushover.net/ and click 'Login or Signup' on the top right to sign up for a free account, and create your API keys.\n",
18
- "\n",
19
- "Once you've signed up, on the home screen, click \"Create an Application/API Token\", and give it any name (like Agents) and click Create Application.\n",
20
- "\n",
21
- "Then add 2 lines to your `.env` file:\n",
22
- "\n",
23
- "PUSHOVER_USER=_put the key that's on the top right of your Pushover home screen and probably starts with a u_ \n",
24
- "PUSHOVER_TOKEN=_put the key when you click into your new application called Agents (or whatever) and probably starts with an a_\n",
25
- "\n",
26
- "Remember to save your `.env` file, and run `load_dotenv(override=True)` after saving, to set your environment variables.\n",
27
- "\n",
28
- "Finally, click \"Add Phone, Tablet or Desktop\" to install on your phone."
29
- ]
30
- },
31
- {
32
- "cell_type": "code",
33
- "execution_count": 3,
34
- "metadata": {},
35
- "outputs": [],
36
- "source": [
37
- "# imports\n",
38
- "\n",
39
- "from dotenv import load_dotenv\n",
40
- "from openai import OpenAI\n",
41
- "import json\n",
42
- "import os\n",
43
- "import requests\n",
44
- "from pypdf import PdfReader\n",
45
- "import gradio as gr"
46
- ]
47
- },
48
- {
49
- "cell_type": "code",
50
- "execution_count": 4,
51
- "metadata": {},
52
- "outputs": [],
53
- "source": [
54
- "# The usual start\n",
55
- "\n",
56
- "load_dotenv(override=True)\n",
57
- "openai = OpenAI()"
58
- ]
59
- },
60
- {
61
- "cell_type": "code",
62
- "execution_count": 5,
63
- "metadata": {},
64
- "outputs": [
65
- {
66
- "name": "stdout",
67
- "output_type": "stream",
68
- "text": [
69
- "Pushover user found and starts with u\n",
70
- "Pushover token found and starts with a\n"
71
- ]
72
- }
73
- ],
74
- "source": [
75
- "# For pushover\n",
76
- "\n",
77
- "pushover_user = os.getenv(\"PUSHOVER_USER\")\n",
78
- "pushover_token = os.getenv(\"PUSHOVER_TOKEN\")\n",
79
- "pushover_url = \"https://api.pushover.net/1/messages.json\"\n",
80
- "\n",
81
- "if pushover_user:\n",
82
- " print(f\"Pushover user found and starts with {pushover_user[0]}\")\n",
83
- "else:\n",
84
- " print(\"Pushover user not found\")\n",
85
- "\n",
86
- "if pushover_token:\n",
87
- " print(f\"Pushover token found and starts with {pushover_token[0]}\")\n",
88
- "else:\n",
89
- " print(\"Pushover token not found\")"
90
- ]
91
- },
92
- {
93
- "cell_type": "code",
94
- "execution_count": 8,
95
- "metadata": {},
96
- "outputs": [],
97
- "source": [
98
- "def push(message):\n",
99
- " print(f\"Push: {message}\")\n",
100
- " payload = {\"user\": pushover_user, \"token\": pushover_token, \"message\": message}\n",
101
- " requests.post(pushover_url, data=payload)"
102
- ]
103
- },
104
- {
105
- "cell_type": "code",
106
- "execution_count": 9,
107
- "metadata": {},
108
- "outputs": [
109
- {
110
- "name": "stdout",
111
- "output_type": "stream",
112
- "text": [
113
- "Push: HEY!!\n"
114
- ]
115
- }
116
- ],
117
- "source": [
118
- "push(\"HEY!!\")"
119
- ]
120
- },
121
- {
122
- "cell_type": "code",
123
- "execution_count": 11,
124
- "metadata": {},
125
- "outputs": [],
126
- "source": [
127
- "def record_user_details(email, name=\"Name not provided\", notes=\"not provided\"):\n",
128
- " push(f\"Recording interest from {name} with email {email} and notes {notes}\")\n",
129
- " return {\"recorded\": \"ok\"}"
130
- ]
131
- },
132
- {
133
- "cell_type": "code",
134
- "execution_count": 13,
135
- "metadata": {},
136
- "outputs": [],
137
- "source": [
138
- "def record_unknown_question(question):\n",
139
- " push(f\"Recording {question} asked that I couldn't answer\")\n",
140
- " return {\"recorded\": \"ok\"}"
141
- ]
142
- },
143
- {
144
- "cell_type": "code",
145
- "execution_count": 14,
146
- "metadata": {},
147
- "outputs": [],
148
- "source": [
149
- "record_user_details_json = {\n",
150
- " \"name\": \"record_user_details\",\n",
151
- " \"description\": \"Use this tool to record that a user is interested in being in touch and provided an email address\",\n",
152
- " \"parameters\": {\n",
153
- " \"type\": \"object\",\n",
154
- " \"properties\": {\n",
155
- " \"email\": {\n",
156
- " \"type\": \"string\",\n",
157
- " \"description\": \"The email address of this user\"\n",
158
- " },\n",
159
- " \"name\": {\n",
160
- " \"type\": \"string\",\n",
161
- " \"description\": \"The user's name, if they provided it\"\n",
162
- " }\n",
163
- " ,\n",
164
- " \"notes\": {\n",
165
- " \"type\": \"string\",\n",
166
- " \"description\": \"Any additional information about the conversation that's worth recording to give context\"\n",
167
- " }\n",
168
- " },\n",
169
- " \"required\": [\"email\"],\n",
170
- " \"additionalProperties\": False\n",
171
- " }\n",
172
- "}"
173
- ]
174
- },
175
- {
176
- "cell_type": "code",
177
- "execution_count": 15,
178
- "metadata": {},
179
- "outputs": [],
180
- "source": [
181
- "record_unknown_question_json = {\n",
182
- " \"name\": \"record_unknown_question\",\n",
183
- " \"description\": \"Always use this tool to record any question that couldn't be answered as you didn't know the answer\",\n",
184
- " \"parameters\": {\n",
185
- " \"type\": \"object\",\n",
186
- " \"properties\": {\n",
187
- " \"question\": {\n",
188
- " \"type\": \"string\",\n",
189
- " \"description\": \"The question that couldn't be answered\"\n",
190
- " },\n",
191
- " },\n",
192
- " \"required\": [\"question\"],\n",
193
- " \"additionalProperties\": False\n",
194
- " }\n",
195
- "}"
196
- ]
197
- },
198
- {
199
- "cell_type": "code",
200
- "execution_count": 16,
201
- "metadata": {},
202
- "outputs": [],
203
- "source": [
204
- "tools = [{\"type\": \"function\", \"function\": record_user_details_json},\n",
205
- " {\"type\": \"function\", \"function\": record_unknown_question_json}]"
206
- ]
207
- },
208
- {
209
- "cell_type": "code",
210
- "execution_count": 18,
211
- "metadata": {},
212
- "outputs": [
213
- {
214
- "data": {
215
- "text/plain": [
216
- "[{'type': 'function',\n",
217
- " 'function': {'name': 'record_user_details',\n",
218
- " 'description': 'Use this tool to record that a user is interested in being in touch and provided an email address',\n",
219
- " 'parameters': {'type': 'object',\n",
220
- " 'properties': {'email': {'type': 'string',\n",
221
- " 'description': 'The email address of this user'},\n",
222
- " 'name': {'type': 'string',\n",
223
- " 'description': \"The user's name, if they provided it\"},\n",
224
- " 'notes': {'type': 'string',\n",
225
- " 'description': \"Any additional information about the conversation that's worth recording to give context\"}},\n",
226
- " 'required': ['email'],\n",
227
- " 'additionalProperties': False}}},\n",
228
- " {'type': 'function',\n",
229
- " 'function': {'name': 'record_unknown_question',\n",
230
- " 'description': \"Always use this tool to record any question that couldn't be answered as you didn't know the answer\",\n",
231
- " 'parameters': {'type': 'object',\n",
232
- " 'properties': {'question': {'type': 'string',\n",
233
- " 'description': \"The question that couldn't be answered\"}},\n",
234
- " 'required': ['question'],\n",
235
- " 'additionalProperties': False}}}]"
236
- ]
237
- },
238
- "execution_count": 18,
239
- "metadata": {},
240
- "output_type": "execute_result"
241
- }
242
- ],
243
- "source": [
244
- "tools"
245
- ]
246
- },
247
- {
248
- "cell_type": "code",
249
- "execution_count": 19,
250
- "metadata": {},
251
- "outputs": [],
252
- "source": [
253
- "# This function can take a list of tool calls, and run them. This is the IF statement!!\n",
254
- "\n",
255
- "def handle_tool_calls(tool_calls):\n",
256
- " results = []\n",
257
- " for tool_call in tool_calls:\n",
258
- " tool_name = tool_call.function.name\n",
259
- " arguments = json.loads(tool_call.function.arguments)\n",
260
- " print(f\"Tool called: {tool_name}\", flush=True)\n",
261
- "\n",
262
- " # THE BIG IF STATEMENT!!!\n",
263
- "\n",
264
- " if tool_name == \"record_user_details\":\n",
265
- " result = record_user_details(**arguments)\n",
266
- " elif tool_name == \"record_unknown_question\":\n",
267
- " result = record_unknown_question(**arguments)\n",
268
- "\n",
269
- " results.append({\"role\": \"tool\",\"content\": json.dumps(result),\"tool_call_id\": tool_call.id})\n",
270
- " return results"
271
- ]
272
- },
273
- {
274
- "cell_type": "code",
275
- "execution_count": 20,
276
- "metadata": {},
277
- "outputs": [
278
- {
279
- "name": "stdout",
280
- "output_type": "stream",
281
- "text": [
282
- "Push: Recording this is a really hard question asked that I couldn't answer\n"
283
- ]
284
- },
285
- {
286
- "data": {
287
- "text/plain": [
288
- "{'recorded': 'ok'}"
289
- ]
290
- },
291
- "execution_count": 20,
292
- "metadata": {},
293
- "output_type": "execute_result"
294
- }
295
- ],
296
- "source": [
297
- "globals()[\"record_unknown_question\"](\"this is a really hard question\")"
298
- ]
299
- },
300
- {
301
- "cell_type": "code",
302
- "execution_count": null,
303
- "metadata": {},
304
- "outputs": [],
305
- "source": [
306
- "# This is a more elegant way that avoids the IF statement.\n",
307
- "\n",
308
- "def handle_tool_calls(tool_calls):\n",
309
- " results = []\n",
310
- " for tool_call in tool_calls:\n",
311
- " tool_name = tool_call.function.name\n",
312
- " arguments = json.loads(tool_call.function.arguments)\n",
313
- " print(f\"Tool called: {tool_name}\", flush=True)\n",
314
- " tool = globals().get(tool_name)\n",
315
- " result = tool(**arguments) if tool else {}\n",
316
- " results.append({\"role\": \"tool\",\"content\": json.dumps(result),\"tool_call_id\": tool_call.id})\n",
317
- " return results"
318
- ]
319
- },
320
- {
321
- "cell_type": "code",
322
- "execution_count": null,
323
- "metadata": {},
324
- "outputs": [],
325
- "source": [
326
- "reader = PdfReader(\"me/linkedin.pdf\")\n",
327
- "linkedin = \"\"\n",
328
- "for page in reader.pages:\n",
329
- " text = page.extract_text()\n",
330
- " if text:\n",
331
- " linkedin += text\n",
332
- "\n",
333
- "with open(\"me/summary.txt\", \"r\", encoding=\"utf-8\") as f:\n",
334
- " summary = f.read()\n",
335
- "\n",
336
- "name = \"Ed Donner\""
337
- ]
338
- },
339
- {
340
- "cell_type": "code",
341
- "execution_count": 24,
342
- "metadata": {},
343
- "outputs": [],
344
- "source": [
345
- "# this version expands on above to include CV\n",
346
- "# Read LinkedIn PDF \n",
347
- "reader = PdfReader(\"me/linkedin.pdf\")\n",
348
- "linkedin = \"\"\n",
349
- "for page in reader.pages:\n",
350
- " text = page.extract_text()\n",
351
- " if text:\n",
352
- " linkedin += text\n",
353
- "\n",
354
- "# Read CV PDF\n",
355
- "reader = PdfReader(\"me/CV.pdf\")\n",
356
- "cv = \"\"\n",
357
- "for page in reader.pages:\n",
358
- " text = page.extract_text()\n",
359
- " if text:\n",
360
- " cv += text\n",
361
- "\n",
362
- "# Read summary\n",
363
- "with open(\"me/summary.txt\", \"r\", encoding=\"utf-8\") as f:\n",
364
- " summary = f.read()\n",
365
- "\n",
366
- "name = \"Calbert Graham\""
367
- ]
368
- },
369
- {
370
- "cell_type": "code",
371
- "execution_count": 25,
372
- "metadata": {},
373
- "outputs": [],
374
- "source": [
375
- "system_prompt = f\"You are acting as {name}. You are answering questions on {name}'s website, \\\n",
376
- "particularly questions related to {name}'s career, background, skills and experience. \\\n",
377
- "Your responsibility is to represent {name} for interactions on the website as faithfully as possible. \\\n",
378
- "You are given a summary of {name}'s background and LinkedIn profile which you can use to answer questions. \\\n",
379
- "Be professional and engaging, as if talking to a potential client or future employer who came across the website. \\\n",
380
- "If you don't know the answer to any question, use your record_unknown_question tool to record the question that you couldn't answer, even if it's about something trivial or unrelated to career. \\\n",
381
- "If the user is engaging in discussion, try to steer them towards getting in touch via email; ask for their email and record it using your record_user_details tool. \"\n",
382
- "\n",
383
- "system_prompt += f\"\\n\\n## Summary:\\n{summary}\\n\\n## LinkedIn Profile:\\n{linkedin}\\n\\n## CV:\\n{cv}\\n\\n\"\n",
384
- "system_prompt += f\"With this context, please chat with the user, always staying in character as {name}.\"\n",
385
- "# system_prompt += f\"\\n\\n## Summary:\\n{summary}\\n\\n## LinkedIn Profile:\\n{linkedin}\\n\\n\"\n",
386
- "# system_prompt += f\"With this context, please chat with the user, always staying in character as {name}.\"\n"
387
- ]
388
- },
389
- {
390
- "cell_type": "code",
391
- "execution_count": 26,
392
- "metadata": {},
393
- "outputs": [],
394
- "source": [
395
- "def chat(message, history):\n",
396
- " messages = [{\"role\": \"system\", \"content\": system_prompt}] + history + [{\"role\": \"user\", \"content\": message}]\n",
397
- " done = False\n",
398
- " while not done:\n",
399
- "\n",
400
- " # This is the call to the LLM - see that we pass in the tools json\n",
401
- "\n",
402
- " response = openai.chat.completions.create(model=\"gpt-4o-mini\", messages=messages, tools=tools)\n",
403
- "\n",
404
- " finish_reason = response.choices[0].finish_reason\n",
405
- " \n",
406
- " # If the LLM wants to call a tool, we do that!\n",
407
- " \n",
408
- " if finish_reason==\"tool_calls\":\n",
409
- " message = response.choices[0].message\n",
410
- " tool_calls = message.tool_calls\n",
411
- " results = handle_tool_calls(tool_calls)\n",
412
- " messages.append(message)\n",
413
- " messages.extend(results)\n",
414
- " else:\n",
415
- " done = True\n",
416
- " return response.choices[0].message.content"
417
- ]
418
- },
419
- {
420
- "cell_type": "code",
421
- "execution_count": null,
422
- "metadata": {},
423
- "outputs": [
424
- {
425
- "name": "stdout",
426
- "output_type": "stream",
427
- "text": [
428
- "* Running on local URL: http://127.0.0.1:7873\n",
429
- "* To create a public link, set `share=True` in `launch()`.\n"
430
- ]
431
- },
432
- {
433
- "data": {
434
- "text/html": [
435
- "<div><iframe src=\"http://127.0.0.1:7873/\" width=\"100%\" height=\"500\" allow=\"autoplay; camera; microphone; clipboard-read; clipboard-write;\" frameborder=\"0\" allowfullscreen></iframe></div>"
436
- ],
437
- "text/plain": [
438
- "<IPython.core.display.HTML object>"
439
- ]
440
- },
441
- "metadata": {},
442
- "output_type": "display_data"
443
- },
444
- {
445
- "data": {
446
- "text/plain": []
447
- },
448
- "execution_count": 27,
449
- "metadata": {},
450
- "output_type": "execute_result"
451
- },
452
- {
453
- "name": "stdout",
454
- "output_type": "stream",
455
- "text": [
456
- "Tool called: record_unknown_question\n",
457
- "Push: Recording do you have a patent? asked that I couldn't answer\n",
458
- "Tool called: record_unknown_question\n",
459
- "Push: Recording Who is your favourite musician? asked that I couldn't answer\n",
460
- "Tool called: record_user_details\n",
461
- "Push: Recording interest from Name not provided with email [email protected] and notes not provided\n"
462
- ]
463
- }
464
- ],
465
- "source": [
466
- "gr.ChatInterface(chat, type=\"messages\").launch()"
467
- ]
468
- },
469
- {
470
- "cell_type": "markdown",
471
- "metadata": {},
472
- "source": [
473
- "## And now for deployment\n",
474
- "\n",
475
- "This code is in `app.py`\n",
476
- "\n",
477
- "We will deploy to HuggingFace Spaces.\n",
478
- "\n",
479
- "Before you start: remember to update the files in the \"me\" directory - your LinkedIn profile and summary.txt - so that it talks about you! Also change `self.name = \"Ed Donner\"` in `app.py`.. \n",
480
- "\n",
481
- "Also check that there's no README file within the 1_foundations directory. If there is one, please delete it. The deploy process creates a new README file in this directory for you.\n",
482
- "\n",
483
- "1. Visit https://huggingface.co and set up an account \n",
484
- "2. From the Avatar menu on the top right, choose Access Tokens. Choose \"Create New Token\". Give it WRITE permissions - it needs to have WRITE permissions! Keep a record of your new key. \n",
485
- "3. In the Terminal, run: `uv tool install 'huggingface_hub[cli]'` to install the HuggingFace tool, then `hf auth login --token YOUR_TOKEN_HERE`, like `hf auth login --token hf_xxxxxx`, to login at the command line with your key. Afterwards, run `hf auth whoami` to check you're logged in \n",
486
- "4. Take your new token and add it to your .env file: `HF_TOKEN=hf_xxx` for the future\n",
487
- "5. From the 1_foundations folder, enter: `uv run gradio deploy` \n",
488
- "6. Follow its instructions: name it \"career_conversation\", specify app.py, choose cpu-basic as the hardware, say Yes to needing to supply secrets, provide your openai api key, your pushover user and token, and say \"no\" to github actions. \n",
489
- "\n",
490
- "Thank you Robert, James, Martins, Andras and Priya for these tips. \n",
491
- "Please read the next 2 sections - how to change your Secrets, and how to redeploy your Space (you may need to delete the README.md that gets created in this 1_foundations directory).\n",
492
- "\n",
493
- "#### More about these secrets:\n",
494
- "\n",
495
- "If you're confused by what's going on with these secrets: it just wants you to enter the key name and value for each of your secrets -- so you would enter: \n",
496
- "`OPENAI_API_KEY` \n",
497
- "Followed by: \n",
498
- "`sk-proj-...` \n",
499
- "\n",
500
- "And if you don't want to set secrets this way, or something goes wrong with it, it's no problem - you can change your secrets later: \n",
501
- "1. Log in to HuggingFace website \n",
502
- "2. Go to your profile screen via the Avatar menu on the top right \n",
503
- "3. Select the Space you deployed \n",
504
- "4. Click on the Settings wheel on the top right \n",
505
- "5. You can scroll down to change your secrets (Variables and Secrets section), delete the space, etc.\n",
506
- "\n",
507
- "#### And now you should be deployed!\n",
508
- "\n",
509
- "If you want to completely replace everything and start again with your keys, you may need to delete the README.md that got created in this 1_foundations folder.\n",
510
- "\n",
511
- "Here is mine: https://huggingface.co/spaces/ed-donner/Career_Conversation\n",
512
- "\n",
513
- "I just got a push notification that a student asked me how they can become President of their country 😂😂\n",
514
- "\n",
515
- "For more information on deployment:\n",
516
- "\n",
517
- "https://www.gradio.app/guides/sharing-your-app#hosting-on-hf-spaces\n",
518
- "\n",
519
- "To delete your Space in the future: \n",
520
- "1. Log in to HuggingFace\n",
521
- "2. From the Avatar menu, select your profile\n",
522
- "3. Click on the Space itself and select the settings wheel on the top right\n",
523
- "4. Scroll to the Delete section at the bottom\n",
524
- "5. ALSO: delete the README file that Gradio may have created inside this 1_foundations folder (otherwise it won't ask you the questions the next time you do a gradio deploy)\n"
525
- ]
526
- },
527
- {
528
- "cell_type": "markdown",
529
- "metadata": {},
530
- "source": [
531
- "<table style=\"margin: 0; text-align: left; width:100%\">\n",
532
- " <tr>\n",
533
- " <td style=\"width: 150px; height: 150px; vertical-align: middle;\">\n",
534
- " <img src=\"../assets/exercise.png\" width=\"150\" height=\"150\" style=\"display: block;\" />\n",
535
- " </td>\n",
536
- " <td>\n",
537
- " <h2 style=\"color:#ff7800;\">Exercise</h2>\n",
538
- " <span style=\"color:#ff7800;\">• First and foremost, deploy this for yourself! It's a real, valuable tool - the future resume..<br/>\n",
539
- " • Next, improve the resources - add better context about yourself. If you know RAG, then add a knowledge base about you.<br/>\n",
540
- " • Add in more tools! You could have a SQL database with common Q&A that the LLM could read and write from?<br/>\n",
541
- " • Bring in the Evaluator from the last lab, and add other Agentic patterns.\n",
542
- " </span>\n",
543
- " </td>\n",
544
- " </tr>\n",
545
- "</table>"
546
- ]
547
- },
548
- {
549
- "cell_type": "markdown",
550
- "metadata": {},
551
- "source": [
552
- "<table style=\"margin: 0; text-align: left; width:100%\">\n",
553
- " <tr>\n",
554
- " <td style=\"width: 150px; height: 150px; vertical-align: middle;\">\n",
555
- " <img src=\"../assets/business.png\" width=\"150\" height=\"150\" style=\"display: block;\" />\n",
556
- " </td>\n",
557
- " <td>\n",
558
- " <h2 style=\"color:#00bfff;\">Commercial implications</h2>\n",
559
- " <span style=\"color:#00bfff;\">Aside from the obvious (your career alter-ego) this has business applications in any situation where you need an AI assistant with domain expertise and an ability to interact with the real world.\n",
560
- " </span>\n",
561
- " </td>\n",
562
- " </tr>\n",
563
- "</table>"
564
- ]
565
- }
566
- ],
567
- "metadata": {
568
- "kernelspec": {
569
- "display_name": ".venv",
570
- "language": "python",
571
- "name": "python3"
572
- },
573
- "language_info": {
574
- "codemirror_mode": {
575
- "name": "ipython",
576
- "version": 3
577
- },
578
- "file_extension": ".py",
579
- "mimetype": "text/x-python",
580
- "name": "python",
581
- "nbconvert_exporter": "python",
582
- "pygments_lexer": "ipython3",
583
- "version": "3.12.9"
584
- }
585
- },
586
- "nbformat": 4,
587
- "nbformat_minor": 2
588
- }