James Edmunds commited on
Commit
d147321
·
1 Parent(s): 0cc0152

Checkpoint! local working probably. new embeddings, added to readme, additional scripts, updated process_lyrics and upload_embeddings and added some testscripts.

Browse files
config/settings.py CHANGED
@@ -29,7 +29,7 @@ class Settings:
29
  LLM_MODEL = "gpt-4"
30
 
31
  # ChromaDB Settings
32
- CHROMA_COLLECTION_NAME = "langchain"
33
 
34
  @classmethod
35
  def is_huggingface(cls) -> bool:
 
29
  LLM_MODEL = "gpt-4"
30
 
31
  # ChromaDB Settings
32
+ CHROMA_COLLECTION_NAME = "lyrics_v1"
33
 
34
  @classmethod
35
  def is_huggingface(cls) -> bool:
docs/PROJECT_README.md CHANGED
@@ -70,4 +70,56 @@ python scripts/test_embeddings.py
70
  python scripts/test_semantic.py
71
  ```
72
  !
73
- For troubleshooting and known issues, see TROUBLESHOOTING.md
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
70
  python scripts/test_semantic.py
71
  ```
72
  !
73
+ For troubleshooting and known issues, see TROUBLESHOOTING.md
74
+
75
+ ## Data Processing & Loading Workflow
76
+
77
+ ### 1. Lyrics Processing (`scripts/process_lyrics.py`)
78
+ - Handles initial lyrics processing and embedding creation
79
+ - Features:
80
+ - Batch processing with rate limiting
81
+ - Recursive text splitting (300 chars, 75 overlap)
82
+ - Automatic retry on API limits
83
+ - Comprehensive metadata tracking
84
+ - Progress monitoring with tqdm
85
+
86
+ ### 2. Data Loading (`src/utils/data_loader.py`)
87
+ - Manages raw lyrics loading and cleaning
88
+ - Features:
89
+ - Multi-encoding support (utf-8, latin-1, cp1252)
90
+ - Section marker detection ([Verse], [Chorus], etc.)
91
+ - Intelligent line breaking
92
+ - Metadata preservation (artist, song title)
93
+ - Robust validation checks
94
+
95
+ ### 3. Workflow Steps
96
+ ```bash
97
+ # 1. Load and clean lyrics
98
+ python scripts/process_lyrics.py
99
+ # Creates cleaned, chunked documents with metadata
100
+
101
+ # 2. Generate embeddings
102
+ # (Handled automatically by process_lyrics.py)
103
+ # Stores in data/processed/embeddings/chroma
104
+
105
+ # 3. Upload to HuggingFace
106
+ python scripts/upload_embeddings.py
107
+ # Verifies and uploads to SongLift/LyrGen2_DB
108
+ ```
109
+
110
+ ### Data Flow
111
+ ```
112
+ Raw Lyrics (txt)
113
+
114
+ Data Loader (cleaning)
115
+
116
+ Text Splitter (chunking)
117
+
118
+ OpenAI Embeddings
119
+
120
+ ChromaDB Storage
121
+
122
+ HuggingFace Dataset
123
+ ```
124
+
125
+ Each step includes validation and error handling to ensure data integrity. The process is designed to be resumable and maintains consistency between local and deployed environments.
scripts/check_collections.py ADDED
@@ -0,0 +1,63 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import sqlite3
2
+ from pathlib import Path
3
+ import sys
4
+
5
+ # Add project root to path
6
+ project_root = Path(__file__).parent.parent
7
+ sys.path.append(str(project_root))
8
+
9
+ from config.settings import Settings
10
+
11
+ def check_collections():
12
+ """Check collections in ChromaDB"""
13
+ chroma_path = Settings.get_chroma_path()
14
+ sqlite_file = chroma_path / "chroma.sqlite3"
15
+
16
+ print(f"\nExamining: {sqlite_file}")
17
+ conn = sqlite3.connect(sqlite_file)
18
+ cursor = conn.cursor()
19
+
20
+ try:
21
+ # List collections
22
+ cursor.execute("SELECT name, id FROM collections;")
23
+ collections = cursor.fetchall()
24
+
25
+ print("\nCollections found:")
26
+ for name, coll_id in collections:
27
+ print(f"Name: {name}")
28
+ print(f"ID: {coll_id}")
29
+
30
+ # Get count of embeddings using correct schema
31
+ try:
32
+ cursor.execute("""
33
+ SELECT COUNT(*)
34
+ FROM embeddings e
35
+ JOIN segments s ON e.segment_id = s.id
36
+ WHERE s.collection = ?
37
+ """, (coll_id,))
38
+ count = cursor.fetchone()[0]
39
+ print(f"Embeddings count: {count}")
40
+ except sqlite3.OperationalError as e:
41
+ print(f"Could not get count: {e}")
42
+
43
+ # Get metadata using correct schema
44
+ try:
45
+ cursor.execute("""
46
+ SELECT key, str_value
47
+ FROM collection_metadata
48
+ WHERE collection_id = ?
49
+ """, (coll_id,))
50
+ metadata = cursor.fetchall()
51
+ if metadata:
52
+ print("Collection Metadata:")
53
+ for key, value in metadata:
54
+ print(f" {key}: {value}")
55
+ except sqlite3.OperationalError as e:
56
+ print(f"Could not get metadata: {e}")
57
+ print()
58
+
59
+ finally:
60
+ conn.close()
61
+
62
+ if __name__ == "__main__":
63
+ check_collections()
scripts/cleanup_old_embeddings.py ADDED
@@ -0,0 +1,53 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import sqlite3
2
+ import shutil
3
+ from pathlib import Path
4
+ import sys
5
+
6
+ # Add project root to path
7
+ project_root = Path(__file__).parent.parent
8
+ sys.path.append(str(project_root))
9
+
10
+ from config.settings import Settings
11
+
12
+
13
+ def cleanup_old_embeddings():
14
+ """Clean up old embedding directories"""
15
+ chroma_path = Settings.get_chroma_path()
16
+
17
+ # List all UUID directories
18
+ collection_dirs = list(chroma_path.glob("*-*-*-*-*"))
19
+
20
+ print("\nFound collection directories:")
21
+ for dir_path in collection_dirs:
22
+ print(f"- {dir_path.name}")
23
+
24
+ # Get current collection info from database
25
+ sqlite_file = chroma_path / "chroma.sqlite3"
26
+ conn = sqlite3.connect(sqlite_file)
27
+ cursor = conn.cursor()
28
+
29
+ try:
30
+ # Get all active collection IDs
31
+ cursor.execute("SELECT id FROM collections")
32
+ active_ids = {row[0] for row in cursor.fetchall()}
33
+ print("\nActive collection IDs:")
34
+ for id in active_ids:
35
+ print(f"- {id}")
36
+
37
+ # Find directories that don't match any active collection
38
+ for dir_path in collection_dirs:
39
+ if dir_path.name not in active_ids:
40
+ print(f"\nFound unused collection directory: {dir_path.name}")
41
+ response = input("Delete this directory? (y/N): ")
42
+ if response.lower() == 'y':
43
+ shutil.rmtree(dir_path)
44
+ print(f"Deleted: {dir_path}")
45
+ else:
46
+ print("Skipped deletion")
47
+
48
+ finally:
49
+ conn.close()
50
+
51
+
52
+ if __name__ == "__main__":
53
+ cleanup_old_embeddings()
scripts/process_lyrics.py CHANGED
@@ -3,6 +3,8 @@ import sys
3
  import time
