Omkar192002 commited on
Commit
e6bfb30
·
verified ·
1 Parent(s): c797940

adding api

Browse files
Files changed (3) hide show
  1. Dockerfile +23 -0
  2. embed_data.py +124 -0
  3. main.py +208 -0
Dockerfile ADDED
@@ -0,0 +1,23 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ FROM python:3.9-slim
2
+
3
+ RUN useradd -m -u 1000 user
4
+ USER user
5
+ ENV PATH="/home/user/.local/bin:$PATH"
6
+
7
+ WORKDIR /app
8
+
9
+ # Copy requirements first to leverage Docker cache
10
+ COPY --chown=user ./requirements.txt requirements.txt
11
+ RUN pip install --no-cache-dir --upgrade -r requirements.txt
12
+
13
+ # Copy the application code
14
+ COPY --chown=user . /app
15
+
16
+ # Set environment variables
17
+ ENV PORT=8000
18
+
19
+ # Expose the port
20
+ EXPOSE ${PORT}
21
+
22
+ # Run the application
23
+ CMD ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "7860"]
embed_data.py ADDED
@@ -0,0 +1,124 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import json
2
+ import os
3
+ from pinecone import Pinecone, ServerlessSpec
4
+ import numpy as np
5
+ from openai import OpenAI
6
+
7
+ # Load environment variables
8
+ from dotenv import load_dotenv
9
+ load_dotenv()
10
+
11
+ # Get API keys from environment variables
12
+ PINECONE_API_KEY = os.getenv('PINECONE_API_KEY')
13
+ OPENAI_API_KEY = os.getenv('OPENAI_API_KEY')
14
+
15
+ if not PINECONE_API_KEY:
16
+ raise ValueError("PINECONE_API_KEY environment variable not set")
17
+ if not OPENAI_API_KEY:
18
+ raise ValueError("OPENAI_API_KEY environment variable not set")
19
+
20
+ # Initialize OpenAI client
21
+ openai_client = OpenAI(api_key=OPENAI_API_KEY)
22
+
23
+ # Define the embedding model using OpenAI
24
+ class OpenAIEmbedder:
25
+ def __init__(self, model_name="text-embedding-3-small"):
26
+ self.model_name = model_name
27
+ self.client = openai_client
28
+ self.embedding_dimension = 1536 # Dimension of text-embedding-3-small
29
+
30
+ def encode(self, texts):
31
+ if isinstance(texts, str):
32
+ texts = [texts]
33
+
34
+ # Get embeddings from OpenAI
35
+ response = self.client.embeddings.create(
36
+ input=texts,
37
+ model=self.model_name
38
+ )
39
+
40
+ # Extract embeddings from response
41
+ embeddings = [item.embedding for item in response.data]
42
+ return np.array(embeddings)
43
+
44
+ # Initialize Pinecone client
45
+ def initialize_pinecone():
46
+ pc = Pinecone(api_key=PINECONE_API_KEY)
47
+
48
+ # Define index name
49
+ index_name = "ebikes-search"
50
+
51
+ # Check if index already exists
52
+ existing_indexes = pc.list_indexes().names()
53
+ if index_name not in existing_indexes:
54
+ # Create index with 1536 dimensions (matches text-embedding-3-small)
55
+ pc.create_index(
56
+ name=index_name,
57
+ dimension=1536,
58
+ metric="cosine",
59
+ spec=ServerlessSpec(cloud="aws", region="us-west-2")
60
+ )
61
+ print(f"Created new index: {index_name}")
62
+
63
+ # Connect to the index
64
+ index = pc.Index(index_name)
65
+ return index
66
+
67
+ # Load the e-bikes data
68
+ def load_ebikes_data(file_path="ebikes_data.json"):
69
+ with open(file_path, 'r') as f:
70
+ data = json.load(f)
71
+ return data.get('ebikes', [])
72
+
73
+ # Create embeddings and upload to Pinecone
74
+ def create_and_upload_embeddings(ebikes_data, encoder, pinecone_index):
75
+ # Prepare data for indexing
76
+ ids = []
77
+ descriptions = []
78
+ metadata = []
79
+
80
+ for bike in ebikes_data:
81
+ ids.append(bike['id'])
82
+ descriptions.append(bike['description'])
83
+ metadata.append({
84
+ 'id': bike['id'],
85
+ 'name': bike['name'],
86
+ 'type': bike['type'],
87
+ 'description': bike['description'][:100] + "..." # Adding truncated description to metadata
88
+ })
89
+
90
+ # Create embeddings
91
+ embeddings = encoder.encode(descriptions)
92
+
93
+ # Prepare vectors for Pinecone
94
+ vectors_to_upsert = []
95
+ for i in range(len(ids)):
96
+ vector = {
97
+ 'id': ids[i],
98
+ 'values': embeddings[i].tolist(),
99
+ 'metadata': metadata[i]
100
+ }
101
+ vectors_to_upsert.append(vector)
102
+
103
+ # Upsert vectors to Pinecone
104
+ pinecone_index.upsert(vectors=vectors_to_upsert)
105
+ print(f"Uploaded {len(vectors_to_upsert)} embeddings to Pinecone")
106
+
107
+ # Main function to run the embedding creation process
108
+ def main():
109
+ # Initialize the embedding model
110
+ encoder = OpenAIEmbedder()
111
+
112
+ # Initialize Pinecone
113
+ pinecone_index = initialize_pinecone()
114
+
115
+ # Load ebikes data
116
+ ebikes_data = load_ebikes_data()
117
+
118
+ # Create and upload embeddings
119
+ create_and_upload_embeddings(ebikes_data, encoder, pinecone_index)
120
+
121
+ print("Embedding creation and upload completed successfully!")
122
+
123
+ if __name__ == "__main__":
124
+ main()
main.py ADDED
@@ -0,0 +1,208 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import json
2
+ import os
3
+ from typing import List, Dict, Any, Optional
4
+ from pydantic import BaseModel
5
+ import uvicorn
6
+ from fastapi import FastAPI, HTTPException
7
+ from pinecone import Pinecone , ServerlessSpec
8
+ import numpy as np
9
+ from openai import OpenAI
10
+
11
+ # Load environment variables
12
+ from dotenv import load_dotenv
13
+ load_dotenv()
14
+
15
+ # Get API keys from environment variables
16
+ PINECONE_API_KEY = os.getenv('PINECONE_API_KEY')
17
+ OPENAI_API_KEY = os.getenv('OPENAI_API_KEY')
18
+
19
+ if not PINECONE_API_KEY:
20
+ raise ValueError("PINECONE_API_KEY environment variable not set")
21
+ if not OPENAI_API_KEY:
22
+ raise ValueError("OPENAI_API_KEY environment variable not set")
23
+
24
+ # Create FastAPI app
25
+ app = FastAPI(title="E-Bikes Semantic Search API",
26
+ description="API for finding similar e-bikes based on semantic search",
27
+ version="1.0.0")
28
+
29
+ # Request and response models
30
+ class SearchRequest(BaseModel):
31
+ description: str
32
+ top_k: int = 3
33
+
34
+ class BikeMatch(BaseModel):
35
+ id: str
36
+ name: str
37
+ type: str
38
+ description: str
39
+ score: float
40
+
41
+ class SearchResponse(BaseModel):
42
+ matches: List[BikeMatch]
43
+
44
+ # Initialize OpenAI client
45
+ openai_client = OpenAI(api_key=OPENAI_API_KEY)
46
+
47
+ # Define the embedding model using OpenAI
48
+ class OpenAIEmbedder:
49
+ def __init__(self, model_name="text-embedding-3-small"):
50
+ self.model_name = model_name
51
+ self.client = openai_client
52
+ self.embedding_dimension = 1536 # Dimension of text-embedding-3-small
53
+
54
+ def encode(self, texts):
55
+ if isinstance(texts, str):
56
+ texts = [texts]
57
+
58
+ # Get embeddings from OpenAI
59
+ response = self.client.embeddings.create(
60
+ input=texts,
61
+ model=self.model_name
62
+ )
63
+
64
+ # Extract embeddings from response
65
+ embeddings = [item.embedding for item in response.data]
66
+ return np.array(embeddings)
67
+
68
+ # Initialize Pinecone client
69
+ def initialize_pinecone():
70
+ pc = Pinecone(api_key=PINECONE_API_KEY)
71
+
72
+ # Define index name
73
+ index_name = "ebikes-search"
74
+
75
+ # Check if index already exists
76
+ existing_indexes = pc.list_indexes().names()
77
+ if index_name not in existing_indexes:
78
+ # Create index with 1536 dimensions (matches text-embedding-3-small)
79
+ pc.create_index(
80
+ name=index_name,
81
+ dimension=1536,
82
+ metric="cosine",
83
+ spec=ServerlessSpec(cloud="aws", region="us-east-1")
84
+ )
85
+ print(f"Created new index: {index_name}")
86
+
87
+ # Connect to the index
88
+ try:
89
+ index = pc.Index(index_name)
90
+ return index
91
+ except Exception as e:
92
+ print(f"Error connecting to Pinecone index: {e}")
93
+ raise
94
+
95
+ # Load the e-bikes data
96
+ def load_ebikes_data(file_path="data.json"):
97
+ try:
98
+ with open(file_path, 'r') as f:
99
+ data = json.load(f)
100
+ return data.get('ebikes', [])
101
+ except Exception as e:
102
+ print(f"Error loading e-bikes data: {e}")
103
+ return []
104
+
105
+ # Create embeddings and upload to Pinecone
106
+ def create_and_upload_embeddings(ebikes_data, encoder, pinecone_index):
107
+ # Prepare data for indexing
108
+ ids = []
109
+ descriptions = []
110
+ metadata = []
111
+
112
+ for bike in ebikes_data:
113
+ ids.append(bike['id'])
114
+ descriptions.append(bike['description'])
115
+ metadata.append({
116
+ 'id': bike['id'],
117
+ 'name': bike['name'],
118
+ 'type': bike['type'],
119
+ 'description': bike['description']
120
+ })
121
+
122
+ # Create embeddings
123
+ embeddings = encoder.encode(descriptions)
124
+
125
+ # Prepare vectors for Pinecone
126
+ vectors_to_upsert = []
127
+ for i in range(len(ids)):
128
+ vector = {
129
+ 'id': ids[i],
130
+ 'values': embeddings[i].tolist(),
131
+ 'metadata': metadata[i]
132
+ }
133
+ vectors_to_upsert.append(vector)
134
+
135
+ # Upsert vectors to Pinecone
136
+ pinecone_index.upsert(vectors=vectors_to_upsert)
137
+ print(f"Uploaded {len(vectors_to_upsert)} embeddings to Pinecone")
138
+
139
+ # Global variables for model and Pinecone index
140
+ encoder = None
141
+ pinecone_index = None
142
+
143
+ # Initialize data at startup
144
+ @app.on_event("startup")
145
+ async def startup_event():
146
+ global encoder, pinecone_index
147
+
148
+ print("Initializing OpenAI embedder...")
149
+ encoder = OpenAIEmbedder()
150
+
151
+ print("Connecting to Pinecone...")
152
+ pinecone_index = initialize_pinecone()
153
+
154
+ print("Loading e-bikes data...")
155
+ ebikes_data = load_ebikes_data("data.json")
156
+
157
+ if not ebikes_data:
158
+ print("No e-bikes data found, skipping embedding creation")
159
+ return
160
+
161
+ print("Creating and uploading embeddings...")
162
+ create_and_upload_embeddings(ebikes_data, encoder, pinecone_index)
163
+
164
+ print("API startup completed successfully!")
165
+
166
+ @app.get("/health")
167
+ async def health_check():
168
+ """Health check endpoint"""
169
+ return {"status": "healthy"}
170
+
171
+ @app.post("/search", response_model=SearchResponse)
172
+ async def search_ebikes(request: SearchRequest):
173
+ """
174
+ Search for e-bikes similar to the provided description
175
+
176
+ This endpoint uses semantic search to find e-bikes that match the user's description.
177
+ """
178
+ try:
179
+ # Create embedding for the query
180
+ query_embedding = encoder.encode(request.description)[0]
181
+
182
+ # Query Pinecone
183
+ results = pinecone_index.query(
184
+ vector=query_embedding.tolist(),
185
+ top_k=request.top_k,
186
+ include_metadata=True
187
+ )
188
+
189
+ # Parse results
190
+ matches = []
191
+ for match in results.matches:
192
+ bike_match = BikeMatch(
193
+ id=match.metadata.get('id'),
194
+ name=match.metadata.get('name'),
195
+ type=match.metadata.get('type'),
196
+ description=match.metadata.get('description'),
197
+ score=float(match.score)
198
+ )
199
+ matches.append(bike_match)
200
+
201
+ return SearchResponse(matches=matches)
202
+
203
+ except Exception as e:
204
+ print(f"Error during search: {e}")
205
+ raise HTTPException(status_code=500, detail=f"Search failed: {str(e)}")
206
+
207
+ if __name__ == "__main__":
208
+ uvicorn.run("main:app", host="0.0.0.0", port=8000, reload=True)