Pranesh64 commited on
Commit
170ccaf
·
verified ·
1 Parent(s): 634387d

Create backend/llm.py

Browse files
Files changed (1) hide show
  1. backend/llm.py +192 -0
backend/llm.py ADDED
@@ -0,0 +1,192 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ LLM module for generating answers using Azure OpenAI.
3
+ """
4
+
5
+ import os
6
+ from typing import List, Dict
7
+ from openai import AzureOpenAI
8
+ from dotenv import load_dotenv
9
+
10
+ # Load environment variables
11
+ load_dotenv()
12
+
13
+
14
+ class LLMClient:
15
+ """Wrapper for Azure OpenAI API."""
16
+
17
+ def __init__(self):
18
+ """Initialize Azure OpenAI client."""
19
+ self.api_key = os.getenv('AZURE_OPENAI_API_KEY', '')
20
+ self.endpoint = os.getenv('AZURE_OPENAI_ENDPOINT', '')
21
+ self.api_version = os.getenv('AZURE_OPENAI_VERSION', '2024-02-01')
22
+ self.deployment_name = os.getenv('AZURE_OPENAI_DEPLOYMENT', '')
23
+
24
+ self.client = None
25
+
26
+ if self.has_token():
27
+ try:
28
+ self.client = AzureOpenAI(
29
+ api_key=self.api_key,
30
+ azure_endpoint=self.endpoint,
31
+ api_version=self.api_version,
32
+ )
33
+ print("✅ Azure OpenAI client initialized successfully")
34
+ except Exception as e:
35
+ print(f"❌ Failed to initialize Azure OpenAI client: {str(e)}")
36
+ self.client = None
37
+ else:
38
+ print("⚠️ Azure OpenAI credentials not found. Using extractive fallback.")
39
+
40
+ def has_token(self) -> bool:
41
+ """Check if Azure OpenAI credentials are available."""
42
+ return bool(self.api_key and self.endpoint and self.deployment_name)
43
+
44
+ def generate_answer(self, question: str, context_chunks: List[Dict], max_tokens: int = 800) -> str:
45
+ """
46
+ Generate answer using Azure OpenAI with context.
47
+
48
+ Args:
49
+ question: User question
50
+ context_chunks: List of retrieved context chunks
51
+ max_tokens: Maximum response length
52
+
53
+ Returns:
54
+ Generated answer text
55
+ """
56
+ if not context_chunks:
57
+ return "No relevant context found. Please index some documents first."
58
+
59
+ # Format context from chunks
60
+ context_parts = []
61
+ for i, chunk in enumerate(context_chunks, 1):
62
+ source = chunk.get('source', 'Unknown')
63
+ text = chunk.get('text', '')
64
+ context_parts.append(f"[Source {i}: {source}]\n{text}")
65
+
66
+ context = "\n\n".join(context_parts)
67
+
68
+ # Generate answer using Azure OpenAI
69
+ if self.client:
70
+ try:
71
+ messages = [
72
+ {
73
+ "role": "system",
74
+ "content": """You are a helpful research assistant. Use ONLY the provided context to answer questions accurately and comprehensively.
75
+
76
+ Guidelines:
77
+ - Base your answer strictly on the provided context
78
+ - If the context doesn't contain enough information, clearly state this
79
+ - Cite sources when possible
80
+ - Provide detailed, well-structured answers
81
+ - If multiple sources contain relevant information, synthesize them coherently"""
82
+ },
83
+ {
84
+ "role": "user",
85
+ "content": f"""Question: {question}
86
+
87
+ Context:
88
+ {context}
89
+
90
+ Please provide a comprehensive answer based on the context above."""
91
+ }
92
+ ]
93
+
94
+ response = self.client.chat.completions.create(
95
+ model=self.deployment_name,
96
+ messages=messages,
97
+ max_tokens=max_tokens,
98
+ temperature=0.3, # Lower temperature for more focused answers
99
+ top_p=0.9,
100
+ frequency_penalty=0.1,
101
+ presence_penalty=0.1
102
+ )
103
+
104
+ if response.choices and response.choices[0].message:
105
+ answer = response.choices[0].message.content
106
+ return answer.strip() if answer else "No answer generated."
107
+ else:
108
+ return "No response from Azure OpenAI."
109
+
110
+ except Exception as e:
111
+ error_msg = str(e)
112
+ if "rate limit" in error_msg.lower():
113
+ return "⚠️ Rate limit exceeded. Please try again in a moment."
114
+ elif "content filter" in error_msg.lower():
115
+ return "⚠️ Content filtered by Azure OpenAI. Please try rephrasing your question."
116
+ elif "timeout" in error_msg.lower():
117
+ return "⚠️ Request timed out. Please try again."
118
+ else:
119
+ return f"❌ Error generating answer: {error_msg}"
120
+ else:
121
+ # Fallback: extractive answer from context
122
+ return self._extractive_fallback(question, context_chunks)
123
+
124
+ def _extractive_fallback(self, question: str, context_chunks: List[Dict]) -> str:
125
+ """
126
+ Fallback extractive answer when Azure OpenAI is not available.
127
+ Returns the most relevant chunk as answer.
128
+ """
129
+ if not context_chunks:
130
+ return "No context available. Please configure Azure OpenAI credentials for LLM generation."
131
+
132
+ # Return the top chunk as answer
133
+ top_chunk = context_chunks[0]
134
+ source = top_chunk.get('source', 'Unknown')
135
+ text = top_chunk.get('text', '')
136
+ score = top_chunk.get('score', 0)
137
+
138
+ answer = f"**Extractive Answer** (Relevance: {score:.3f})\n\n"
139
+ answer += f"**Source:** {source}\n\n"
140
+ answer += f"**Content:** {text[:800]}"
141
+
142
+ if len(text) > 800:
143
+ answer += "...\n\n*Note: This is an extractive answer. Configure Azure OpenAI for generated responses.*"
144
+ else:
145
+ answer += "\n\n*Note: This is an extractive answer. Configure Azure OpenAI for generated responses.*"
146
+
147
+ return answer
148
+
149
+ def test_connection(self) -> Dict[str, str]:
150
+ """
151
+ Test Azure OpenAI connection.
152
+
153
+ Returns:
154
+ Dictionary with status and message
155
+ """
156
+ if not self.has_token():
157
+ return {
158
+ "status": "error",
159
+ "message": "Missing Azure OpenAI credentials. Please check environment variables."
160
+ }
161
+
162
+ if not self.client:
163
+ return {
164
+ "status": "error",
165
+ "message": "Azure OpenAI client not initialized."
166
+ }
167
+
168
+ try:
169
+ # Test with a simple query
170
+ response = self.client.chat.completions.create(
171
+ model=self.deployment_name,
172
+ messages=[{"role": "user", "content": "Hello, are you working?"}],
173
+ max_tokens=10,
174
+ temperature=0.1
175
+ )
176
+
177
+ if response.choices and response.choices[0].message:
178
+ return {
179
+ "status": "success",
180
+ "message": "Azure OpenAI connection successful!"
181
+ }
182
+ else:
183
+ return {
184
+ "status": "error",
185
+ "message": "No response from Azure OpenAI."
186
+ }
187
+
188
+ except Exception as e:
189
+ return {
190
+ "status": "error",
191
+ "message": f"Connection test failed: {str(e)}"
192
+ }