Observer04 commited on
Commit
90da3f4
·
verified ·
1 Parent(s): 7f3dbbc

Delete 4_lab4.ipynb

Browse files
Files changed (1) hide show
  1. 4_lab4.ipynb +0 -542
4_lab4.ipynb DELETED
@@ -1,542 +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
- "Finally, click \"Add Phone, Tablet or Desktop\" to install on your phone."
27
- ]
28
- },
29
- {
30
- "cell_type": "code",
31
- "execution_count": 28,
32
- "metadata": {},
33
- "outputs": [],
34
- "source": [
35
- "# imports\n",
36
- "\n",
37
- "from dotenv import load_dotenv\n",
38
- "from openai import OpenAI,AzureOpenAI\n",
39
- "import json\n",
40
- "import os\n",
41
- "import requests\n",
42
- "from pypdf import PdfReader\n",
43
- "import gradio as gr"
44
- ]
45
- },
46
- {
47
- "cell_type": "code",
48
- "execution_count": 29,
49
- "metadata": {},
50
- "outputs": [],
51
- "source": [
52
- "# The usual start\n",
53
- "\n",
54
- "load_dotenv(override=True)\n",
55
- "openai_api_key = os.getenv('OPENAI_API_KEY')\n",
56
- "azure_endpoint = os.getenv('AZURE_ENDPOINT')\n",
57
- "api_version= os.getenv('OPENAI_API_VERSION')\n",
58
- "openai = AzureOpenAI(\n",
59
- " azure_endpoint=azure_endpoint,\n",
60
- " api_key=openai_api_key\n",
61
- ")"
62
- ]
63
- },
64
- {
65
- "cell_type": "code",
66
- "execution_count": 30,
67
- "metadata": {},
68
- "outputs": [],
69
- "source": [
70
- "# For pushover\n",
71
- "\n",
72
- "pushover_user = os.getenv(\"PUSHOVER_USER\")\n",
73
- "pushover_token = os.getenv(\"PUSHOVER_TOKEN\")\n",
74
- "pushover_url = \"https://api.pushover.net/1/messages.json\""
75
- ]
76
- },
77
- {
78
- "cell_type": "code",
79
- "execution_count": 31,
80
- "metadata": {},
81
- "outputs": [],
82
- "source": [
83
- "def push(message):\n",
84
- " print(f\"Push: {message}\")\n",
85
- " payload = {\"user\": pushover_user, \"token\": pushover_token, \"message\": message}\n",
86
- " requests.post(pushover_url, data=payload)"
87
- ]
88
- },
89
- {
90
- "cell_type": "code",
91
- "execution_count": 48,
92
- "metadata": {},
93
- "outputs": [
94
- {
95
- "name": "stdout",
96
- "output_type": "stream",
97
- "text": [
98
- "Push: HEY!!\n"
99
- ]
100
- }
101
- ],
102
- "source": [
103
- "push(\"HEY!!\")"
104
- ]
105
- },
106
- {
107
- "cell_type": "code",
108
- "execution_count": 33,
109
- "metadata": {},
110
- "outputs": [],
111
- "source": [
112
- "def record_user_details(email, name=\"Name not provided\", notes=\"not provided\"):\n",
113
- " push(f\"Recording interest from {name} with email {email} and notes {notes}\")\n",
114
- " return {\"recorded\": \"ok\"}"
115
- ]
116
- },
117
- {
118
- "cell_type": "code",
119
- "execution_count": 34,
120
- "metadata": {},
121
- "outputs": [],
122
- "source": [
123
- "def record_unknown_question(question):\n",
124
- " push(f\"Recording {question} asked that I couldn't answer\")\n",
125
- " return {\"recorded\": \"ok\"}"
126
- ]
127
- },
128
- {
129
- "cell_type": "code",
130
- "execution_count": 35,
131
- "metadata": {},
132
- "outputs": [],
133
- "source": [
134
- "record_user_details_json = {\n",
135
- " \"name\": \"record_user_details\",\n",
136
- " \"description\": \"Use this tool to record that a user is interested in being in touch and provided an email address\",\n",
137
- " \"parameters\": {\n",
138
- " \"type\": \"object\",\n",
139
- " \"properties\": {\n",
140
- " \"email\": {\n",
141
- " \"type\": \"string\",\n",
142
- " \"description\": \"The email address of this user\"\n",
143
- " },\n",
144
- " \"name\": {\n",
145
- " \"type\": \"string\",\n",
146
- " \"description\": \"The user's name, if they provided it\"\n",
147
- " }\n",
148
- " ,\n",
149
- " \"notes\": {\n",
150
- " \"type\": \"string\",\n",
151
- " \"description\": \"Any additional information about the conversation that's worth recording to give context\"\n",
152
- " }\n",
153
- " },\n",
154
- " \"required\": [\"email\"],\n",
155
- " \"additionalProperties\": False\n",
156
- " }\n",
157
- "}"
158
- ]
159
- },
160
- {
161
- "cell_type": "code",
162
- "execution_count": 36,
163
- "metadata": {},
164
- "outputs": [],
165
- "source": [
166
- "record_unknown_question_json = {\n",
167
- " \"name\": \"record_unknown_question\",\n",
168
- " \"description\": \"Always use this tool to record any question that couldn't be answered as you didn't know the answer\",\n",
169
- " \"parameters\": {\n",
170
- " \"type\": \"object\",\n",
171
- " \"properties\": {\n",
172
- " \"question\": {\n",
173
- " \"type\": \"string\",\n",
174
- " \"description\": \"The question that couldn't be answered\"\n",
175
- " },\n",
176
- " },\n",
177
- " \"required\": [\"question\"],\n",
178
- " \"additionalProperties\": False\n",
179
- " }\n",
180
- "}"
181
- ]
182
- },
183
- {
184
- "cell_type": "code",
185
- "execution_count": 37,
186
- "metadata": {},
187
- "outputs": [],
188
- "source": [
189
- "tools = [{\"type\": \"function\", \"function\": record_user_details_json},\n",
190
- " {\"type\": \"function\", \"function\": record_unknown_question_json}]"
191
- ]
192
- },
193
- {
194
- "cell_type": "code",
195
- "execution_count": 38,
196
- "metadata": {},
197
- "outputs": [
198
- {
199
- "data": {
200
- "text/plain": [
201
- "[{'type': 'function',\n",
202
- " 'function': {'name': 'record_user_details',\n",
203
- " 'description': 'Use this tool to record that a user is interested in being in touch and provided an email address',\n",
204
- " 'parameters': {'type': 'object',\n",
205
- " 'properties': {'email': {'type': 'string',\n",
206
- " 'description': 'The email address of this user'},\n",
207
- " 'name': {'type': 'string',\n",
208
- " 'description': \"The user's name, if they provided it\"},\n",
209
- " 'notes': {'type': 'string',\n",
210
- " 'description': \"Any additional information about the conversation that's worth recording to give context\"}},\n",
211
- " 'required': ['email'],\n",
212
- " 'additionalProperties': False}}},\n",
213
- " {'type': 'function',\n",
214
- " 'function': {'name': 'record_unknown_question',\n",
215
- " 'description': \"Always use this tool to record any question that couldn't be answered as you didn't know the answer\",\n",
216
- " 'parameters': {'type': 'object',\n",
217
- " 'properties': {'question': {'type': 'string',\n",
218
- " 'description': \"The question that couldn't be answered\"}},\n",
219
- " 'required': ['question'],\n",
220
- " 'additionalProperties': False}}}]"
221
- ]
222
- },
223
- "execution_count": 38,
224
- "metadata": {},
225
- "output_type": "execute_result"
226
- }
227
- ],
228
- "source": [
229
- "tools"
230
- ]
231
- },
232
- {
233
- "cell_type": "code",
234
- "execution_count": 39,
235
- "metadata": {},
236
- "outputs": [],
237
- "source": [
238
- "# This function can take a list of tool calls, and run them. This is the IF statement!!\n",
239
- "\n",
240
- "def handle_tool_calls(tool_calls):\n",
241
- " results = []\n",
242
- " for tool_call in tool_calls:\n",
243
- " tool_name = tool_call.function.name\n",
244
- " arguments = json.loads(tool_call.function.arguments)\n",
245
- " print(f\"Tool called: {tool_name}\", flush=True)\n",
246
- "\n",
247
- " # THE BIG IF STATEMENT!!!\n",
248
- "\n",
249
- " if tool_name == \"record_user_details\":\n",
250
- " result = record_user_details(**arguments)\n",
251
- " elif tool_name == \"record_unknown_question\":\n",
252
- " result = record_unknown_question(**arguments)\n",
253
- "\n",
254
- " results.append({\"role\": \"tool\",\"content\": json.dumps(result),\"tool_call_id\": tool_call.id})\n",
255
- " return results"
256
- ]
257
- },
258
- {
259
- "cell_type": "code",
260
- "execution_count": 49,
261
- "metadata": {},
262
- "outputs": [
263
- {
264
- "name": "stdout",
265
- "output_type": "stream",
266
- "text": [
267
- "Push: Recording this is a really hard question asked that I couldn't answer\n"
268
- ]
269
- },
270
- {
271
- "data": {
272
- "text/plain": [
273
- "{'recorded': 'ok'}"
274
- ]
275
- },
276
- "execution_count": 49,
277
- "metadata": {},
278
- "output_type": "execute_result"
279
- }
280
- ],
281
- "source": [
282
- "globals()[\"record_unknown_question\"](\"this is a really hard question\")"
283
- ]
284
- },
285
- {
286
- "cell_type": "code",
287
- "execution_count": 41,
288
- "metadata": {},
289
- "outputs": [],
290
- "source": [
291
- "# This is a more elegant way that avoids the IF statement.\n",
292
- "\n",
293
- "def handle_tool_calls(tool_calls):\n",
294
- " results = []\n",
295
- " for tool_call in tool_calls:\n",
296
- " tool_name = tool_call.function.name\n",
297
- " arguments = json.loads(tool_call.function.arguments)\n",
298
- " print(f\"Tool called: {tool_name}\", flush=True)\n",
299
- " tool = globals().get(tool_name)\n",
300
- " result = tool(**arguments) if tool else {}\n",
301
- " results.append({\"role\": \"tool\",\"content\": json.dumps(result),\"tool_call_id\": tool_call.id})\n",
302
- " return results"
303
- ]
304
- },
305
- {
306
- "cell_type": "code",
307
- "execution_count": 50,
308
- "metadata": {},
309
- "outputs": [],
310
- "source": [
311
- "reader = PdfReader(\"me/Profile.pdf\")\n",
312
- "linkedin = \"\"\n",
313
- "for page in reader.pages:\n",
314
- " text = page.extract_text()\n",
315
- " if text:\n",
316
- " linkedin += text\n",
317
- "\n",
318
- "with open(\"me/summary.txt\", \"r\", encoding=\"utf-8\") as f:\n",
319
- " summary = f.read()\n",
320
- "\n",
321
- "name = \"Ed Donner\""
322
- ]
323
- },
324
- {
325
- "cell_type": "code",
326
- "execution_count": 51,
327
- "metadata": {},
328
- "outputs": [],
329
- "source": [
330
- "system_prompt = f\"You are acting as {name}. You are answering questions on {name}'s website, \\\n",
331
- "particularly questions related to {name}'s career, background, skills and experience. \\\n",
332
- "Your responsibility is to represent {name} for interactions on the website as faithfully as possible. \\\n",
333
- "You are given a summary of {name}'s background and LinkedIn profile which you can use to answer questions. \\\n",
334
- "Be professional and engaging, as if talking to a potential client or future employer who came across the website. \\\n",
335
- "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",
336
- "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",
337
- "\n",
338
- "system_prompt += f\"\\n\\n## Summary:\\n{summary}\\n\\n## LinkedIn Profile:\\n{linkedin}\\n\\n\"\n",
339
- "system_prompt += f\"With this context, please chat with the user, always staying in character as {name}.\"\n"
340
- ]
341
- },
342
- {
343
- "cell_type": "code",
344
- "execution_count": 53,
345
- "metadata": {},
346
- "outputs": [],
347
- "source": [
348
- "def chat(message, history):\n",
349
- " messages = [{\"role\": \"system\", \"content\": system_prompt}] + history + [{\"role\": \"user\", \"content\": message}]\n",
350
- " done = False\n",
351
- " while not done:\n",
352
- "\n",
353
- " # This is the call to the LLM - see that we pass in the tools json\n",
354
- "\n",
355
- " response = openai.chat.completions.create(model=\"gpt-4o-mini\", messages=messages, tools=tools)\n",
356
- "\n",
357
- " finish_reason = response.choices[0].finish_reason\n",
358
- " \n",
359
- " # If the LLM wants to call a tool, we do that!\n",
360
- " \n",
361
- " if finish_reason==\"tool_calls\":\n",
362
- " message = response.choices[0].message\n",
363
- " tool_calls = message.tool_calls\n",
364
- " results = handle_tool_calls(tool_calls)\n",
365
- " messages.append(message)\n",
366
- " messages.extend(results)\n",
367
- " else:\n",
368
- " done = True\n",
369
- " return response.choices[0].message.content"
370
- ]
371
- },
372
- {
373
- "cell_type": "code",
374
- "execution_count": 54,
375
- "metadata": {},
376
- "outputs": [
377
- {
378
- "name": "stdout",
379
- "output_type": "stream",
380
- "text": [
381
- "* Running on local URL: http://127.0.0.1:7866\n",
382
- "* To create a public link, set `share=True` in `launch()`.\n"
383
- ]
384
- },
385
- {
386
- "data": {
387
- "text/html": [
388
- "<div><iframe src=\"http://127.0.0.1:7866/\" width=\"100%\" height=\"500\" allow=\"autoplay; camera; microphone; clipboard-read; clipboard-write;\" frameborder=\"0\" allowfullscreen></iframe></div>"
389
- ],
390
- "text/plain": [
391
- "<IPython.core.display.HTML object>"
392
- ]
393
- },
394
- "metadata": {},
395
- "output_type": "display_data"
396
- },
397
- {
398
- "data": {
399
- "text/plain": []
400
- },
401
- "execution_count": 54,
402
- "metadata": {},
403
- "output_type": "execute_result"
404
- },
405
- {
406
- "name": "stdout",
407
- "output_type": "stream",
408
- "text": [
409
- "Tool called: record_user_details\n",
410
- "Push: Recording interest from Name not provided with email [email protected] and notes not provided\n",
411
- "Tool called: record_user_details\n",
412
- "Push: Recording interest from Name not provided with email [email protected] and notes User requested Ed's resume before Friday.\n",
413
- "Tool called: record_unknown_question\n",
414
- "Push: Recording Can Ed send his resume to the user before coming Friday? asked that I couldn't answer\n"
415
- ]
416
- }
417
- ],
418
- "source": [
419
- "gr.ChatInterface(chat, type=\"messages\").launch()"
420
- ]
421
- },
422
- {
423
- "cell_type": "markdown",
424
- "metadata": {},
425
- "source": [
426
- "## And now for deployment\n",
427
- "\n",
428
- "This code is in `app.py`\n",
429
- "\n",
430
- "We will deploy to HuggingFace Spaces. Thank you student Robert M for improving these instructions.\n",
431
- "\n",
432
- "Before you start: remember to update the files in the \"me\" directory - your LinkedIn profile and summary.txt - so that it talks about you! \n",
433
- "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",
434
- "\n",
435
- "1. Visit https://huggingface.co and set up an account \n",
436
- "2. From the Avatar menu on the top right, choose Access Tokens. Choose \"Create New Token\". Give it WRITE permissions.\n",
437
- "3. Take this token and add it to your .env file: `HF_TOKEN=hf_xxx` and see note below if this token doesn't seem to get picked up during deployment \n",
438
- "4. From the 1_foundations folder, enter: `uv run gradio deploy` and if for some reason this still wants you to enter your HF token, then interrupt it with ctrl+c and run this instead: `uv run dotenv -f ../.env run -- uv run gradio deploy` which forces your keys to all be set as environment variables \n",
439
- "5. 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",
440
- "\n",
441
- "#### Extra note about the HuggingFace token\n",
442
- "\n",
443
- "A couple of students have mentioned the HuggingFace doesn't detect their token, even though it's in the .env file. Here are things to try: \n",
444
- "1. Restart Cursor \n",
445
- "2. Rerun load_dotenv(override=True) and use a new terminal (the + button on the top right of the Terminal) \n",
446
- "3. In the Terminal, run this before the gradio deploy: `$env:HF_TOKEN = \"hf_XXXX\"` \n",
447
- "Thank you James and Martins for these tips. \n",
448
- "\n",
449
- "#### More about these secrets:\n",
450
- "\n",
451
- "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",
452
- "`OPENAI_API_KEY` \n",
453
- "Followed by: \n",
454
- "`sk-proj-...` \n",
455
- "\n",
456
- "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",
457
- "1. Log in to HuggingFace website \n",
458
- "2. Go to your profile screen via the Avatar menu on the top right \n",
459
- "3. Select the Space you deployed \n",
460
- "4. Click on the Settings wheel on the top right \n",
461
- "5. You can scroll down to change your secrets, delete the space, etc.\n",
462
- "\n",
463
- "#### And now you should be deployed!\n",
464
- "\n",
465
- "Here is mine: https://huggingface.co/spaces/ed-donner/Career_Conversation\n",
466
- "\n",
467
- "I just got a push notification that a student asked me how they can become President of their country 😂😂\n",
468
- "\n",
469
- "For more information on deployment:\n",
470
- "\n",
471
- "https://www.gradio.app/guides/sharing-your-app#hosting-on-hf-spaces\n",
472
- "\n",
473
- "To delete your Space in the future: \n",
474
- "1. Log in to HuggingFace\n",
475
- "2. From the Avatar menu, select your profile\n",
476
- "3. Click on the Space itself and select the settings wheel on the top right\n",
477
- "4. Scroll to the Delete section at the bottom\n",
478
- "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"
479
- ]
480
- },
481
- {
482
- "cell_type": "markdown",
483
- "metadata": {},
484
- "source": [
485
- "<table style=\"margin: 0; text-align: left; width:100%\">\n",
486
- " <tr>\n",
487
- " <td style=\"width: 150px; height: 150px; vertical-align: middle;\">\n",
488
- " <img src=\"../assets/exercise.png\" width=\"150\" height=\"150\" style=\"display: block;\" />\n",
489
- " </td>\n",
490
- " <td>\n",
491
- " <h2 style=\"color:#ff7800;\">Exercise</h2>\n",
492
- " <span style=\"color:#ff7800;\">• First and foremost, deploy this for yourself! It's a real, valuable tool - the future resume..<br/>\n",
493
- " • Next, improve the resources - add better context about yourself. If you know RAG, then add a knowledge base about you.<br/>\n",
494
- " • Add in more tools! You could have a SQL database with common Q&A that the LLM could read and write from?<br/>\n",
495
- " • Bring in the Evaluator from the last lab, and add other Agentic patterns.\n",
496
- " </span>\n",
497
- " </td>\n",
498
- " </tr>\n",
499
- "</table>"
500
- ]
501
- },
502
- {
503
- "cell_type": "markdown",
504
- "metadata": {},
505
- "source": [
506
- "<table style=\"margin: 0; text-align: left; width:100%\">\n",
507
- " <tr>\n",
508
- " <td style=\"width: 150px; height: 150px; vertical-align: middle;\">\n",
509
- " <img src=\"../assets/business.png\" width=\"150\" height=\"150\" style=\"display: block;\" />\n",
510
- " </td>\n",
511
- " <td>\n",
512
- " <h2 style=\"color:#00bfff;\">Commercial implications</h2>\n",
513
- " <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",
514
- " </span>\n",
515
- " </td>\n",
516
- " </tr>\n",
517
- "</table>"
518
- ]
519
- }
520
- ],
521
- "metadata": {
522
- "kernelspec": {
523
- "display_name": ".venv",
524
- "language": "python",
525
- "name": "python3"
526
- },
527
- "language_info": {
528
- "codemirror_mode": {
529
- "name": "ipython",
530
- "version": 3
531
- },
532
- "file_extension": ".py",
533
- "mimetype": "text/x-python",
534
- "name": "python",
535
- "nbconvert_exporter": "python",
536
- "pygments_lexer": "ipython3",
537
- "version": "3.12.3"
538
- }
539
- },
540
- "nbformat": 4,
541
- "nbformat_minor": 2
542
- }