imravi commited on
Commit
c5898fb
·
verified ·
1 Parent(s): c3bb59b

Update main.py

Browse files
Files changed (1) hide show
  1. main.py +89 -31
main.py CHANGED
@@ -1,17 +1,23 @@
1
- from flask import Flask, request, jsonify
 
 
2
  import torch
3
  import numpy as np
4
  import pandas as pd
5
  from sentence_transformers import SentenceTransformer, util
6
  from transformers import AutoTokenizer, AutoModelForCausalLM
 
7
  import json
 
8
 
9
- app = Flask(__name__)
 
 
 
10
 
11
  class PlantChatbot:
12
  def __init__(self, preprocessed_data_path: str):
13
  self.device = "cuda" if torch.cuda.is_available() else "cpu"
14
- print(f"Using device: {self.device}")
15
 
16
  self.embedding_model = SentenceTransformer(
17
  model_name_or_path="all-mpnet-base-v2",
@@ -30,6 +36,9 @@ class PlantChatbot:
30
  trust_remote_code=True,
31
  torch_dtype=torch.float16
32
  )
 
 
 
33
 
34
  def load_data(self, preprocessed_data_path: str):
35
  df = pd.read_csv(preprocessed_data_path)
@@ -46,13 +55,21 @@ class PlantChatbot:
46
 
47
  df["embedding"] = df["embedding"].apply(parse_embedding)
48
  df = df.dropna(subset=['embedding'])
 
49
  self.chunks_data = df.to_dict(orient="records")
50
 
51
  embeddings_array = np.stack(df["embedding"].values)
52
- self.embeddings = torch.tensor(embeddings_array, dtype=torch.float32).to(self.device)
 
 
 
53
 
54
- def retrieve_relevant_chunks(self, query: str, n_chunks: int = 5) -> list[dict]:
55
- query_embedding = self.embedding_model.encode(query, convert_to_tensor=True).to(self.device)
 
 
 
 
56
 
57
  if len(query_embedding.shape) == 1:
58
  query_embedding = query_embedding.unsqueeze(0)
@@ -69,22 +86,41 @@ class PlantChatbot:
69
  for i in indices
70
  ]
71
 
72
- def generate_response(self, query: str):
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
73
  try:
74
  context_chunks = self.retrieve_relevant_chunks(query)
75
  prompt = self.format_prompt(query, context_chunks)
76
- inputs = self.tokenizer(prompt, return_tensors="pt", padding=True)
77
- inputs = {k: v.to(self.device) for k, v in inputs.items()}
78
-
79
- generated_text = ""
80
- max_new_tokens = 4096
81
- chunk_size = 50
82
 
 
 
83
  with torch.no_grad():
84
- for _ in range(0, max_new_tokens, chunk_size):
85
- outputs = self.model.generate(
86
- **inputs,
87
- max_new_tokens=chunk_size,
 
 
88
  do_sample=True,
89
  temperature=0.7,
90
  top_p=0.8,
@@ -92,28 +128,50 @@ class PlantChatbot:
92
  pad_token_id=self.tokenizer.pad_token_id,
93
  eos_token_id=self.tokenizer.eos_token_id,
94
  )
95
- new_tokens = outputs[0][inputs['input_ids'].shape[1]:]
96
- new_text = self.tokenizer.decode(new_tokens, skip_special_tokens=True)
97
- generated_text += new_text
98
- yield new_text
99
 
100
- inputs['input_ids'] = outputs
101
 
102
- if outputs[0][-1] == self.tokenizer.eos_token_id:
103
  break
 
 
 
 
 
 
 
104
 
105
  context_info = json.dumps({"context_items": context_chunks})
106
  yield f"\n<context>{context_info}</context>"
 
107
  except Exception as e:
108
  print(f"Error generating response: {str(e)}")
109
- yield "Error in processing your query."
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
110
 
111
- @app.route("/chat", methods=["POST"])
112
- def chat():
113
- query = request.json.get('query')
114
- chatbot = PlantChatbot("plant_data_chunks_and_embeddings.csv")
115
- response = chatbot.generate_response(query)
116
- return jsonify({"response": next(response)})
117
 
118
  if __name__ == "__main__":
119
- app.run(host='0.0.0.0', port=5000)
 
 
1
+ from fastapi import FastAPI, HTTPException
2
+ from fastapi.responses import StreamingResponse
3
+ from pydantic import BaseModel
4
  import torch
5
  import numpy as np
6
  import pandas as pd
7
  from sentence_transformers import SentenceTransformer, util
8
  from transformers import AutoTokenizer, AutoModelForCausalLM
9
+ from typing import Generator, List, Dict
10
  import json
11
+ import asyncio
12
 
13
+ app = FastAPI(title="Plant Chatbot API")
14
+
15
+ class Query(BaseModel):
16
+ query: str
17
 
18
  class PlantChatbot:
19
  def __init__(self, preprocessed_data_path: str):
20
  self.device = "cuda" if torch.cuda.is_available() else "cpu"
 
21
 
