sjw commited on
Commit
83cbb27
·
1 Parent(s): 494ea9a

Delete app.py

Browse files
Files changed (1) hide show
  1. app.py +0 -844
app.py DELETED
@@ -1,844 +0,0 @@
1
- # Standard Library Imports
2
- import os
3
- import random
4
- import re
5
- import time
6
- from urllib.parse import urlparse, parse_qs
7
-
8
- # Third-Party Imports
9
- import gradio as gr
10
- import lyricsgenius
11
- import requests
12
- import spotipy
13
- from bs4 import BeautifulSoup
14
- from dotenv import load_dotenv
15
- from fuzzywuzzy import fuzz
16
- from pydantic import BaseModel, Field
17
- from requests.exceptions import Timeout
18
- from sentence_transformers import SentenceTransformer
19
- from sklearn.metrics.pairwise import cosine_similarity
20
- from spotipy.exceptions import SpotifyException
21
-
22
- # Local Application/Library Specific Imports
23
- from langchain.agents import OpenAIFunctionsAgent, AgentExecutor, tool
24
- from langchain.chat_models import ChatOpenAI
25
- from langchain.memory import ConversationBufferMemory
26
- from langchain.prompts import MessagesPlaceholder
27
- from langchain.schema import SystemMessage, HumanMessage
28
- from messages import SYSTEM_MESSAGE, GENRE_LIST
29
-
30
- from dotenv import load_dotenv
31
- load_dotenv()
32
-
33
-
34
- # ------------------------------
35
- # Section: Global Vars
36
- # ------------------------------
37
-
38
-
39
- GENIUS_TOKEN = os.getenv("GENIUS_ACCESS_TOKEN")
40
- OPENAI_API_KEY = os.getenv("OPENAI_API_KEY")
41
-
42
- DEBUG_MODE = True
43
- def debug_print(*args, **kwargs):
44
- if DEBUG_MODE:
45
- print(*args, **kwargs)
46
-
47
- THEME = gr.themes.Default(
48
- primary_hue=gr.themes.colors.red,
49
- secondary_hue=gr.themes.colors.pink,
50
- font=[gr.themes.GoogleFont("Inconsolata"), "Arial", "sans-serif"],
51
- spacing_size=gr.themes.sizes.spacing_sm,
52
- radius_size=gr.themes.sizes.radius_sm
53
- )#.set(body_background_fill="#FFFFFF")
54
-
55
- # TODO: switch to personal website
56
- REDIRECT_URI = "https://huggingface.co/sjw"
57
-
58
- # Spotify functions
59
- SCOPE = [
60
- 'user-library-read',
61
- 'user-read-playback-state',
62
- 'user-modify-playback-state',
63
- 'playlist-modify-public',
64
- 'user-top-read'
65
- ]
66
-
67
- MOOD_SETTINGS = {
68
- "happy": {"max_instrumentalness": 0.001, "min_valence": 0.6},
69
- "sad": {"max_danceability": 0.65, "max_valence": 0.4},
70
- "energetic": {"min_tempo": 120, "min_danceability": 0.75},
71
- "calm": {"max_energy": 0.65, "max_tempo": 130}
72
- }
73
-
74
- # genre + mood function
75
- NUM_ARTISTS = 20 # artists to retrieve from user's top artists
76
- TIME_RANGE = "medium_term" # short, medium, long
77
- NUM_TRACKS = 10 # tracks to add to playback
78
- MAX_ARTISTS = 4 # sp.recommendations() seeds: 4/5 artists, 1/5 genre
79
-
80
- # artist + mood function
81
- NUM_ALBUMS = 20 # maximum number of albums to retrieve from an artist
82
- MAX_TRACKS = 10 # tracks to randomly select from an artist
83
-
84
- # matching playlists + moods
85
- MODEL = SentenceTransformer('sentence-transformers/all-MiniLM-L6-v2') # smaller BERT
86
- os.environ["TOKENIZERS_PARALLELISM"] = "false" # warning
87
- MOOD_LIST = ["happy", "sad", "energetic", "calm"]
88
- MOOD_EMBEDDINGS = MODEL.encode(MOOD_LIST)
89
- GENRE_EMBEDDINGS = MODEL.encode(GENRE_LIST)
90
-
91
- # agent tools
92
- RETURN_DIRECT = True
93
-
94
- MODEL = "gpt-3.5-turbo-0613"
95
-
96
- THEMES = ["Epic", "Hypnotic", "Dreamy", "Legendary", "Majestic",
97
- "Enchanting", "Ethereal", "Super Lit", "Harmonious", "Heroic"]
98
-
99
-
100
- with gr.Blocks(theme=THEME) as auth_page:
101
-
102
- # ------------------------------
103
- # Section: Spotify Authentication
104
- # ------------------------------
105
-
106
-
107
- ACCESS_TOKEN_VAR = gr.State()
108
- AGENT_EXECUTOR_VAR = gr.State()
109
-
110
-
111
- client_id = gr.Textbox(placeholder="5. Paste Spotify Client ID here, then click the button below", container=False, text_align="center")
112
- generate_link = gr.Button("6. Get Authentication Link")
113
- display_link = gr.Markdown()
114
- url = gr.Textbox(placeholder="7. Paste entire URL here, then click the button below", container=False, text_align="center")
115
- authorize_url = gr.Button("8. Authorize URL")
116
- auth_result = gr.Markdown()
117
-
118
-
119
- def spotify_auth(client_id, url=None, access_tokens=None):
120
- """
121
- Authenticate Spotify with the provided client_id and url.
122
- """
123
- if url:
124
- parsed_url = urlparse(url)
125
- fragment = parsed_url.fragment
126
- access_token = parse_qs(fragment)['access_token'][0]
127
- print(access_token)
128
-
129
- return access_token, """<span style="font-size:18px;">Authentication Success.</span>"""
130
-
131
- else:
132
- auth_url = (
133
- f"https://accounts.spotify.com/authorize?response_type=token&client_id={client_id}"
134
- f"&scope={'%20'.join(SCOPE)}&redirect_uri={REDIRECT_URI}"
135
- )
136
-
137
- return {
138
- display_link: ("""<span style="font-size:18px;">Authorize by clicking <strong><a href='""" + f"{auth_url}" +
139
- """' target="_blank">here</a></strong> and copy the '<strong>entire URL</strong>' you are redirected to</span>""")
140
- }
141
-
142
-
143
- generate_link.click(spotify_auth, inputs=[client_id], outputs=display_link)
144
- authorize_url.click(spotify_auth, inputs=[client_id, url, ACCESS_TOKEN_VAR], outputs=[ACCESS_TOKEN_VAR, auth_result])
145
-
146
- create_agent_button = gr.Button("Create Apollo")
147
-
148
- def create_agent(access_token):
149
-
150
-
151
- # ------------------------------
152
- # Section: Spotify Functions
153
- # ------------------------------
154
-
155
-
156
- sp = spotipy.Spotify(auth=access_token)
157
- device_id = sp.devices()['devices'][0]['id']
158
-
159
-
160
- def find_track_by_name(track_name):
161
- """
162
- Finds the Spotify track URI given the track name.
163
- """
164
- results = sp.search(q=track_name, type='track')
165
- track_uri = results['tracks']['items'][0]['uri']
166
- return track_uri
167
-
168
-
169
- def play_track_by_name(track_name):
170
- """
171
- Plays a track given its name. Uses the above function.
172
- """
173
- track_uri = find_track_by_name(track_name)
174
- track_name = sp.track(track_uri)["name"]
175
- artist_name = sp.track(track_uri)['artists'][0]['name']
176
-
177
- try:
178
- sp.start_playback(device_id=device_id, uris=[track_uri])
179
- return f"♫ Now playing {track_name} by {artist_name} ♫"
180
- except SpotifyException as e:
181
- return f"An error occurred with Spotify: {e}. \n\n**Remember to wake up Spotify.**"
182
- except Exception as e:
183
- return f"An unexpected error occurred: {e}."
184
-
185
-
186
- def queue_track_by_name(track_name):
187
- """
188
- Queues track given its name.
189
- """
190
- track_uri = find_track_by_name(track_name)
191
- track_name = sp.track(track_uri)["name"]
192
- sp.add_to_queue(uri=track_uri, device_id=device_id.value)
193
- return f"♫ Added {track_name} to your queue ♫"
194
-
195
-
196
- def pause_track():
197
- """
198
- Pauses the current playback.
199
- """
200
- sp.pause_playback(device_id=device_id.value)
201
- return "♫ Playback paused ♫"
202
-
203
-
204
- def resume_track():
205
- """
206
- Resumes the current playback.
207
- """
208
- sp.start_playback(device_id=device_id.value)
209
- return "♫ Playback started ♫"
210
-
211
-
212
- def skip_track():
213
- """
214
- Skips the current playback.
215
- """
216
- sp.next_track(device_id=device_id.value)
217
- return "♫ Skipped to your next track ♫"
218
-
219
-
220
- ### ### ### More Elaborate Functions ### ### ###
221
-
222
-
223
- def play_album_by_name_and_artist(album_name, artist_name):
224
- """
225
- Plays an album given its name and the artist.
226
- context_uri (provide a context_uri to start playback of an album, artist, or playlist) expects a string.
227
- """
228
- results = sp.search(q=f'{album_name} {artist_name}', type='album')
229
- album_id = results['albums']['items'][0]['id']
230
- album_info = sp.album(album_id)
231
- album_name = album_info['name']
232
- artist_name = album_info['artists'][0]['name']
233
-
234
- try:
235
- sp.start_playback(device_id=device_id.value, context_uri=f'spotify:album:{album_id}')
236
- return f"♫ Now playing {album_name} by {artist_name} ♫"
237
- except spotipy.SpotifyException as e:
238
- return f"An error occurred with Spotify: {e}. \n\n**Remember to wake up Spotify.**"
239
- except Timeout:
240
- return f"An unexpected error occurred: {e}."
241
-
242
-
243
- def play_playlist_by_name(playlist_name):
244
- """
245
- Plays an existing playlist in the user's library given its name.
246
- """
247
- playlists = sp.current_user_playlists()
248
- playlist_dict = {playlist['name']: (playlist['id'], playlist['owner']['display_name']) for playlist in playlists['items']}
249
- playlist_names = [key for key in playlist_dict.keys()]
250
-
251
- # defined inside to capture user-specific playlists
252
- playlist_name_embeddings = MODEL.encode(playlist_names)
253
- user_playlist_embedding = MODEL.encode([playlist_name])
254
-
255
- # compares (embedded) given name to (embedded) playlist library and outputs the closest match
256
- similarity_scores = cosine_similarity(user_playlist_embedding, playlist_name_embeddings)
257
- most_similar_index = similarity_scores.argmax()
258
- playlist_name = playlist_names[most_similar_index]
259
-
260
- try:
261
- playlist_id, creator_name = playlist_dict[playlist_name]
262
- sp.start_playback(device_id=device_id.value, context_uri=f'spotify:playlist:{playlist_id}')
263
- return f'♫ Now playing {playlist_name} by {creator_name} ♫'
264
- except:
265
- return "Unable to find playlist. Please try again."
266
-
267
-
268
- def get_track_info():
269
- """
270
- Harvests information for explain_track() using Genius' API and basic webscraping.
271
- """
272
- current_track_item = sp.current_user_playing_track()['item']
273
- track_name = current_track_item['name']
274
- artist_name = current_track_item['artists'][0]['name']
275
- album_name = current_track_item['album']['name']
276
- release_date = current_track_item['album']['release_date']
277
- basic_info = {
278
- 'track_name': track_name,
279
- 'artist_name': artist_name,
280
- 'album_name': album_name,
281
- 'release_date': release_date,
282
- }
283
-
284
- # define inside to avoid user conflicts (simultaneously query Genius)
285
- genius = lyricsgenius.Genius(GENIUS_TOKEN)
286
- # removing feature information from song titles to avoid scewing search
287
- track_name = re.split(' \(with | \(feat\. ', track_name)[0]
288
- result = genius.search_song(track_name, artist_name)
289
-
290
- # if no Genius page exists
291
- if result is not None and hasattr(result, 'artist'):
292
- genius_artist = result.artist.lower().replace(" ", "")
293
- spotify_artist = artist_name.lower().replace(" ", "")
294
- debug_print(spotify_artist)
295
- debug_print(genius_artist)
296
- if spotify_artist not in genius_artist:
297
- return basic_info, None, None, None
298
- else:
299
- genius_artist = None
300
- return basic_info, None, None, None
301
-
302
- # if Genius page exists
303
- lyrics = result.lyrics
304
- url = result.url
305
- response = requests.get(url)
306
-
307
- # parsing the webpage and locating 'About' section
308
- soup = BeautifulSoup(response.text, 'html.parser')
309
- # universal 'About' section element across all Genius song lyrics pages
310
- about_section = soup.select_one('div[class^="RichText__Container-oz284w-0"]')
311
-
312
- # if no 'About' section exists
313
- if not about_section:
314
- return basic_info, None, lyrics, url
315
-
316
- # if 'About' section exists
317
- else:
318
- about_section = about_section.get_text(separator='\n')
319
- return basic_info, about_section, lyrics, url
320
-
321
-
322
- def explain_track():
323
- """
324
- Displays track information in an organized, informational, and compelling manner.
325
- Uses the above function.
326
- """
327
- # defined inside to avoid circular importing
328
- from final_agent import LLM_STATE
329
-
330
- basic_info, about_section, lyrics, url = get_track_info()
331
- debug_print(basic_info, about_section, lyrics, url)
332
-
333
- if lyrics: # if Genius page exists
334
- system_message_content = """
335
- Your task is to create an engaging summary for a track using the available details
336
- about the track and its lyrics. If there's insufficient or no additional information
337
- besides the lyrics, craft the entire summary based solely on the lyrical content."
338
- """
339
- human_message_content = f"{about_section}\n\n{lyrics}"
340
- messages = [
341
- SystemMessage(content=system_message_content),
342
- HumanMessage(content=human_message_content)
343
- ]
344
- ai_response = LLM_STATE.value(messages).content
345
- summary = f"""
346
- **Name:** <span style="color: red; font-weight: bold; font-style: italic;">{basic_info["track_name"]}</span>
347
- **Artist:** {basic_info["artist_name"]}
348
- **Album:** {basic_info["album_name"]}
349
- **Release:** {basic_info["release_date"]}
350
-
351
- **About:**
352
- {ai_response}
353
-
354
- <a href='{url}'>Click here for more information on Genius!</a>
355
- """
356
- return summary
357
-
358
- else: # if no Genius page exists
359
- url = "https://genius.com/Genius-how-to-add-songs-to-genius-annotated"
360
- summary = f"""
361
- **Name:** <span style="color: red; font-weight: bold; font-style: italic;">{basic_info["track_name"]}</span>
362
- **Artist:** {basic_info["artist_name"]}
363
- **Album:** {basic_info["album_name"]}
364
- **Release:** {basic_info["release_date"]}
365
-
366
- **About:**
367
- Unfortunately, this track has not been uploaded to Genius.com
368
-
369
- <a href='{url}'>Be the first to change that!</a>
370
- """
371
- return summary
372
-
373
-
374
- ### ### ### Genre + Mood ### ### ###
375
-
376
-
377
- def get_user_mood(user_mood):
378
- """
379
- Categorizes the user's mood as either 'happy', 'sad', 'energetic', or 'calm'.
380
- Uses same cosine similarity/embedding concepts as with determining playlist names.
381
- """
382
- if user_mood.lower() in MOOD_LIST:
383
- user_mood = user_mood.lower()
384
- return user_mood
385
- else:
386
- user_mood_embedding = MODEL.encode([user_mood.lower()])
387
- similarity_scores = cosine_similarity(user_mood_embedding, MOOD_EMBEDDINGS)
388
- most_similar_index = similarity_scores.argmax()
389
- user_mood = MOOD_LIST[most_similar_index]
390
- return user_mood
391
-
392
-
393
- def get_genre_by_name(genre_name):
394
- """
395
- Matches user's desired genre to closest (most similar) existing genre in the list of genres.
396
- recommendations() only accepts genres from this list.
397
- """
398
- if genre_name.lower() in GENRE_LIST.value:
399
- genre_name = genre_name.lower()
400
- return genre_name
401
- else:
402
- genre_name_embedding = MODEL.encode([genre_name.lower()])
403
- similarity_scores = cosine_similarity(genre_name_embedding, GENRE_EMBEDDINGS.value)
404
- most_similar_index = similarity_scores.argmax()
405
- genre_name = GENRE_LIST.value[most_similar_index]
406
- return genre_name
407
-
408
-
409
- def is_genre_match(genre1, genre2, threshold=75):
410
- """
411
- Determines if two genres are semantically similar.
412
- token_set_ratio() - for quantifying semantic similarity - and
413
- threshold of 75 (out of 100) were were arbitrarily determined through basic testing.
414
- """
415
- score = fuzz.token_set_ratio(genre1, genre2)
416
- debug_print(score)
417
- return score >= threshold
418
-
419
-
420
- def create_track_list_str(track_uris):
421
- """
422
- Creates an organized list of track names.
423
- Used in final return statements by functions below.
424
- """
425
- track_details = sp.tracks(track_uris)
426
- track_names_with_artists = [f"{track['name']} by {track['artists'][0]['name']}" for track in track_details['tracks']]
427
- track_list_str = "<br>".join(track_names_with_artists)
428
- return track_list_str
429
-
430
-
431
- def play_genre_by_name_and_mood(genre_name, user_mood):
432
- """
433
- 1. Retrieves user's desired genre and current mood.
434
- 2. Matches genre and mood to existing options.
435
- 3. Gets 4 of user's top artists that align with genre.
436
- 4. Conducts personalized recommendations() search.
437
- 5. Plays selected track, clears the queue, and adds the rest to the now-empty queue.
438
- """
439
- genre_name = get_genre_by_name(genre_name)
440
- user_mood = get_user_mood(user_mood).lower()
441
- debug_print(genre_name)
442
- debug_print(user_mood)
443
-
444
- # increased personalization
445
- user_top_artists = sp.current_user_top_artists(limit=NUM_ARTISTS, time_range=TIME_RANGE)
446
- matching_artists_ids = []
447
-
448
- for artist in user_top_artists['items']:
449
- debug_print(artist['genres'])
450
- for artist_genre in artist['genres']:
451
- if is_genre_match(genre_name, artist_genre):
452
- matching_artists_ids.append(artist['id'])
453
- break # don't waste time cycling artist genres after match
454
- if len(matching_artists_ids) == MAX_ARTISTS:
455
- break
456
-
457
- if not matching_artists_ids:
458
- matching_artists_ids = None
459
- else:
460
- artist_names = [artist['name'] for artist in sp.artists(matching_artists_ids)['artists']]
461
- debug_print(artist_names)
462
- debug_print(matching_artists_ids)
463
-
464
- recommendations = sp.recommendations( # accepts maximum {genre + artists} = 5 seeds
465
- seed_artists=matching_artists_ids,
466
- seed_genres=[genre_name],
467
- seed_tracks=None,
468
- limit=NUM_TRACKS, # number of tracks to return
469
- country=None,
470
- **MOOD_SETTINGS[user_mood]) # maps to mood settings dictionary
471
-
472
- track_uris = [track['uri'] for track in recommendations['tracks']]
473
- track_list_str = create_track_list_str(track_uris)
474
- sp.start_playback(device_id=device_id.value, uris=track_uris)
475
-
476
- return f"""
477
- **♫ Now Playing:** <span style="color: red; font-weight: bold; font-style: italic;">{genre_name}</span> ♫
478
-
479
- **Selected Tracks:**
480
- {track_list_str}
481
- """
482
-
483
-
484
- ### ### ### Artist + Mood ### ### ###
485
-
486
-
487
- def play_artist_by_name_and_mood(artist_name, user_mood):
488
- """
489
- Plays tracks (randomly selected) by a given artist that matches the user's mood.
490
- """
491
- user_mood = get_user_mood(user_mood).lower()
492
- debug_print(user_mood)
493
-
494
- # retrieving and shuffling all artist's tracks
495
- first_name = artist_name.split(',')[0].strip()
496
- results = sp.search(q=first_name, type='artist')
497
- artist_id = results['artists']['items'][0]['id']
498
- # most recent albums retrieved first
499
- artist_albums = sp.artist_albums(artist_id, album_type='album', limit=NUM_ALBUMS)
500
- artist_tracks = []
501
- for album in artist_albums['items']:
502
- album_tracks = sp.album_tracks(album['id'])['items']
503
- artist_tracks.extend(album_tracks)
504
- random.shuffle(artist_tracks)
505
-
506
- # filtering until we find enough (MAX_TRACKS) tracks that match user's mood
507
- selected_tracks = []
508
- for track in artist_tracks:
509
- if len(selected_tracks) == MAX_TRACKS:
510
- break
511
- features = sp.audio_features([track['id']])[0]
512
- mood_criteria = MOOD_SETTINGS[user_mood]
513
-
514
- match = True
515
- for criteria, threshold in mood_criteria.items():
516
- if "min_" in criteria and features[criteria[4:]] < threshold:
517
- match = False
518
- break
519
- elif "max_" in criteria and features[criteria[4:]] > threshold:
520
- match = False
521
- break
522
- if match:
523
- debug_print(f"{features}\n{mood_criteria}\n\n")
524
- selected_tracks.append(track)
525
-
526
- track_names = [track['name'] for track in selected_tracks]
527
- track_list_str = "<br>".join(track_names) # using HTML line breaks for each track name
528
- debug_print(track_list_str)
529
- track_uris = [track['uri'] for track in selected_tracks]
530
- sp.start_playback(device_id=device_id.value, uris=track_uris)
531
-
532
- return f"""
533
- **♫ Now Playing:** <span style="color: red; font-weight: bold; font-style: italic;">{artist_name}</span> ♫
534
-
535
- **Selected Tracks:**
536
- {track_list_str}
537
- """
538
-
539
-
540
- ### ### ### Recommendations ### ### ###
541
-
542
-
543
- def recommend_tracks(genre_name=None, artist_name=None, track_name=None, user_mood=None):
544
- """
545
- 1. Retrieves user's preferences based on artist_name, track_name, genre_name, and/or user_mood.
546
- 2. Uses these parameters to conduct personalized recommendations() search.
547
- 3. Returns the track URIs of (NUM_TRACKS) recommendation tracks.
548
- """
549
- user_mood = get_user_mood(user_mood).lower() if user_mood else None
550
- debug_print(user_mood)
551
-
552
- seed_genre, seed_artist, seed_track = None, None, None
553
-
554
- if genre_name:
555
- first_name = genre_name.split(',')[0].strip()
556
- genre_name = get_genre_by_name(first_name)
557
- seed_genre = [genre_name]
558
- debug_print(seed_genre)
559
-
560
- if artist_name:
561
- first_name = artist_name.split(',')[0].strip() # if user provides multiple artists, use the first
562
- results = sp.search(q='artist:' + first_name, type='artist')
563
- seed_artist = [results['artists']['items'][0]['id']]
564
-
565
- if track_name:
566
- first_name = track_name.split(',')[0].strip()
567
- results = sp.search(q='track:' + first_name, type='track')
568
- seed_track = [results['tracks']['items'][0]['id']]
569
-
570
- # if user requests recommendations without specifying anything but their mood
571
- # this is because recommendations() requires at least one seed
572
- if seed_genre is None and seed_artist is None and seed_track is None:
573
- raise ValueError("At least one genre, artist, or track must be provided.")
574
-
575
- recommendations = sp.recommendations( # passing in 3 seeds
576
- seed_artists=seed_artist,
577
- seed_genres=seed_genre,
578
- seed_tracks=seed_track,
579
- limit=NUM_TRACKS,
580
- country=None,
581
- **MOOD_SETTINGS[user_mood] if user_mood else {})
582
-
583
- track_uris = [track['uri'] for track in recommendations['tracks']]
584
- return track_uris
585
-
586
-
587
- def play_recommended_tracks(genre_name=None, artist_name=None, track_name=None, user_mood=None):
588
- """
589
- Plays the track_uris returned by recommend_tracks().
590
- """
591
- try:
592
- track_uris = recommend_tracks(genre_name, artist_name, track_name, user_mood)
593
- track_list_str = create_track_list_str(track_uris)
594
- sp.start_playback(device_id=device_id.value, uris=track_uris)
595
-
596
- return f"""
597
- **♫ Now Playing Recommendations Based On:** <span style="color: red; font-weight: bold; font-style: italic;">
598
- {', '.join(filter(None, [genre_name, artist_name, track_name, "Your Mood"]))}</span> ♫
599
-
600
- **Selected Tracks:**
601
- {track_list_str}
602
- """
603
- except ValueError as e:
604
- return str(e)
605
-
606
-
607
- def create_playlist_from_recommendations(genre_name=None, artist_name=None, track_name=None, user_mood=None):
608
- """
609
- Creates a playlist from recommend_tracks().
610
- """
611
- user = sp.current_user()
612
- user_id = user['id']
613
- user_name = user["display_name"]
614
-
615
- playlists = sp.current_user_playlists()
616
- playlist_names = [playlist['name'] for playlist in playlists["items"]]
617
- chosen_theme = random.choice(THEMES)
618
- playlist_name = f"{user_name}'s {chosen_theme} Playlist"
619
- # ensuring the use of new adjective each time
620
- while playlist_name in playlist_names:
621
- chosen_theme = random.choice(THEMES)
622
- playlist_name = f"{user_name}'s {chosen_theme} Playlist"
623
-
624
- playlist_description=f"Apollo AI's personalized playlist for {user_name}. Get yours here: (add link)." # TODO: add link to project
625
- new_playlist = sp.user_playlist_create(user=user_id, name=playlist_name,
626
- public=True, collaborative=False, description=playlist_description)
627
-
628
- track_uris = recommend_tracks(genre_name, artist_name, track_name, user_mood)
629
- track_list_str = create_track_list_str(track_uris)
630
- sp.user_playlist_add_tracks(user=user_id, playlist_id=new_playlist['id'], tracks=track_uris, position=None)
631
- playlist_url = f"https://open.spotify.com/playlist/{new_playlist['id']}"
632
-
633
- return f"""
634
- ♫ Created *{playlist_name}* Based On: <span style='color: red; font-weight: bold; font-style: italic;'>
635
- {', '.join(filter(None, [genre_name, artist_name, track_name, 'Your Mood']))}</span> ♫
636
-
637
- **Selected Tracks:**
638
- {track_list_str}
639
-
640
- <a href='{playlist_url}'>Click here to listen to the playlist on Spotify!</a>
641
- """
642
-
643
-
644
- # ------------------------------
645
- # Section: Agent Tools
646
- # ------------------------------
647
-
648
-
649
- class TrackNameInput(BaseModel):
650
- track_name: str = Field(description="Track name in the user's request.")
651
-
652
-
653
- class AlbumNameAndArtistNameInput(BaseModel):
654
- album_name: str = Field(description="Album name in the user's request.")
655
- artist_name: str = Field(description="Artist name in the user's request.")
656
-
657
-
658
- class PlaylistNameInput(BaseModel):
659
- playlist_name: str = Field(description="Playlist name in the user's request.")
660
-
661
-
662
- class GenreNameAndUserMoodInput(BaseModel):
663
- genre_name: str = Field(description="Genre name in the user's request.")
664
- user_mood: str = Field(description="User's current mood/state-of-being.")
665
-
666
-
667
- class ArtistNameAndUserMoodInput(BaseModel):
668
- artist_name: str = Field(description="Artist name in the user's request.")
669
- user_mood: str = Field(description="User's current mood/state-of-being.")
670
-
671
-
672
- class RecommendationsInput(BaseModel):
673
- genre_name: str = Field(description="Genre name in the user's request.")
674
- artist_name: str = Field(description="Artist name in the user's request.")
675
- track_name: str = Field(description="Track name in the user's request.")
676
- user_mood: str = Field(description="User's current mood/state-of-being.")
677
-
678
-
679
- @tool("play_track_by_name", return_direct=RETURN_DIRECT, args_schema=TrackNameInput)
680
- def tool_play_track_by_name(track_name: str) -> str:
681
- """
682
- Use this tool when a user wants to play a particular track by its name.
683
- You will need to identify the track name from the user's request.
684
- Usually, the requests will look like 'play {track name}'.
685
- This tool is specifically designed for clear and accurate track requests.
686
- """
687
- return play_track_by_name(track_name)
688
-
689
-
690
- @tool("queue_track_by_name", return_direct=RETURN_DIRECT, args_schema=TrackNameInput)
691
- def tool_queue_track_by_name(track_name: str) -> str:
692
- """
693
- Always use this tool when a user says "queue" in their request.
694
- """
695
- return queue_track_by_name(track_name)
696
-
697
-
698
- @tool("pause_track", return_direct=RETURN_DIRECT)
699
- def tool_pause_track(query: str) -> str:
700
- """
701
- Always use this tool when a user says "pause" or "stop" in their request.
702
- """
703
- return pause_track()
704
-
705
-
706
- @tool("resume_track", return_direct=RETURN_DIRECT)
707
- def tool_resume_track(query: str) -> str:
708
- """
709
- Always use this tool when a user says "resume" or "unpause" in their request.
710
- """
711
- return resume_track()
712
-
713
-
714
- @tool("skip_track", return_direct=RETURN_DIRECT)
715
- def tool_skip_track(query: str) -> str:
716
- """
717
- Always use this tool when a user says "skip" or "next" in their request.
718
- """
719
- return skip_track()
720
-
721
-
722
- @tool("play_album_by_name_and_artist", return_direct=RETURN_DIRECT, args_schema=AlbumNameAndArtistNameInput)
723
- def tool_play_album_by_name_and_artist(album_name: str, artist_name: str) -> str:
724
- """
725
- Use this tool when a user wants to play an album.
726
- You will need to identify both the album name and artist name from the user's request.
727
- Usually, the requests will look like 'play the album {album_name} by {artist_name}'.
728
- """
729
- return play_album_by_name_and_artist(album_name, artist_name)
730
-
731
-
732
- @tool("play_playlist_by_name", return_direct=RETURN_DIRECT, args_schema=PlaylistNameInput)
733
- def tool_play_playlist_by_name(playlist_name: str) -> str:
734
- """
735
- Use this tool when a user wants to play one of their playlists.
736
- You will need to identify the playlist name from the user's request.
737
- """
738
- return play_playlist_by_name(playlist_name)
739
-
740
-
741
- @tool("explain_track", return_direct=RETURN_DIRECT)
742
- def tool_explain_track(query: str) -> str:
743
- """
744
- Use this tool when a user wants to know about the currently playing track.
745
- """
746
- return explain_track()
747
-
748
-
749
- @tool("play_genre_by_name_and_mood", return_direct=RETURN_DIRECT, args_schema=GenreNameAndUserMoodInput)
750
- def tool_play_genre_by_name_and_mood(genre_name: str, user_mood: str) -> str:
751
- """
752
- Use this tool when a user wants to play a genre.
753
- You will need to identify both the genre name from the user's request,
754
- and also their current mood, which you should always be monitoring.
755
- """
756
- return play_genre_by_name_and_mood(genre_name, user_mood)
757
-
758
-
759
- @tool("play_artist_by_name_and_mood", return_direct=RETURN_DIRECT, args_schema=ArtistNameAndUserMoodInput)
760
- def tool_play_artist_by_name_and_mood(artist_name: str, user_mood: str) -> str:
761
- """
762
- Use this tool when a user wants to play an artist.
763
- You will need to identify both the artist name from the user's request,
764
- and also their current mood, which you should always be monitoring.
765
- If you don't know the user's mood, ask them before using this tool.
766
- """
767
- return play_artist_by_name_and_mood(artist_name, user_mood)
768
-
769
-
770
- @tool("play_recommended_tracks", return_direct=RETURN_DIRECT, args_schema=RecommendationsInput)
771
- def tool_play_recommended_tracks(genre_name: str, artist_name: str, track_name: str, user_mood: str) -> str:
772
- """
773
- Use this tool when a user wants track recommendations.
774
- You will need to identify the genre name, artist name, and/or track name
775
- from the user's request... and also their current mood, which you should always be monitoring.
776
- The user must provide at least genre, artist, or track.
777
- """
778
- return play_recommended_tracks(genre_name, artist_name, track_name, user_mood)
779
-
780
-
781
- @tool("create_playlist_from_recommendations", return_direct=RETURN_DIRECT, args_schema=RecommendationsInput)
782
- def tool_create_playlist_from_recommendations(genre_name: str, artist_name: str, track_name: str, user_mood: str) -> str:
783
- """
784
- Use this tool when a user wants a playlist created (from recommended tracks).
785
- You will need to identify the genre name, artist name, and/or track name
786
- from the user's request... and also their current mood, which you should always be monitoring.
787
- The user must provide at least genre, artist, or track.
788
- """
789
- return create_playlist_from_recommendations(genre_name, artist_name, track_name, user_mood)
790
-
791
-
792
- CUSTOM_TOOLS =[
793
- tool_play_track_by_name,
794
- tool_queue_track_by_name,
795
- tool_pause_track,
796
- tool_resume_track,
797
- tool_skip_track,
798
- tool_play_album_by_name_and_artist,
799
- tool_play_playlist_by_name,
800
- tool_explain_track,
801
- tool_play_genre_by_name_and_mood,
802
- tool_play_artist_by_name_and_mood,
803
- tool_play_recommended_tracks,
804
- tool_create_playlist_from_recommendations
805
- ]
806
-
807
-
808
- # ------------------------------
809
- # Section: Chatbot
810
- # ------------------------------
811
-
812
-
813
- system_message = SystemMessage(content=SYSTEM_MESSAGE)
814
- MEMORY_KEY = "chat_history"
815
- prompt = OpenAIFunctionsAgent.create_prompt(
816
- system_message=system_message,
817
- extra_prompt_messages=[MessagesPlaceholder(variable_name=MEMORY_KEY)]
818
- )
819
- memory = ConversationBufferMemory(memory_key=MEMORY_KEY, return_messages=True)
820
- llm = ChatOpenAI(openai_api_key=OPENAI_API_KEY, max_retries=3, temperature=0, model=MODEL)
821
- agent = OpenAIFunctionsAgent(llm=llm, tools=CUSTOM_TOOLS, prompt=prompt)
822
- agent_executor = AgentExecutor(agent=agent, tools=CUSTOM_TOOLS, memory=memory, verbose=True)
823
-
824
- return agent_executor
825
-
826
- create_agent_button.click(create_agent, inputs=[ACCESS_TOKEN_VAR], outputs=[AGENT_EXECUTOR_VAR])
827
-
828
-
829
- # ------------------------------
830
- # Section: Chat Interface
831
- # ------------------------------
832
-
833
- chatbot = gr.Chatbot()
834
- msg = gr.Textbox()
835
-
836
- def respond(user_message, chat_history, agent_executor):
837
- bot_message = agent_executor.run(user_message)
838
- chat_history.append((user_message, bot_message))
839
- time.sleep(2)
840
- return "", chat_history
841
-
842
- msg.submit(respond, inputs=[msg, chatbot, AGENT_EXECUTOR_VAR], outputs=[msg, chatbot])
843
-
844
- auth_page.launch(share=True)