Julian Vanecek commited on
Commit
dfb7b03
·
1 Parent(s): 09adb26
Files changed (2) hide show
  1. app.py +217 -0
  2. requirements.txt +11 -0
app.py ADDED
@@ -0,0 +1,217 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
+ import json
3
+ import gradio as gr
4
+ import requests
5
+ from elasticsearch import Elasticsearch
6
+ from dotenv import load_dotenv
7
+ import gradio.components as gc
8
+ from openai import OpenAI
9
+ import uuid
10
+
11
+ # Load environment variables
12
+ load_dotenv()
13
+
14
+ # Get sensitive config from environment variables (set these in your .env file)
15
+ ELASTICSEARCH_URL = os.getenv("ELASTICSEARCH_URL")
16
+ ELASTICSEARCH_USER = os.getenv("ELASTICSEARCH_USER")
17
+ ELASTICSEARCH_PASSWORD = os.getenv("ELASTICSEARCH_PASSWORD")
18
+ OPENAI_API_KEY = os.getenv("OPENAI_API_KEY")
19
+ AWS_LAMBDA_URL = os.getenv("AWS_LAMBDA_URL")
20
+ GRADIO_AUTH_USERNAME = os.getenv("GRADIO_AUTH_USERNAME")
21
+ GRADIO_AUTH_PASSWORD = os.getenv("GRADIO_AUTH_PASSWORD")
22
+
23
+ # Check required env vars (skip Elasticsearch-related)
24
+ missing_vars = []
25
+ for var in ["OPENAI_API_KEY", "AWS_LAMBDA_URL", "GRADIO_AUTH_USERNAME", "GRADIO_AUTH_PASSWORD"]:
26
+ if not os.getenv(var):
27
+ missing_vars.append(var)
28
+ if missing_vars:
29
+ raise RuntimeError(f"Missing required environment variables: {', '.join(missing_vars)}. Please set them in your .env file.")
30
+
31
+ # Initialize clients
32
+ if ELASTICSEARCH_URL:
33
+ es = Elasticsearch(
34
+ ELASTICSEARCH_URL,
35
+ basic_auth=(str(ELASTICSEARCH_USER), str(ELASTICSEARCH_PASSWORD))
36
+ )
37
+ else:
38
+ es = None
39
+
40
+ # Initialize OpenAI
41
+ openai_client = OpenAI(api_key=OPENAI_API_KEY)
42
+ def chat_completion(messages, model="gpt-3.5-turbo", temperature=0.1):
43
+ return openai_client.chat.completions.create(
44
+ model=model,
45
+ messages=messages,
46
+ temperature=temperature
47
+ )
48
+
49
+ def process_faq(question, user_id="anonymous", model="claude-sonnet"):
50
+ """Process FAQ by calling AWS Lambda function with streaming response"""
51
+ try:
52
+ # Determine the correct Lambda URL and model parameter based on selection
53
+ if model.startswith("nova-"):
54
+ lambda_url = "https://tz2ttiieoc5z4aq6pskg24zu740bvqup.lambda-url.us-west-2.on.aws/"
55
+ model_param = model.replace("nova-", "") # Extract micro/lite/pro
56
+ elif model.startswith("claude-"):
57
+ lambda_url = "https://myzano2bfze54q6yqp32wwpj6q0ixpmy.lambda-url.us-west-2.on.aws/"
58
+ model_param = model.replace("claude-", "") # Extract haiku/sonnet
59
+ else:
60
+ return "Error: Invalid model selection"
61
+
62
+ # Prepare the request payload
63
+ payload = {
64
+ "message": question.strip(),
65
+ "user_id": user_id,
66
+ "model": model_param
67
+ }
68
+
69
+ print(f"DEBUG: Sending to {lambda_url}")
70
+ print(f"DEBUG: Payload: {json.dumps(payload, indent=2)}")
71
+
72
+ # Make the API call with streaming
73
+ with requests.post(
74
+ lambda_url,
75
+ headers={"Content-Type": "application/json"},
76
+ json=payload,
77
+ stream=True
78
+ ) as response:
79
+ if response.status_code != 200:
80
+ return f"Error: Lambda function returned status code {response.status_code}"
81
+
82
+ # Process the streaming response
83
+ full_response = ""
84
+ for chunk in response.iter_content(chunk_size=1024, decode_unicode=True):
85
+ if chunk:
86
+ try:
87
+ # Try to parse the chunk as JSON
88
+ chunk_data = json.loads(chunk)
89
+ if "response" in chunk_data:
90
+ chunk_text = chunk_data["response"]
91
+ full_response += chunk_text
92
+ yield full_response
93
+ except json.JSONDecodeError:
94
+ # If not JSON, treat as plain text
95
+ full_response += chunk
96
+ yield full_response
97
+
98
+ return full_response
99
+
100
+ except Exception as e:
101
+ return f"Error processing FAQ: {str(e)}"
102
+
103
+ def natural_to_query(natural_query):
104
+ """Convert natural language to Elasticsearch query body"""
105
+ try:
106
+ prompt = f"""Convert the following natural language query into an Elasticsearch query body.\nThe query should be in JSON format and follow Elasticsearch query DSL syntax.\n\nNatural language query: {natural_query}\n\nReturn only the JSON query body, nothing else."""
107
+ response = chat_completion([
108
+ {"role": "system", "content": "You are an expert in Elasticsearch query DSL. Convert natural language to Elasticsearch queries."},
109
+ {"role": "user", "content": prompt}
110
+ ], model="gpt-3.5-turbo", temperature=0.1)
111
+ # Extract and format the query
112
+ if hasattr(response, 'choices'):
113
+ # For OpenAI v1.x
114
+ content = response.choices[0].message.content.strip()
115
+ else:
116
+ # For OpenAI v0.x
117
+ content = response["choices"][0]["message"]["content"].strip()
118
+ try:
119
+ query_json = json.loads(content)
120
+ return json.dumps(query_json, indent=2)
121
+ except json.JSONDecodeError:
122
+ return content
123
+ except Exception as e:
124
+ return f"Error generating query: {str(e)}"
125
+
126
+ def execute_elasticsearch_query(query_body):
127
+ """Execute the Elasticsearch query"""
128
+ try:
129
+ # Parse the query body
130
+ query_json = json.loads(query_body)
131
+
132
+ # Execute the query
133
+ response = es.search(
134
+ index="your_index_name", # Replace with your actual index name
135
+ body=query_json
136
+ )
137
+
138
+ # Format the response
139
+ return json.dumps(response, indent=2)
140
+ except json.JSONDecodeError:
141
+ return "Error: Invalid JSON query body"
142
+ except Exception as e:
143
+ return f"Error executing query: {str(e)}"
144
+
145
+ # --- Gradio v4.x UI ---
146
+ def faq_wrapper(question, user_id, model):
147
+ # Gradio expects a non-generator for Interface
148
+ result = ""
149
+ for chunk in process_faq(question, user_id, model):
150
+ result = chunk
151
+ return result
152
+
153
+ def elasticsearch_generate(natural_input):
154
+ return natural_to_query(natural_input)
155
+
156
+ def elasticsearch_execute(query_body):
157
+ return execute_elasticsearch_query(query_body)
158
+
159
+ with gr.Blocks() as demo:
160
+ gc.Markdown("# MCP Tools - Local Version")
161
+ with gr.Tab(label="FAQ"): # type: ignore
162
+ faq_input = gc.Textbox(label="Enter your question", lines=3)
163
+ model_selector = gc.Dropdown(
164
+ label="Select Model",
165
+ choices=["nova-micro", "nova-lite", "nova-pro", "claude-haiku", "claude-sonnet"],
166
+ value="claude-sonnet",
167
+ interactive=True
168
+ )
169
+ # Generate random user ID for this session
170
+ session_user_id = str(uuid.uuid4())[:8]
171
+ faq_button = gc.Button("Process")
172
+ faq_output = gc.Textbox(label="Response", lines=10)
173
+ with gr.Row(): # type: ignore
174
+ thumbs_down = gc.Button("Report bad response", elem_id="thumbs-down", interactive=True)
175
+ feedback_msg = gc.Markdown(visible=False)
176
+
177
+ def report_bad_response():
178
+ return gr.update(value="Bad response reported. Thank you for your feedback.", visible=True), gr.update(interactive=False)
179
+
180
+ thumbs_down.click(report_bad_response, outputs=[feedback_msg, thumbs_down])
181
+
182
+ # Re-enable report button and clear feedback when a new FAQ is processed
183
+ def reset_feedback(*args):
184
+ return gr.update(interactive=True), gr.update(value="", visible=False)
185
+ faq_button.click(reset_feedback, outputs=[thumbs_down, feedback_msg], preprocess=False)
186
+ faq_button.click(lambda q, m: faq_wrapper(q, session_user_id, m), inputs=[faq_input, model_selector], outputs=faq_output)
187
+ with gr.Tab(label="Elasticsearch"): # type: ignore
188
+ gc.Markdown("### Step 1: Natural Language to Query")
189
+ natural_input = gc.Textbox(label="Describe what you want to search for", lines=3, placeholder="Example: Find all documents containing 'machine learning' in the title")
190
+ generate_button = gc.Button("Generate Query")
191
+ query_output = gc.Textbox(label="Generated Query Body", lines=10, placeholder="The generated Elasticsearch query will appear here")
192
+ generate_button.click(elasticsearch_generate, inputs=natural_input, outputs=query_output)
193
+ gc.Markdown("### Step 2: Execute Query")
194
+ gc.Markdown("You can modify the query above if needed, then click Execute")
195
+ execute_button = gc.Button("Execute Query")
196
+ result_output = gc.Textbox(label="Query Results", lines=10, placeholder="The query results will appear here")
197
+ execute_button.click(elasticsearch_execute, inputs=query_output, outputs=result_output)
198
+
199
+ if __name__ == "__main__":
200
+ # Check if running in Hugging Face Spaces
201
+ is_spaces = os.getenv("SPACE_ID") is not None
202
+
203
+ # Get auth credentials from environment variables
204
+ auth_username = GRADIO_AUTH_USERNAME
205
+ auth_password = GRADIO_AUTH_PASSWORD
206
+
207
+ # Configure launch parameters
208
+ launch_params = {
209
+ "server_name": "0.0.0.0",
210
+ "server_port": int(os.getenv("PORT", 7860)),
211
+ "share": not is_spaces, # Only enable sharing when not in Spaces
212
+ "auth": (auth_username, auth_password), # Add basic auth
213
+ "auth_message": "Please enter your credentials to access the application."
214
+ }
215
+
216
+ # Launch the app
217
+ demo.launch(**launch_params)
requirements.txt ADDED
@@ -0,0 +1,11 @@
 
 
 
 
 
 
 
 
 
 
 
 
1
+ gradio==4.44.1
2
+ python-dotenv==1.0.0
3
+ elasticsearch==8.11.1
4
+ pydub==0.25.1
5
+ openai==1.17.0
6
+ httpx==0.27.2
7
+ requests==2.32.4
8
+ urllib3==2.5.0
9
+ certifi==2024.7.4
10
+ cryptography==44.0.1
11
+ pyOpenSSL==25.1.0