4
  from datetime import datetime
5
  from pathlib import Path
 
 
6
 
7
  from langchain.text_splitter import RecursiveCharacterTextSplitter
8
  from langchain_community.vectorstores import Chroma
@@ -35,7 +37,9 @@ class LyricsProcessor:
35
  self.output_dir = Path(output_dir)
36
  self.batch_size = batch_size
37
  self.embeddings = OpenAIEmbeddings()
38
- self.collection_name = "langchain"
 
 
39
 
40
  # Configure text splitter for lyrics
41
  self.text_splitter = RecursiveCharacterTextSplitter(
@@ -83,6 +87,32 @@ class LyricsProcessor:
83
  print("Validating configuration...")
84
  self.validate_text_splitter()
85
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
86
  # Load all lyrics documents
87
  print("Loading lyrics files...")
88
  documents = self.loader.load_lyrics()
 
3
  import time
4
  from datetime import datetime
5
  from pathlib import Path
6
+ import sqlite3
7
+ import shutil
8
 
9
  from langchain.text_splitter import RecursiveCharacterTextSplitter
10
  from langchain_community.vectorstores import Chroma
 
37
  self.output_dir = Path(output_dir)
38
  self.batch_size = batch_size
39
  self.embeddings = OpenAIEmbeddings()
40
+ self.collection_name = Settings.CHROMA_COLLECTION_NAME
41
+
42
+ print(f"Using collection name: {self.collection_name}")
43
 
44
  # Configure text splitter for lyrics
45
  self.text_splitter = RecursiveCharacterTextSplitter(
 
87
  print("Validating configuration...")
88
  self.validate_text_splitter()
89
 
90
+ # Check for existing collection
91
+ chroma_dir = Path(self.output_dir) / "chroma"
92
+ if chroma_dir.exists():
93
+ sqlite_file = chroma_dir / "chroma.sqlite3"
94
+ if sqlite_file.exists():
95
+ try:
96
+ conn = sqlite3.connect(sqlite_file)
97
+ cursor = conn.cursor()
98
+ cursor.execute("SELECT name FROM collections WHERE name = ?",
99
+ (self.collection_name,))
100
+ if cursor.fetchone():
101
+ response = input(
102
+ f"\nWarning: Collection '{self.collection_name}' already exists.\n"
103
+ "Do you want to delete and recreate? (y/N): "
104
+ )
105
+ if response.lower() != 'y':
106
+ print("Aborting.")
107
+ return
108
+ print("Removing existing collection...")
109
+ shutil.rmtree(chroma_dir)
110
+ chroma_dir.mkdir(parents=True)
111
+ conn.close()
112
+ except Exception as e:
113
+ print(f"Error checking existing collection: {e}")
114
+ print("Continuing with processing...")
115
+
116
  # Load all lyrics documents
117
  print("Loading lyrics files...")
118
  documents = self.loader.load_lyrics()
scripts/upload_embeddings.py CHANGED
@@ -2,8 +2,7 @@
2
  import sys
3
  from pathlib import Path
4
  from huggingface_hub import HfApi
5
- import os
6
- import shutil
7
 
8
  # Add parent directory to path to locate 'config' module
9
  sys.path.append(str(Path(__file__).parent.parent))
@@ -31,41 +30,86 @@ def verify_space_access():
31
  print(f"Error verifying space access: {str(e)}")
32
  return False
33
 
34
- def main():
35
- """Upload embeddings directory to HuggingFace Space using HfApi.upload_folder"""
36
- if not Settings.HF_TOKEN:
37
- raise ValueError("HF_TOKEN not found in environment variables")
38
-
39
- print("Verifying Space access...")
40
- if not verify_space_access():
41
- raise RuntimeError("Failed to verify Space access")
42
-
43
- print("Starting upload process...")
44
 
45
- # Calculate total size of embeddings
46
- total_size = sum(
47
- f.stat().st_size for f in Settings.EMBEDDINGS_DIR.glob('**/*') if f.is_file()
48
- )
49
- print(f"Found embeddings: {total_size / 1024 / 1024:.2f} MB")
50
 
51
- api = HfApi(token=Settings.HF_TOKEN)
 
 
52
 
53
- print(f"Uploading to Space: {Settings.HF_SPACE}...")
54
  try:
55
- # Upload using upload_folder
56
- api.upload_folder(
57
- folder_path=str(Settings.EMBEDDINGS_DIR),
58
- repo_id=Settings.HF_SPACE,
59
- repo_type="space",
60
- ignore_patterns=["*.pyc", "__pycache__/", ".DS_Store"]
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
61
  )
62
- print("Upload complete!")
63
- except Exception as e:
64
- print(f"Error during upload: {str(e)}")
65
- if "storage" in str(e).lower():
66
- print("\nNOTE: This might be a Git LFS limitation.")
67
- print("Consider splitting the upload into smaller chunks or using the Hugging Face web interface.")
68
- raise
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
69
 
70
  if __name__ == "__main__":
71
- main()
 
2
  import sys
3
  from pathlib import Path
4
  from huggingface_hub import HfApi
5
+ import sqlite3
 
6
 
7
  # Add parent directory to path to locate 'config' module
8
  sys.path.append(str(Path(__file__).parent.parent))
 
30
  print(f"Error verifying space access: {str(e)}")
31
  return False
32
 
33
+ def verify_and_upload():
34
+ """Verify local embeddings and upload to HuggingFace"""
35
+ local_chroma = Settings.get_chroma_path()
 
 
 
 
 
 
 
36
 
37
+ # Verify local files first
38
+ print("\nVerifying local embeddings...")
39
+ sqlite_file = local_chroma / "chroma.sqlite3"
40
+ if not sqlite_file.exists():
41
+ raise RuntimeError(f"SQLite database not found at {sqlite_file}")
42
 
43
+ # Get collection info
44
+ conn = sqlite3.connect(sqlite_file)
45
+ cursor = conn.cursor()
46
 
 
47
  try:
48
+ # Verify collection
49
+ cursor.execute("SELECT name, id FROM collections;")
50
+ collections = cursor.fetchall()
51
+
52
+ if not collections:
53
+ raise RuntimeError("No collections found in database")
54
+
55
+ print("\nCollections in database:")
56
+ for name, coll_id in collections:
57
+ # Get embeddings count
58
+ cursor.execute("""
59
+ SELECT COUNT(*)
60
+ FROM embeddings e
61
+ JOIN segments s ON e.segment_id = s.id
62
+ WHERE s.collection = ?
63
+ """, (coll_id,))
64
+ count = cursor.fetchone()[0]
65
+ print(f"Collection: {name}")
66
+ print(f"ID: {coll_id}")
67
+ print(f"Embeddings: {count}")
68
+
69
+ # Verify expected collection exists
70
+ collection_names = [c[0] for c in collections]
71
+ if Settings.CHROMA_COLLECTION_NAME not in collection_names:
72
+ raise RuntimeError(
73
+ f"Expected collection '{Settings.CHROMA_COLLECTION_NAME}' not found. "
74
+ f"Found: {collection_names}"
75
+ )
76
+
77
+ # Calculate total size
78
+ total_size = sum(
79
+ f.stat().st_size for f in local_chroma.glob('**/*') if f.is_file()
80
  )
81
+ print(f"\nTotal size to upload: {total_size / (1024*1024):.2f} MB")
82
+
83
+ # Confirm before upload
84
+ response = input("\nReady to upload to HuggingFace. Continue? (y/N): ")
85
+ if response.lower() != 'y':
86
+ print("Upload cancelled.")
87
+ return
88
+
89
+ print("\nUploading to HuggingFace...")
90
+ api = HfApi(token=Settings.HF_TOKEN)
91
+
92
+ try:
93
+ # Upload files to the existing repo
94
+ api.upload_folder(
95
+ folder_path=str(local_chroma),
96
+ repo_id=Settings.HF_DATASET,
97
+ repo_type="dataset",
98
+ path_in_repo="chroma",
99
+ ignore_patterns=["*.pyc", "__pycache__/", ".DS_Store"]
100
+ )
101
+ print("Upload complete!")
102
+
103
+ except Exception as e:
104
+ print(f"Error during upload: {e}")
105
+ print("\nNOTE: If this was a storage limitation error:")
106
+ print("1. Verify Git LFS is enabled")
107
+ print("2. Check HuggingFace storage quota")
108
+ print("3. Ensure all files are under size limits")
109
+ raise
110
+
111
+ finally:
112
+ conn.close()
113
 
114
  if __name__ == "__main__":
115
+ verify_and_upload()