1
+ '''
2
+ Deltix back_end
3
+ '''
4
+
5
+ from flask import Flask , request , jsonify
6
+ from supabase import create_client
7
+ from openai import OpenAI , AssistantEventHandler
8
+ import os
9
+ import json
10
+ import random
11
+ from datetime import datetime
12
+
13
+ app = Flask (__name__ )
14
+
15
+ supabase_url = os .getenv ('SUPABASE_URL' )
16
+ supabase_key = os .getenv ('SUPABASE_KEY' )
17
+ openrouter_key = os .getenv ('OPENROUTER_API_KEY' )
18
+
19
+ if not supabase_url or not supabase_key :
20
+ raise ValueError ("Supabase URL and Key must be set in environment variables" )
21
+
22
+ if not openrouter_key :
23
+ raise ValueError ("OpenRouter API Key must be set in environment variables" )
24
+
25
+ supabase = create_client (supabase_url , supabase_key )
26
+
27
+ WEATHER_KEYWORDS = ['clima' , 'temperatura' , 'pronostico' , 'tiempo' , 'lluvia' , 'viento' , 'llover' , 'soleado' , 'ventoso' , 'humedad' , 'tormenta' , 'nublado' , 'calor' , 'frio' ]
28
+ ALMACEN_KEYWORDS = ['almacen' , 'almacén' , 'almacenera' , 'almaceneras' ]
29
+ JILGUERO_KEYWORDS = ['jilguero' , 'carapachay' , 'angostura' ]
30
+ INTERISLENA_KEYWORDS = ['interisleña' , 'interislena' , 'sarmiento' , 'san antonio' , 'capitan' , 'capitán' ]
31
+ LINEASDELTA_KEYWORDS = ['lineasdelta' , 'caraguatá' , 'caraguata' , 'canal arias' , 'paraná miní' , 'parana mini' , 'lineas delta' ]
32
+
33
+ client = OpenAI (
34
+ base_url = "https://openrouter.ai/api/v1" ,
35
+ api_key = openrouter_key ,
36
+ )
37
+
38
+ def load_weather_data ():
39
+ try :
40
+ weather_file_path = "rag/weather_data.json"
41
+ with open (weather_file_path , 'r' ) as file :
42
+ weather_data = json .load (file )
43
+ return weather_data
44
+ except Exception as e :
45
+ print (f"Error loading weather data: { e } " )
46
+ return None
47
+
48
+ def format_weather_for_context (weather_data ):
49
+ if not weather_data :
50
+ return ""
51
+ try :
52
+ current = weather_data .get ('current_weather' , {})
53
+ location = current .get ('name' , 'Desconocido' )
54
+ country = current .get ('sys' , {}).get ('country' , '' )
55
+ temp = current .get ('main' , {}).get ('temp' , 'N/A' )
56
+ feels_like = current .get ('main' , {}).get ('feels_like' , 'N/A' )
57
+ description = current .get ('weather' , [{}])[0 ].get ('description' , 'N/A' )
58
+ humidity = current .get ('main' , {}).get ('humidity' , 'N/A' )
59
+ wind_speed = current .get ('wind' , {}).get ('speed' , 'N/A' )
60
+ timestamp_str = weather_data .get ('timestamp' , 'Desconocido' )
61
+ forecast_items = weather_data .get ('forecast' , {}).get ('list' , [])[:8 ]
62
+ forecast_text = ""
63
+ for item in forecast_items :
64
+ dt_txt = item .get ('dt_txt' , '' )
65
+ temp_forecast = item .get ('main' , {}).get ('temp' , 'N/A' )
66
+ desc_forecast = item .get ('weather' , [{}])[0 ].get ('description' , 'N/A' )
67
+ forecast_text += f"- { dt_txt } : { temp_forecast } °C, { desc_forecast } \n "
68
+ context = f"""
69
+ Información del clima para { location } , { country } (actualizado el { timestamp_str } ):
70
+ Condiciones actuales: { temp } °C (sensación térmica de { feels_like } °C), { description }
71
+ Humedad: { humidity } %, Velocidad del viento: { wind_speed } m/s
72
+
73
+ Pronóstico para las próximas 24 horas:
74
+ { forecast_text }
75
+ """
76
+ return context
77
+ except Exception as e :
78
+ print (f"Error formatting weather data: { e } " )
79
+ return "La información del clima está disponible pero no se pudo formatear correctamente."
80
+
81
+ def contains_weather_keywords (user_input ):
82
+ lower_input = user_input .lower ()
83
+ return any (keyword in lower_input for keyword in WEATHER_KEYWORDS )
84
+
85
+ def contains_almacen_keywords (user_input ):
86
+ lower_input = user_input .lower ()
87
+ return any (keyword in lower_input for keyword in ALMACEN_KEYWORDS )
88
+
89
+ def contains_jilguero_keywords (user_input ):
90
+ lower_input = user_input .lower ()
91
+ return any (keyword in lower_input for keyword in JILGUERO_KEYWORDS )
92
+
93
+ def contains_interislena_keywords (user_input ):
94
+ lower_input = user_input .lower ()
95
+ return any (keyword in lower_input for keyword in INTERISLENA_KEYWORDS )
96
+
97
+ def contains_lineasdelta_keywords (user_input ):
98
+ lower_input = user_input .lower ()
99
+ return any (keyword in lower_input for keyword in LINEASDELTA_KEYWORDS )
100
+
101
+ def load_almacen_data ():
102
+ try :
103
+ almacen_file_path = os .path .join (os .getcwd (), "rag" , "almaceneras.txt" )
104
+ if os .path .exists (almacen_file_path ):
105
+ with open (almacen_file_path , 'r' , encoding = 'utf-8' ) as file :
106
+ return file .read ()
107
+ else :
108
+ print (f"Almacen data file not found at: { almacen_file_path } " )
109
+ return ""
110
+ except Exception as e :
111
+ print (f"Error loading almacen data: { e } " )
112
+ return ""
113
+
114
+ def load_transportation_data (company_name ):
115
+ try :
116
+ file_path = os .path .join (os .getcwd (), "rag" , f"{ company_name } .txt" )
117
+ if os .path .exists (file_path ):
118
+ with open (file_path , 'r' , encoding = 'utf-8' ) as file :
119
+ return file .read ()
120
+ else :
121
+ print (f"Transportation data file not found at: { file_path } " )
122
+ return ""
123
+ except Exception as e :
124
+ print (f"Error loading transportation data: { e } " )
125
+ return ""
126
+
127
+ def make_api_call (user_input , project_id , documents , retries = 3 , delay = 2 ):
128
+ try :
129
+ context = []
130
+ if any (keyword in user_input .lower () for keyword in ['seguridad' , 'policia' , 'emergencia' , 'telefono' ]):
131
+ with open ("rag/policia.txt" , "r" ) as file :
132
+ context .append (file .read ())
133
+ if contains_weather_keywords (user_input ):
134
+ weather_data = load_weather_data ()
135
+ if weather_data :
136
+ weather_context = format_weather_for_context (weather_data )
137
+ context .append (weather_context )
138
+ if contains_almacen_keywords (user_input ):
139
+ almacen_context = load_almacen_data ()
140
+ if almacen_context :
141
+ context .append (f"Información sobre almacenes de la isla:\n { almacen_context } " )
142
+ if contains_jilguero_keywords (user_input ):
143
+ jilguero_context = load_transportation_data ("jilguero" )
144
+ if jilguero_context :
145
+ context .append (f"Información sobre la lancha colectiva Jilguero:\n { jilguero_context } " )
146
+ if contains_interislena_keywords (user_input ):
147
+ interislena_context = load_transportation_data ("interislena" )
148
+ if interislena_context :
149
+ context .append (f"Información sobre la lancha colectiva Interisleña:\n { interislena_context } " )
150
+ if contains_lineasdelta_keywords (user_input ):
151
+ lineasdelta_context = load_transportation_data ("lineasdelta" )
152
+ if lineasdelta_context :
153
+ context .append (f"Información sobre la lancha colectiva LineasDelta:\n { lineasdelta_context } " )
154
+ previous_messages = supabase .from_ ("chat_history" ).select ("*" ).eq ("project_id" , project_id ).execute ().data
155
+ previous_messages_content = "\n " .join ([msg ["content" ] for msg in previous_messages if msg ["role" ] == "user" ][- 5 :])
156
+ context_text = "\n \n " .join (context )
157
+ for attempt in range (retries ):
158
+ completion = client .chat .completions .create (
159
+ extra_headers = {
160
+ "HTTP-Referer" : "<YOUR_SITE_URL>" ,
161
+ "X-Title" : "<YOUR_SITE_NAME>" ,
162
+ },
163
+ extra_body = {},
164
+ model = "deepseek/deepseek-chat:free" ,
165
+ messages = [
166
+ {
167
+ "role" : "system" ,
168
+ "content" : "Vos sos Deltix, el bot del humedal. Eres argentino y amable. Ingresando algunas de estas palabras el usuario puede obtener información útil: mareas: obtener el pronóstico de mareas, windguru: pronóstico meteorológico de windgurú, Colectivas: horarios de lanchas colectivas, memes: ver los memes más divertidos de la isla, clima/pronostico: información meteorológica actualizada, almaceneras: información sobre almacenes de la isla. Si hay información de contexto, intenta responder con esa información o guia al usuario para que ingrese las palabras clave"
169
+ },
170
+ {
171
+ "role" : "user" ,
172
+ "content" : f"{ user_input } \n \n Mensajes anteriores:\n { previous_messages_content } \n \n Contexto:\n { context_text } "
173
+ }
174
+ ]
175
+ )
176
+ response_content = completion .choices [0 ].message .content
177
+ if response_content .strip ():
178
+ return response_content
179
+ time .sleep (delay )
180
+ raise ValueError ("Received empty response from OpenRouter API after multiple attempts" )
181
+ except Exception as e :
182
+ raise e
183
+
184
+ def store_chat_message (project_id , role , content ):
185
+ supabase .from_ ("chat_history" ).insert ({"project_id" : project_id , "role" : role , "content" : content }).execute ()
186
+
187
+ def create_project ():
188
+ response = supabase .from_ ("projects" ).insert ({"name" : "Nueva conversacion" }).execute ()
189
+ return response .data [0 ]["id" ]
190
+
191
+ def get_random_meme ():
192
+ meme_files = [f"memes/{ file } " for file in os .listdir ("memes" ) if file .endswith (".png" )]
193
+ return random .choice (meme_files ) if meme_files else None
194
+
195
+ def colectivas ():
196
+ msg = "Elegí la empresa de lancha colectiva:\n - **Jilguero**: va por el Carapachay-Angostura\n - **Interisleña**: Sarmiento, San Antonio y muchos más\n - **LineasDelta**: Caraguatá, Canal Arias, Paraná Miní"
197
+ st .session_state .chat_messages .append ({"role" : "assistant" , "content" : msg })
198
+ st .chat_message ("assistant" , avatar = "bot_icon.png" ).write (msg )
199
+ store_chat_message (project_id , "assistant" , msg )
200
+ st .session_state .colectivas_step = "select_company"
201
+
202
+ def handle_colectivas_input (user_input ):
203
+ # Implement the logic to handle user input for colectivas
204
+ pass
205
+
206
+ @app .route ('/api/chat' , methods = ['POST' ])
207
+ def chat ():
208
+ data = request .json
209
+ user_input = data .get ('user_input' )
210
+ project_id = data .get ('project_id' )
211
+ documents = data .get ('documents' , [])
212
+ response_content = make_api_call (user_input , project_id , documents )
213
+ store_chat_message (project_id , "user" , user_input )
214
+ store_chat_message (project_id , "assistant" , response_content )
215
+ return jsonify ({"response" : response_content })
216
+
217
+ @app .route ('/api/create_project' , methods = ['POST' ])
218
+ def create_new_project ():
219
+ project_id = create_project ()
220
+ return jsonify ({"project_id" : project_id })
221
+
222
+ @app .route ('/api/get_random_meme' , methods = ['GET' ])
223
+ def random_meme ():
224
+ meme_file = get_random_meme ()
225
+ return jsonify ({"meme_file" : meme_file })
226
+
227
+ if __name__ == '__main__' :
228
+ app .run (debug = True )
0 commit comments