22
  self.embedding_model = SentenceTransformer(
23
  model_name_or_path="all-mpnet-base-v2",
 
36
  trust_remote_code=True,
37
  torch_dtype=torch.float16
38
  )
39
+
40
+ if self.tokenizer.pad_token is None:
41
+ self.tokenizer.pad_token = self.tokenizer.eos_token
42
 
43
  def load_data(self, preprocessed_data_path: str):
44
  df = pd.read_csv(preprocessed_data_path)
 
55
 
56
  df["embedding"] = df["embedding"].apply(parse_embedding)
57
  df = df.dropna(subset=['embedding'])
58
+
59
  self.chunks_data = df.to_dict(orient="records")
60
 
61
  embeddings_array = np.stack(df["embedding"].values)
62
+ self.embeddings = torch.tensor(
63
+ embeddings_array,
64
+ dtype=torch.float32
65
+ ).to(self.device)
66
 
67
+ def retrieve_relevant_chunks(self, query: str, n_chunks: int = 5) -> List[Dict]:
68
+ query_embedding = self.embedding_model.encode(
69
+ query,
70
+ convert_to_tensor=True,
71
+ show_progress_bar=False
72
+ ).to(self.device)
73
 
74
  if len(query_embedding.shape) == 1:
75
  query_embedding = query_embedding.unsqueeze(0)
 
86
  for i in indices
87
  ]
88
 
89
+ def format_prompt(self, query: str, context_chunks: List[Dict]) -> str:
90
+ context = "- " + "\n- ".join([chunk["sentence_chunk"] for chunk in context_chunks])
91
+
92
+ prompt = f"""<|im_start|>system
93
+ You are a knowledgeable plant expert. Provide helpful and accurate information about plants based on the given context.
94
+ <|im_end|>
95
+ <|im_start|>user
96
+ Based on the following context about plants, please answer the query.
97
+ Please be specific and detailed in your response, using only the information provided in the context.
98
+ If you cannot answer the question based on the provided context, please say so.
99
+
100
+ Context:
101
+ {context}
102
+
103
+ User query: {query}
104
+ <|im_end|>
105
+ <|im_start|>assistant
106
+ """
107
+ return prompt
108
+
109
+ async def generate_response(self, query: str) -> Generator[str, None, None]:
110
  try:
111
  context_chunks = self.retrieve_relevant_chunks(query)
112
  prompt = self.format_prompt(query, context_chunks)
113
+ input_ids = self.tokenizer.encode(prompt, return_tensors="pt").to(self.device)
 
 
 
 
 
114
 
115
+ max_length = input_ids.shape[1] + 512
116
+
117
  with torch.no_grad():
118
+ generated = input_ids
119
+
120
+ while generated.shape[1] < max_length:
121
+ outputs = self.model.forward(
122
+ input_ids=generated,
123
+ max_new_tokens=64,
124
  do_sample=True,
125
  temperature=0.7,
126
  top_p=0.8,
 
128
  pad_token_id=self.tokenizer.pad_token_id,
129
  eos_token_id=self.tokenizer.eos_token_id,
130
  )
 
 
 
 
131
 
132
+ next_token = torch.argmax(outputs.logits[:, -1, :], dim=-1).unsqueeze(0)
133
 
134
+ if next_token[0, 0].item() == self.tokenizer.eos_token_id:
135
  break
136
+
137
+ generated = torch.cat([generated, next_token.T], dim=1)
138
+ new_text = self.tokenizer.decode(next_token[0], skip_special_tokens=True)
139
+
140
+ if new_text:
141
+ yield new_text
142
+ await asyncio.sleep(0) # Allow other tasks to run
143
 
144
  context_info = json.dumps({"context_items": context_chunks})
145
  yield f"\n<context>{context_info}</context>"
146
+
147
  except Exception as e:
148
  print(f"Error generating response: {str(e)}")
149
+ yield "I apologize, but I encountered an error while processing your query. Please try again."
150
+
151
+ # Initialize chatbot at startup
152
+ chatbot = PlantChatbot("/home/avinashhn/bheri_bot/ravi/new/plant_data_chunks_and_embeddings.csv")
153
+
154
+ async def response_generator(query: str):
155
+ """Wrapper generator for streaming response"""
156
+ async for chunk in chatbot.generate_response(query):
157
+ yield f"data: {chunk}\n\n"
158
+
159
+ @app.post("/chat")
160
+ async def chat(query: Query):
161
+ """Endpoint for chat interactions"""
162
+ try:
163
+ return StreamingResponse(
164
+ response_generator(query.query),
165
+ media_type="text/event-stream"
166
+ )
167
+ except Exception as e:
168
+ raise HTTPException(status_code=500, detail=str(e))
169
 
170
+ @app.get("/")
171
+ async def root():
172
+ """Root endpoint"""
173
+ return {"message": "Plant Chatbot API is running. Use /chat endpoint for queries."}
 
 
174
 
175
  if __name__ == "__main__":
176
+ import uvicorn
177
+ uvicorn.run(app, host="0.0.0.0", port=5000)