Se realiza una nueva implmentacion para la autenticacion con la api de Google.
This commit is contained in:
parent
86b3fc8d0e
commit
283da64cd3
@ -1,12 +1,15 @@
|
||||
FROM python:3.11-slim
|
||||
|
||||
RUN pip install poetry==1.6.1
|
||||
RUN pip install poetry==1.8.4
|
||||
|
||||
RUN poetry config virtualenvs.create false
|
||||
|
||||
WORKDIR /code
|
||||
|
||||
COPY ./pyproject.toml ./README.md ./poetry.lock* ./
|
||||
COPY ./pyproject.toml ./README.md ./
|
||||
|
||||
# Generar el archivo lock desde cero
|
||||
RUN poetry lock --no-update
|
||||
|
||||
COPY ./package[s] ./packages
|
||||
|
||||
|
48
app/google_auth.py
Normal file
48
app/google_auth.py
Normal file
@ -0,0 +1,48 @@
|
||||
from google.oauth2.credentials import Credentials
|
||||
from google.auth.transport.requests import Request
|
||||
import os.path
|
||||
import json
|
||||
|
||||
# Si modificas estos scopes, elimina el archivo token.json
|
||||
SCOPES = [
|
||||
'https://www.googleapis.com/auth/calendar', # Full access to calendar
|
||||
'https://www.googleapis.com/auth/gmail.readonly', # Read Gmail profile
|
||||
'https://www.googleapis.com/auth/gmail.send', # Send emails
|
||||
'https://www.googleapis.com/auth/calendar.events', # Manage calendar events
|
||||
'https://www.googleapis.com/auth/calendar.readonly' # Read calendar events
|
||||
]
|
||||
|
||||
def get_google_credentials(scopes=None, credentials_file='google_credentials.json'):
|
||||
"""Obtiene las credenciales de Google desde el token pre-generado.
|
||||
|
||||
Args:
|
||||
scopes: Lista opcional de scopes de Google API
|
||||
credentials_file: No se usa, mantenido por compatibilidad
|
||||
|
||||
Returns:
|
||||
Google OAuth2 credentials
|
||||
"""
|
||||
# Si se proporcionan scopes, usarlos; si no, usar los predeterminados
|
||||
scopes_to_use = scopes if scopes else SCOPES
|
||||
|
||||
# Ruta al token pre-generado
|
||||
token_path = os.path.join('tokens', 'gmail_token.json')
|
||||
|
||||
if not os.path.exists(token_path):
|
||||
raise FileNotFoundError(
|
||||
f"No se encontró el archivo {token_path}. "
|
||||
"Ejecuta generate_token.py fuera del Docker primero."
|
||||
)
|
||||
|
||||
try:
|
||||
# Cargar credenciales desde el token
|
||||
creds = Credentials.from_authorized_user_file(token_path, scopes_to_use)
|
||||
|
||||
# Si el token está expirado, intentar refrescarlo
|
||||
if creds.expired:
|
||||
creds.refresh(Request())
|
||||
|
||||
return creds
|
||||
|
||||
except Exception as e:
|
||||
raise Exception(f"Error al cargar o refrescar el token: {e}")
|
@ -10,8 +10,9 @@ from app.rag.split_docs import load_split_docs
|
||||
from app.rag.llm import load_llm_openai
|
||||
from app.rag.embeddings import load_embeddins
|
||||
from app.rag.retriever import create_retriever
|
||||
from app.rag.vectorstore import create_verctorstore
|
||||
from app.rag.vectorstore import create_vectorstore
|
||||
from app.rag.rag_chain import create_rag_chain
|
||||
from app.google_auth import get_google_credentials
|
||||
import pytz
|
||||
import telebot
|
||||
import os
|
||||
@ -31,6 +32,122 @@ class LangChainTools:
|
||||
)
|
||||
return llm
|
||||
|
||||
def list_calendar_events(self, max_results: int = 50) -> list:
|
||||
"""Lista los próximos eventos del calendario."""
|
||||
try:
|
||||
# Usar el sistema centralizado de autenticación
|
||||
creds = get_google_credentials()
|
||||
|
||||
# Construir el servicio de Calendar
|
||||
service = build('calendar', 'v3', credentials=creds)
|
||||
|
||||
# Obtener eventos próximos
|
||||
now = datetime.now(timezone.utc).isoformat()
|
||||
events_result = service.events().list(
|
||||
calendarId='primary',
|
||||
timeMin=now,
|
||||
maxResults=max_results,
|
||||
singleEvents=True,
|
||||
orderBy='startTime'
|
||||
).execute()
|
||||
|
||||
events = events_result.get('items', [])
|
||||
|
||||
if not events:
|
||||
print('No se encontraron eventos próximos.')
|
||||
return []
|
||||
|
||||
# Formatear la salida de eventos
|
||||
formatted_events = []
|
||||
for event in events:
|
||||
start = event['start'].get('dateTime', event['start'].get('date'))
|
||||
formatted_events.append({
|
||||
'start': start,
|
||||
'summary': event['summary'],
|
||||
'id': event['id'],
|
||||
'link': event.get('htmlLink')
|
||||
})
|
||||
print(f"{start} - {event['summary']}")
|
||||
|
||||
return formatted_events
|
||||
|
||||
except Exception as e:
|
||||
print(f"Error al listar eventos: {e}")
|
||||
return {"error": str(e)}
|
||||
|
||||
def create_calendar_event(
|
||||
self, title: str, start_time: datetime,
|
||||
end_time: datetime, attendees: list = None) -> dict:
|
||||
"""Crea un evento en el calendario."""
|
||||
try:
|
||||
# Usar el sistema centralizado de autenticación
|
||||
creds = get_google_credentials()
|
||||
|
||||
# Construir el servicio de Calendar
|
||||
service = build('calendar', 'v3', credentials=creds)
|
||||
|
||||
# Identificador del calendario principal
|
||||
calendar_id = 'primary'
|
||||
|
||||
# Definir el evento
|
||||
event = {
|
||||
'summary': title,
|
||||
'start': {
|
||||
'dateTime': start_time.strftime('%Y-%m-%dT%H:%M:%S'),
|
||||
'timeZone': 'America/Bogota',
|
||||
},
|
||||
'end': {
|
||||
'dateTime': end_time.strftime('%Y-%m-%dT%H:%M:%S'),
|
||||
'timeZone': 'America/Bogota',
|
||||
},
|
||||
}
|
||||
|
||||
# Agregar asistentes si se proporcionan
|
||||
if attendees and isinstance(attendees, list) and len(attendees) > 0:
|
||||
# Asegurarse de que cada asistente tenga un correo válido
|
||||
attendees_list = []
|
||||
for attendee in attendees:
|
||||
if isinstance(attendee, str) and '@' in attendee:
|
||||
attendees_list.append({'email': attendee.strip()})
|
||||
if attendees_list:
|
||||
event['attendees'] = attendees_list
|
||||
# Enviar invitaciones por correo
|
||||
event['sendUpdates'] = 'all'
|
||||
|
||||
# Crear el evento
|
||||
event = service.events().insert(calendarId=calendar_id, body=event).execute()
|
||||
print(f'Evento creado: {event.get("htmlLink")}')
|
||||
return event
|
||||
|
||||
except Exception as e:
|
||||
print(f"Error al crear el evento: {e}")
|
||||
return {"error": str(e)}
|
||||
|
||||
def create_quick_add_event(self, quick_add_text: str) -> dict:
|
||||
"""Crea un evento rápidamente usando texto en lenguaje natural."""
|
||||
try:
|
||||
# Usar el sistema centralizado de autenticación
|
||||
creds = get_google_credentials()
|
||||
|
||||
# Construir el servicio de Calendar
|
||||
service = build('calendar', 'v3', credentials=creds)
|
||||
|
||||
# Identificador del calendario principal
|
||||
calendar_id = 'primary'
|
||||
|
||||
# Crear el evento usando quick add
|
||||
event = service.events().quickAdd(
|
||||
calendarId=calendar_id,
|
||||
text=quick_add_text
|
||||
).execute()
|
||||
|
||||
print(f'Evento creado: {event.get("htmlLink")}')
|
||||
return event
|
||||
|
||||
except Exception as e:
|
||||
print(f"Error al crear el evento rápido: {e}")
|
||||
return {"error": str(e)}
|
||||
|
||||
|
||||
@tool
|
||||
def multiply(first_int: int, second_int: int) -> int:
|
||||
@ -57,232 +174,6 @@ def redact_email(topic: str) -> str:
|
||||
return response
|
||||
|
||||
|
||||
@tool
|
||||
def list_calendar_events(max_results: int = 50) -> list:
|
||||
"""Use this tool to list upcoming calendar events."""
|
||||
|
||||
# Define los alcances que necesitamos para acceder a
|
||||
# la API de Google Calendar
|
||||
SCOPES = ['https://www.googleapis.com/auth/calendar']
|
||||
|
||||
creds = None
|
||||
|
||||
# La ruta al archivo token.json, que contiene
|
||||
# los tokens de acceso y actualización
|
||||
token_path = 'token.json'
|
||||
|
||||
# La ruta al archivo de credenciales de OAuth 2.1
|
||||
creds_path = 'credentials.json'
|
||||
|
||||
# Cargar las credenciales desde el archivo token.json, si existe
|
||||
if os.path.exists(token_path):
|
||||
creds = Credentials.from_authorized_user_file(token_path, SCOPES)
|
||||
|
||||
# Si no hay credenciales válidas disponibles, inicia el flujo de OAuth 2.0
|
||||
# para obtener nuevas credenciales
|
||||
if not creds or not creds.valid:
|
||||
if creds and creds.expired and creds.refresh_token:
|
||||
creds.refresh(Request())
|
||||
else:
|
||||
flow = InstalledAppFlow.from_client_secrets_file(
|
||||
creds_path, SCOPES)
|
||||
creds = flow.run_local_server(port=0)
|
||||
|
||||
# Guarda las credenciales para la próxima ejecución
|
||||
with open(token_path, 'w') as token_file:
|
||||
token_file.write(creds.to_json())
|
||||
|
||||
# Construye el objeto de servicio para interactuar
|
||||
# con la API de Google Calendar
|
||||
service = build('calendar', 'v3', credentials=creds)
|
||||
|
||||
# Identificador del calendario que deseas consultar.
|
||||
# 'primary' se refiere al calendario principal del usuario.
|
||||
calendar_id = 'primary'
|
||||
|
||||
# Realiza una llamada a la API para obtener una lista de eventos.
|
||||
now = datetime.now(timezone.utc).isoformat() # 'Z' indica UTC
|
||||
events_result = service.events().list(
|
||||
calendarId=calendar_id, timeMin=now,
|
||||
maxResults=max_results, singleEvents=True,
|
||||
orderBy='startTime').execute()
|
||||
|
||||
# Extrae los eventos de la respuesta de la API.
|
||||
events = events_result.get('items', [])
|
||||
|
||||
# Si no se encuentran eventos, imprime un mensaje.
|
||||
if not events:
|
||||
print('No upcoming events found.')
|
||||
return
|
||||
|
||||
# Recorre la lista de eventos y muestra la hora de inicio
|
||||
# y el resumen de cada evento.
|
||||
for event in events:
|
||||
# Obtiene la fecha y hora de inicio del evento.
|
||||
# Puede ser 'dateTime' o 'date'.
|
||||
start = event['start'].get('dateTime', event['start'].get('date'))
|
||||
# Imprime la hora de inicio y el resumen (título) del evento.
|
||||
print(start, event['summary'])
|
||||
|
||||
return events
|
||||
|
||||
|
||||
@tool
|
||||
def create_calendar_event(
|
||||
title: str, start_time: datetime,
|
||||
end_time: datetime, attendees: list) -> dict:
|
||||
"""Use this tool to create an event in the calendar.
|
||||
|
||||
Parameters:
|
||||
- title: str - The title of the event.
|
||||
- start_time: datetime - The start time of the event.
|
||||
- end_time: datetime - The end time of the event.
|
||||
- attendees: list - A list of attendee emails (required).
|
||||
|
||||
Returns:
|
||||
- dict - The created event details.
|
||||
"""
|
||||
|
||||
if not attendees:
|
||||
raise ValueError(
|
||||
"El campo 'attendees' es obligatorio y no puede estar vacío.")
|
||||
|
||||
SCOPES = ['https://www.googleapis.com/auth/calendar']
|
||||
creds = None
|
||||
|
||||
# La ruta al archivo token.json,
|
||||
# que contiene los tokens de acceso y actualización
|
||||
token_path = 'token_2.json'
|
||||
|
||||
# La ruta al archivo de credenciales de OAuth 2.0
|
||||
creds_path = 'credentials_2.json'
|
||||
|
||||
# Cargar las credenciales desde el archivo token.json, si existe
|
||||
if os.path.exists(token_path):
|
||||
creds = Credentials.from_authorized_user_file(token_path, SCOPES)
|
||||
|
||||
# Si no hay credenciales válidas disponibles,
|
||||
# inicia el flujo de OAuth 2.0 para obtener nuevas credenciales
|
||||
if not creds or not creds.valid:
|
||||
if creds and creds.expired and creds.refresh_token:
|
||||
creds.refresh(Request())
|
||||
else:
|
||||
flow = InstalledAppFlow.from_client_secrets_file(
|
||||
creds_path, SCOPES)
|
||||
creds = flow.run_local_server(port=0)
|
||||
|
||||
# Guarda las credenciales para la próxima ejecución
|
||||
with open(token_path, 'w') as token_file:
|
||||
token_file.write(creds.to_json())
|
||||
|
||||
# Construye el objeto de servicio para
|
||||
# interactuar con la API de Google Calendar
|
||||
service = build('calendar', 'v3', credentials=creds)
|
||||
|
||||
# Validar y filtrar asistentes
|
||||
valid_attendees = []
|
||||
for email in attendees:
|
||||
if isinstance(email, str) and '@' in email:
|
||||
valid_attendees.append({'email': email})
|
||||
else:
|
||||
raise ValueError(f"'{email}' no es un correo electrónico válido.")
|
||||
|
||||
# Identificador del calendario que deseas modificar.
|
||||
# 'primary' se refiere al calendario principal del usuario.
|
||||
calendar_id = 'primary'
|
||||
|
||||
# Define el cuerpo del evento con el título,
|
||||
# la hora de inicio y la hora de finalización
|
||||
event = {
|
||||
'summary': title,
|
||||
'start': {
|
||||
'dateTime': start_time.strftime('%Y-%m-%dT%H:%M:%S'),
|
||||
'timeZone': 'America/Bogota',
|
||||
},
|
||||
'end': {
|
||||
'dateTime': end_time.strftime('%Y-%m-%dT%H:%M:%S'),
|
||||
'timeZone': 'America/Bogota',
|
||||
},
|
||||
'attendees': valid_attendees
|
||||
}
|
||||
|
||||
try:
|
||||
# Crea el evento en el calendario
|
||||
event = service.events().insert(
|
||||
calendarId=calendar_id, body=event).execute()
|
||||
print('Event created: %s' % (event.get('htmlLink')))
|
||||
except Exception as e:
|
||||
print(f"Error al crear el evento: {e}")
|
||||
return {}
|
||||
|
||||
return event
|
||||
|
||||
|
||||
@tool
|
||||
def create_quick_add_event(quick_add_text: str):
|
||||
"""
|
||||
Use this tool to create events in the calendar from natural language,
|
||||
using the Quick Add feature of Google Calendar.
|
||||
"""
|
||||
quick_add_text: str = input(
|
||||
"- Escribe la descripcion del evento que quieres crear: ")
|
||||
SCOPES = ['https://www.googleapis.com/auth/calendar']
|
||||
|
||||
creds = None
|
||||
|
||||
# La ruta al archivo token.json,
|
||||
# que contiene los tokens de acceso y actualización
|
||||
token_path = 'token_2.json'
|
||||
|
||||
# La ruta al archivo de credenciales de OAuth 2.0
|
||||
creds_path = 'credentials_2.json'
|
||||
|
||||
# Cargar las credenciales desde el archivo token.json, si existe
|
||||
if os.path.exists(token_path):
|
||||
creds = Credentials.from_authorized_user_file(token_path, SCOPES)
|
||||
|
||||
# Si no hay credenciales válidas disponibles,
|
||||
# inicia el flujo de OAuth 2.0 para obtener nuevas credenciales
|
||||
if not creds or not creds.valid:
|
||||
if creds and creds.expired and creds.refresh_token:
|
||||
creds.refresh(Request())
|
||||
else:
|
||||
flow = InstalledAppFlow.from_client_secrets_file(
|
||||
creds_path, SCOPES)
|
||||
creds = flow.run_local_server(port=0)
|
||||
|
||||
# Guarda las credenciales para la próxima ejecución
|
||||
with open(token_path, 'w') as token_file:
|
||||
token_file.write(creds.to_json())
|
||||
|
||||
# Construye el objeto de servicio para interactuar
|
||||
# con la API de Google Calendar
|
||||
service = build('calendar', 'v3', credentials=creds)
|
||||
|
||||
# Identificador del calendario que deseas modificar.
|
||||
# 'primary' se refiere al calendario principal del usuario.
|
||||
calendar_id = 'primary'
|
||||
|
||||
# Crea el evento utilizando la funcionalidad Quick Add
|
||||
event = service.events().quickAdd(
|
||||
calendarId=calendar_id, text=quick_add_text).execute()
|
||||
|
||||
print('Event created: %s' % (event.get('htmlLink')))
|
||||
|
||||
return event
|
||||
|
||||
|
||||
# @tool
|
||||
# def send_message(message: str):
|
||||
# """Use this function when you need to communicate with the user."""
|
||||
# # Configuración del bot
|
||||
# load_dotenv()
|
||||
# API_TOKEN_BOT = os.getenv("API_TOKEN_BOT")
|
||||
# bot = telebot.TeleBot(API_TOKEN_BOT)
|
||||
#
|
||||
# bot.send_message(chat_id="5076346205", text=message)
|
||||
#
|
||||
|
||||
@tool
|
||||
def send_message(message: str):
|
||||
"""Use this function when you need to communicate with Cristian."""
|
||||
@ -308,29 +199,32 @@ def get_company_info(prompt: str) -> str:
|
||||
"""
|
||||
file_path: str = 'onecluster_info.pdf'
|
||||
|
||||
docs_split: list = load_split_docs(file_path)
|
||||
embeddings_model = load_embeddins()
|
||||
llm = load_llm_openai()
|
||||
create_verctorstore(
|
||||
docs_split,
|
||||
embeddings_model,
|
||||
file_path
|
||||
)
|
||||
retriever = create_retriever(
|
||||
embeddings_model,
|
||||
persist_directory="embeddings/onecluster_info"
|
||||
)
|
||||
qa = create_rag_chain(
|
||||
llm, retriever)
|
||||
try:
|
||||
docs_split: list = load_split_docs(file_path)
|
||||
embeddings_model = load_embeddins()
|
||||
llm = load_llm_openai()
|
||||
|
||||
# Usar el nombre corregido de la función
|
||||
create_vectorstore(
|
||||
docs_split,
|
||||
embeddings_model,
|
||||
file_path
|
||||
)
|
||||
|
||||
retriever = create_retriever(
|
||||
embeddings_model,
|
||||
persist_directory="embeddings/onecluster_info"
|
||||
)
|
||||
qa = create_rag_chain(llm, retriever)
|
||||
|
||||
# prompt: str = "Escribe un parrarfo describiendo cuantos son y
|
||||
# cuales son los servicios que ofrece OneCluster
|
||||
# y brinda detalles sobre cada uno."
|
||||
response = qa.invoke(
|
||||
{"input": prompt, "chat_history": []}
|
||||
)
|
||||
response = qa.invoke(
|
||||
{"input": prompt, "chat_history": []}
|
||||
)
|
||||
|
||||
return response["answer"]
|
||||
return response["answer"]
|
||||
except Exception as e:
|
||||
print(f"Error en get_company_info: {e}")
|
||||
return f"Lo siento, hubo un error al procesar la información: {str(e)}"
|
||||
|
||||
|
||||
@tool
|
||||
@ -344,3 +238,123 @@ def get_current_date_and_time():
|
||||
bogota_tz = pytz.timezone('America/Bogota')
|
||||
current_date_and_time = datetime.now(bogota_tz)
|
||||
return current_date_and_time.strftime('%Y-%m-%d %H:%M:%S')
|
||||
|
||||
|
||||
@tool
|
||||
def list_calendar_events(max_results: int = 50) -> list:
|
||||
"""Use this tool to list upcoming calendar events."""
|
||||
try:
|
||||
# Usar el sistema centralizado de autenticación
|
||||
creds = get_google_credentials()
|
||||
|
||||
# Construir el servicio de Calendar
|
||||
service = build('calendar', 'v3', credentials=creds)
|
||||
|
||||
# Obtener eventos próximos
|
||||
now = datetime.now(timezone.utc).isoformat()
|
||||
events_result = service.events().list(
|
||||
calendarId='primary',
|
||||
timeMin=now,
|
||||
maxResults=max_results,
|
||||
singleEvents=True,
|
||||
orderBy='startTime'
|
||||
).execute()
|
||||
|
||||
events = events_result.get('items', [])
|
||||
|
||||
if not events:
|
||||
print('No se encontraron eventos próximos.')
|
||||
return []
|
||||
|
||||
# Formatear la salida de eventos
|
||||
formatted_events = []
|
||||
for event in events:
|
||||
start = event['start'].get('dateTime', event['start'].get('date'))
|
||||
formatted_events.append({
|
||||
'start': start,
|
||||
'summary': event['summary'],
|
||||
'id': event['id'],
|
||||
'link': event.get('htmlLink')
|
||||
})
|
||||
print(f"{start} - {event['summary']}")
|
||||
|
||||
return formatted_events
|
||||
|
||||
except Exception as e:
|
||||
print(f"Error al listar eventos: {e}")
|
||||
return {"error": str(e)}
|
||||
|
||||
@tool
|
||||
def create_calendar_event(
|
||||
title: str, start_time: datetime,
|
||||
end_time: datetime, attendees: list = None) -> dict:
|
||||
"""Use this tool to create an event in the calendar."""
|
||||
try:
|
||||
# Usar el sistema centralizado de autenticación
|
||||
creds = get_google_credentials()
|
||||
|
||||
# Construir el servicio de Calendar
|
||||
service = build('calendar', 'v3', credentials=creds)
|
||||
|
||||
# Identificador del calendario principal
|
||||
calendar_id = 'primary'
|
||||
|
||||
# Definir el evento
|
||||
event = {
|
||||
'summary': title,
|
||||
'start': {
|
||||
'dateTime': start_time.strftime('%Y-%m-%dT%H:%M:%S'),
|
||||
'timeZone': 'America/Bogota',
|
||||
},
|
||||
'end': {
|
||||
'dateTime': end_time.strftime('%Y-%m-%dT%H:%M:%S'),
|
||||
'timeZone': 'America/Bogota',
|
||||
},
|
||||
}
|
||||
|
||||
# Agregar asistentes si se proporcionan
|
||||
if attendees and isinstance(attendees, list) and len(attendees) > 0:
|
||||
# Asegurarse de que cada asistente tenga un correo válido
|
||||
attendees_list = []
|
||||
for attendee in attendees:
|
||||
if isinstance(attendee, str) and '@' in attendee:
|
||||
attendees_list.append({'email': attendee.strip()})
|
||||
if attendees_list:
|
||||
event['attendees'] = attendees_list
|
||||
# Enviar invitaciones por correo
|
||||
event['sendUpdates'] = 'all'
|
||||
|
||||
# Crear el evento
|
||||
event = service.events().insert(calendarId=calendar_id, body=event).execute()
|
||||
print(f'Evento creado: {event.get("htmlLink")}')
|
||||
return event
|
||||
|
||||
except Exception as e:
|
||||
print(f"Error al crear el evento: {e}")
|
||||
return {"error": str(e)}
|
||||
|
||||
@tool
|
||||
def create_quick_add_event(quick_add_text: str) -> dict:
|
||||
"""Use this tool to create events in the calendar from natural language."""
|
||||
try:
|
||||
# Usar el sistema centralizado de autenticación
|
||||
creds = get_google_credentials()
|
||||
|
||||
# Construir el servicio de Calendar
|
||||
service = build('calendar', 'v3', credentials=creds)
|
||||
|
||||
# Identificador del calendario principal
|
||||
calendar_id = 'primary'
|
||||
|
||||
# Crear el evento usando quick add
|
||||
event = service.events().quickAdd(
|
||||
calendarId=calendar_id,
|
||||
text=quick_add_text
|
||||
).execute()
|
||||
|
||||
print(f'Evento creado: {event.get("htmlLink")}')
|
||||
return event
|
||||
|
||||
except Exception as e:
|
||||
print(f"Error al crear el evento rápido: {e}")
|
||||
return {"error": str(e)}
|
||||
|
@ -2,16 +2,18 @@ from langchain_chroma import Chroma
|
||||
import os
|
||||
|
||||
|
||||
def create_verctorstore(docs_split: list, embeddings, file_name: str):
|
||||
def create_vectorstore(docs_split: list, embeddings, file_name: str):
|
||||
db_name: str = file_name.replace(".pdf", "").replace(" ", "_").lower()
|
||||
|
||||
persist_directory: str = f"embeddings/{db_name}"
|
||||
|
||||
if not os.path.exists(persist_directory):
|
||||
vectordb = Chroma.from_documents(
|
||||
persist_directory=persist_directory,
|
||||
documents=docs_split,
|
||||
embedding=embeddings,
|
||||
)
|
||||
# Crear el directorio si no existe
|
||||
os.makedirs(persist_directory, exist_ok=True)
|
||||
|
||||
return vectordb
|
||||
# Siempre crear/actualizar el vectorstore
|
||||
vectordb = Chroma.from_documents(
|
||||
persist_directory=persist_directory,
|
||||
documents=docs_split,
|
||||
embedding=embeddings,
|
||||
)
|
||||
|
||||
return vectordb
|
||||
|
@ -7,8 +7,7 @@ from langchain_core.prompts import ChatPromptTemplate
|
||||
|
||||
from langchain_community.tools.tavily_search import TavilySearchResults
|
||||
from langchain_community.tools.gmail.utils import (
|
||||
build_resource_service,
|
||||
get_gmail_credentials)
|
||||
build_resource_service)
|
||||
from langchain_community.agent_toolkits import GmailToolkit
|
||||
|
||||
from app.langchain_tools.agent_tools import (
|
||||
@ -26,6 +25,8 @@ from langgraph.checkpoint.memory import MemorySaver
|
||||
from typing import Annotated
|
||||
from typing_extensions import TypedDict
|
||||
from dotenv import load_dotenv
|
||||
from app.google_auth import get_google_credentials
|
||||
import os
|
||||
|
||||
load_dotenv()
|
||||
|
||||
@ -37,17 +38,27 @@ llm = ChatOpenAI(
|
||||
|
||||
|
||||
# # Configuración de Gmail
|
||||
toolkit = GmailToolkit()
|
||||
credentials = get_gmail_credentials(
|
||||
token_file="token.json",
|
||||
scopes=["https://mail.google.com/"],
|
||||
client_secrets_file="credentials.json",
|
||||
)
|
||||
api_resource = build_resource_service(credentials=credentials)
|
||||
toolkit = GmailToolkit(api_resource=api_resource)
|
||||
toolkit = None
|
||||
try:
|
||||
token_path = os.path.join('tokens', 'gmail_token.json')
|
||||
credentials = get_google_credentials(
|
||||
scopes=[
|
||||
"https://mail.google.com/",
|
||||
"https://www.googleapis.com/auth/calendar",
|
||||
"https://www.googleapis.com/auth/calendar.events"
|
||||
],
|
||||
credentials_file="google_credentials.json"
|
||||
)
|
||||
api_resource = build_resource_service(credentials=credentials)
|
||||
toolkit = GmailToolkit(api_resource=api_resource)
|
||||
except Exception as e:
|
||||
print(f"Warning: Could not initialize Gmail toolkit: {e}")
|
||||
|
||||
# # Crear herramientas
|
||||
tools = toolkit.get_tools()
|
||||
tools = []
|
||||
if toolkit:
|
||||
tools.extend(toolkit.get_tools())
|
||||
|
||||
search = TavilySearchResults(max_results=2)
|
||||
tools.extend([
|
||||
search, redact_email, list_calendar_events,
|
||||
@ -156,19 +167,26 @@ async def process_text(request: Request):
|
||||
|
||||
if __name__ == "__main__":
|
||||
config = {"configurable": {"thread_id": "thread-1", "recursion_limit": 50}}
|
||||
|
||||
# Modo interactivo por defecto
|
||||
import sys
|
||||
if "--server" not in sys.argv:
|
||||
while True:
|
||||
user_input = input("User: ")
|
||||
if user_input.lower() in ["quit", "exit", "q"]:
|
||||
print("Goodbye!")
|
||||
break
|
||||
|
||||
while True:
|
||||
user_input = input("User: ")
|
||||
if user_input.lower() in ["quit", "exit", "q"]:
|
||||
print("Goodbye!")
|
||||
break
|
||||
events = graph.stream({
|
||||
"messages": [("user", user_input)],
|
||||
"is_last_step": False},
|
||||
config, stream_mode="updates")
|
||||
|
||||
events = graph.stream({
|
||||
"messages": [("user", user_input)],
|
||||
"is_last_step": False},
|
||||
config, stream_mode="updates")
|
||||
|
||||
for event in events:
|
||||
if "agent" in event:
|
||||
print(
|
||||
f"\nAsistente: {event['agent']['messages'][-1].content}\n")
|
||||
for event in events:
|
||||
if "agent" in event:
|
||||
print(
|
||||
f"\nAsistente: {event['agent']['messages'][-1].content}\n")
|
||||
else:
|
||||
# Modo servidor con uvicorn
|
||||
import uvicorn
|
||||
uvicorn.run(app, host="0.0.0.0", port=8080)
|
||||
|
54
app/test_google_auth.py
Normal file
54
app/test_google_auth.py
Normal file
@ -0,0 +1,54 @@
|
||||
import unittest
|
||||
from unittest.mock import patch, MagicMock
|
||||
from google.oauth2.credentials import Credentials
|
||||
from app.google_auth import get_google_credentials
|
||||
import tempfile
|
||||
import shutil
|
||||
import os
|
||||
|
||||
|
||||
class TestGoogleAuth(unittest.TestCase):
|
||||
"""Test cases for Google authentication module."""
|
||||
|
||||
def setUp(self):
|
||||
"""Set up test fixtures."""
|
||||
self.mock_creds = MagicMock(spec=Credentials)
|
||||
self.test_scopes = ['https://www.googleapis.com/auth/gmail.readonly']
|
||||
self.test_dir = tempfile.mkdtemp()
|
||||
self.token_path = os.path.join(
|
||||
self.test_dir, 'tokens', 'gmail_token.json'
|
||||
)
|
||||
|
||||
def tearDown(self):
|
||||
"""Clean up after tests."""
|
||||
shutil.rmtree(self.test_dir)
|
||||
|
||||
@patch('os.path.exists')
|
||||
@patch('google.oauth2.credentials.Credentials.from_authorized_user_file')
|
||||
def test_get_credentials_from_token(self, mock_creds, mock_exists):
|
||||
"""Test getting credentials from existing token file."""
|
||||
mock_exists.return_value = True
|
||||
mock_creds.return_value = self.mock_creds
|
||||
self.mock_creds.valid = True
|
||||
|
||||
creds = get_google_credentials(self.test_scopes)
|
||||
self.assertEqual(creds, self.mock_creds)
|
||||
|
||||
@patch('os.path.exists')
|
||||
@patch('google.oauth2.credentials.Credentials.from_authorized_user_file')
|
||||
def test_refresh_expired_credentials(self, mock_creds, mock_exists):
|
||||
"""Test refreshing expired credentials."""
|
||||
mock_exists.return_value = True
|
||||
mock_creds.return_value = self.mock_creds
|
||||
self.mock_creds.valid = False
|
||||
self.mock_creds.expired = True
|
||||
self.mock_creds.refresh_token = True
|
||||
self.mock_creds.to_json.return_value = '{"mock": "credentials"}'
|
||||
|
||||
creds = get_google_credentials(self.test_scopes)
|
||||
self.assertTrue(self.mock_creds.refresh.called)
|
||||
self.assertEqual(creds, self.mock_creds)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main()
|
@ -1 +0,0 @@
|
||||
{"installed":{"client_id":"19011937557-bi5nh4afvg4tuqr87v6dp55qj9a9o1h2.apps.googleusercontent.com","project_id":"oc-aassistent","auth_uri":"https://accounts.google.com/o/oauth2/auth","token_uri":"https://oauth2.googleapis.com/token","auth_provider_x509_cert_url":"https://www.googleapis.com/oauth2/v1/certs","client_secret":"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","redirect_uris":["http://localhost"]}}
|
@ -1 +0,0 @@
|
||||
{"installed":{"client_id":"629922809906-pl9l1ipout6d5hh19ku50sfvnqgu8ir2.apps.googleusercontent.com","project_id":"calendar-424503","auth_uri":"https://accounts.google.com/o/oauth2/auth","token_uri":"https://oauth2.googleapis.com/token","auth_provider_x509_cert_url":"https://www.googleapis.com/oauth2/v1/certs","client_secret":"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","redirect_uris":["http://localhost"]}}
|
@ -13,8 +13,11 @@ services:
|
||||
- "8080:8080"
|
||||
volumes:
|
||||
- .:/code
|
||||
- ./google_credentials.json:/code/google_credentials.json
|
||||
- ./tokens:/code/tokens
|
||||
environment:
|
||||
- PYTHONUNBUFFERED=1
|
||||
- GOOGLE_APPLICATION_CREDENTIALS=/code/google_credentials.json
|
||||
command: >
|
||||
uvicorn app.server:app --host 0.0.0.0 --port 8080
|
||||
env_file:
|
||||
|
176
google_authentication.md
Normal file
176
google_authentication.md
Normal file
@ -0,0 +1,176 @@
|
||||
# Sistema de Autenticación con Google APIs
|
||||
|
||||
Este documento describe el sistema de autenticación centralizado implementado para interactuar con las APIs de Google (Calendar, Gmail, etc.) en proyectos de Python.
|
||||
|
||||
## Descripción General
|
||||
|
||||
El sistema utiliza OAuth 2.0 para autenticar y autorizar el acceso a las APIs de Google. La implementación está centralizada en un módulo `google_auth.py` que maneja:
|
||||
- Gestión de credenciales
|
||||
- Autenticación inicial
|
||||
- Renovación automática de tokens
|
||||
- Manejo de múltiples alcances (scopes)
|
||||
|
||||
## Archivos Necesarios
|
||||
|
||||
1. **google_credentials.json**: Credenciales de OAuth 2.0 del proyecto de Google Cloud
|
||||
- Contiene client_id, client_secret y otras configuraciones
|
||||
- Se obtiene desde Google Cloud Console
|
||||
- **¡IMPORTANTE!** No compartir ni commitear este archivo
|
||||
|
||||
2. **token.json**: Almacena los tokens de acceso y actualización
|
||||
- Se genera automáticamente en el primer uso
|
||||
- Se actualiza automáticamente cuando los tokens expiran
|
||||
- **¡IMPORTANTE!** No compartir ni commitear este archivo
|
||||
|
||||
3. **.gitignore**: Debe incluir los archivos sensibles
|
||||
```
|
||||
google_credentials.json
|
||||
token.json
|
||||
```
|
||||
|
||||
## Configuración Inicial
|
||||
|
||||
1. Crear un proyecto en Google Cloud Console
|
||||
2. Habilitar las APIs necesarias (Calendar, Gmail, etc.)
|
||||
3. Configurar la pantalla de consentimiento OAuth
|
||||
4. Crear credenciales OAuth 2.0
|
||||
5. Descargar el archivo de credenciales como `google_credentials.json`
|
||||
|
||||
## Implementación
|
||||
|
||||
### 1. Módulo Central de Autenticación (google_auth.py)
|
||||
|
||||
```python
|
||||
from google.oauth2.credentials import Credentials
|
||||
from google_auth_oauthlib.flow import InstalledAppFlow
|
||||
from google.auth.transport.requests import Request
|
||||
import os
|
||||
|
||||
def get_google_credentials(
|
||||
scopes=None,
|
||||
token_file='token.json',
|
||||
credentials_file='google_credentials.json'
|
||||
):
|
||||
"""
|
||||
Obtiene o refresca las credenciales de Google.
|
||||
|
||||
Args:
|
||||
scopes: Lista de scopes requeridos
|
||||
token_file: Ruta al archivo de token
|
||||
credentials_file: Ruta al archivo de credenciales
|
||||
|
||||
Returns:
|
||||
Credentials: Objeto de credenciales de Google
|
||||
"""
|
||||
creds = None
|
||||
if os.path.exists(token_file):
|
||||
creds = Credentials.from_authorized_user_file(token_file, scopes)
|
||||
|
||||
if not creds or not creds.valid:
|
||||
if creds and creds.expired and creds.refresh_token:
|
||||
creds.refresh(Request())
|
||||
else:
|
||||
flow = InstalledAppFlow.from_client_secrets_file(
|
||||
credentials_file, scopes)
|
||||
creds = flow.run_local_server(port=0)
|
||||
|
||||
with open(token_file, 'w') as token:
|
||||
token.write(creds.to_json())
|
||||
|
||||
return creds
|
||||
```
|
||||
|
||||
### 2. Uso en Funciones Específicas
|
||||
|
||||
```python
|
||||
from googleapiclient.discovery import build
|
||||
from google_auth import get_google_credentials
|
||||
|
||||
def create_calendar_event(title, start_time, end_time):
|
||||
# Definir los scopes necesarios
|
||||
SCOPES = ['https://www.googleapis.com/auth/calendar']
|
||||
|
||||
# Obtener credenciales
|
||||
creds = get_google_credentials(SCOPES)
|
||||
|
||||
# Construir el servicio
|
||||
service = build('calendar', 'v3', credentials=creds)
|
||||
|
||||
# Usar el servicio
|
||||
event = {
|
||||
'summary': title,
|
||||
'start': {'dateTime': start_time},
|
||||
'end': {'dateTime': end_time},
|
||||
}
|
||||
|
||||
return service.events().insert(calendarId='primary', body=event).execute()
|
||||
```
|
||||
|
||||
## Scopes Comunes
|
||||
|
||||
- Calendar: `https://www.googleapis.com/auth/calendar`
|
||||
- Gmail (lectura): `https://www.googleapis.com/auth/gmail.readonly`
|
||||
- Gmail (envío): `https://www.googleapis.com/auth/gmail.send`
|
||||
- Gmail (completo): `https://mail.google.com/`
|
||||
|
||||
## Mejores Prácticas
|
||||
|
||||
1. **Seguridad**:
|
||||
- Nunca commitear archivos de credenciales
|
||||
- Usar variables de entorno para configuraciones sensibles
|
||||
- Mantener los tokens seguros
|
||||
|
||||
2. **Manejo de Errores**:
|
||||
- Implementar reintentos para errores temporales
|
||||
- Manejar errores de autenticación específicamente
|
||||
- Logging apropiado de errores
|
||||
|
||||
3. **Gestión de Tokens**:
|
||||
- Implementar rotación de tokens
|
||||
- Monitorear la expiración de tokens
|
||||
- Manejar revocación de acceso
|
||||
|
||||
4. **Organización del Código**:
|
||||
- Mantener la autenticación centralizada
|
||||
- Separar la lógica de negocio de la autenticación
|
||||
- Usar clases para encapsular funcionalidad relacionada
|
||||
|
||||
## Integración con Frameworks
|
||||
|
||||
### LangChain
|
||||
```python
|
||||
from langchain.tools import Tool
|
||||
from google_auth import get_google_credentials
|
||||
|
||||
def create_google_tools():
|
||||
creds = get_google_credentials(SCOPES)
|
||||
service = build('calendar', 'v3', credentials=creds)
|
||||
|
||||
return [
|
||||
Tool(
|
||||
name="Calendar",
|
||||
func=lambda x: service.events().list().execute(),
|
||||
description="Access Google Calendar"
|
||||
)
|
||||
]
|
||||
```
|
||||
|
||||
## Solución de Problemas
|
||||
|
||||
1. **Token Expirado**:
|
||||
- El sistema renovará automáticamente los tokens expirados
|
||||
- Si falla, eliminar `token.json` y reautenticar
|
||||
|
||||
2. **Errores de Scope**:
|
||||
- Verificar que todos los scopes necesarios estén incluidos
|
||||
- Eliminar `token.json` si se agregan nuevos scopes
|
||||
|
||||
3. **Errores de Credenciales**:
|
||||
- Verificar que `google_credentials.json` sea válido
|
||||
- Confirmar que las APIs estén habilitadas en Google Cloud Console
|
||||
|
||||
## Referencias
|
||||
|
||||
- [Google OAuth 2.0 Documentation](https://developers.google.com/identity/protocols/oauth2)
|
||||
- [Google Calendar API](https://developers.google.com/calendar)
|
||||
- [Google Gmail API](https://developers.google.com/gmail/api)
|
1
google_credentials.json
Normal file
1
google_credentials.json
Normal file
@ -0,0 +1 @@
|
||||
{"installed":{"client_id":"123598969297-2686q5bm5ajnbk96dt9u3ajgc3o7441c.apps.googleusercontent.com","project_id":"onecluster-443003","auth_uri":"https://accounts.google.com/o/oauth2/auth","token_uri":"https://oauth2.googleapis.com/token","auth_provider_x509_cert_url":"https://www.googleapis.com/oauth2/v1/certs","client_secret":"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","redirect_uris":["http://localhost"]}}
|
4322
poetry.lock
generated
4322
poetry.lock
generated
File diff suppressed because it is too large
Load Diff
@ -9,18 +9,20 @@ packages = [
|
||||
]
|
||||
|
||||
[tool.poetry.dependencies]
|
||||
python = "^3.11"
|
||||
python = "^3.9"
|
||||
uvicorn = "^0.23.2"
|
||||
langserve = {extras = ["server"], version = ">=0.0.30"}
|
||||
pydantic = "<3"
|
||||
langgraph = "^0.2.28"
|
||||
langchain-community = "^0.3.1"
|
||||
langchain = "^0.3.8"
|
||||
langchain-community = "^0.3.8"
|
||||
langchain-openai = "^0.2.5"
|
||||
langchain-chroma = "^0.1.4"
|
||||
google = "^3.0.0"
|
||||
google-auth = "^2.35.0"
|
||||
google-auth-oauthlib = "^1.2.0"
|
||||
google-api-python-client = "^2.131.0"
|
||||
google-auth-oauthlib = "^1.1.0"
|
||||
google-auth-httplib2 = "^0.1.0"
|
||||
google-api-python-client = "^2.108.0"
|
||||
flake8 = "^7.1.1"
|
||||
httpx = "^0.27.2"
|
||||
pytest = "^8.3.3"
|
||||
@ -31,8 +33,6 @@ pytz = "^2024.2"
|
||||
telebot = "^0.0.5"
|
||||
pypdf = "^5.1.0"
|
||||
|
||||
|
||||
|
||||
[tool.poetry.group.dev.dependencies]
|
||||
langchain-cli = ">=0.0.15"
|
||||
|
||||
|
13
token.json
13
token.json
@ -1,13 +0,0 @@
|
||||
{
|
||||
"token": "ya29.a0AeDClZBerVykTwl0t1rHf8fN0nbGHgDP9meRfhsE4AhJVOQ9Ej8jtqjkSzCriMQwIdfDOp1TOpmXnVjoa_X-SSHMK3ZCiEHHNCIgfwUfmS7_OSnQJ4yXLafr3f2Meg42cW7HhV3TxBftHiWPiTFG5o_T2Bn4K6Zpa_SWgeUZaCgYKAScSARASFQHGX2MiEwE7j68vwAr6m-GQAqHHiA0175",
|
||||
"refresh_token": "1//05FoEzyGCAqH0CgYIARAAGAUSNwF-L9IruLo430JNxzhkCWSHp-PPFr0Y2mMHzxTNrWzQBpyUiyZnBXqokJc9bzBbiVSILQ03usA",
|
||||
"token_uri": "https://oauth2.googleapis.com/token",
|
||||
"client_id": "629922809906-nmiojp1sasq7pkbk6i90c17de77h0cbb.apps.googleusercontent.com",
|
||||
"client_secret": "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",
|
||||
"scopes": [
|
||||
"https://mail.google.com/"
|
||||
],
|
||||
"universe_domain": "googleapis.com",
|
||||
"account": "",
|
||||
"expiry": "2024-11-13T01:18:38.011810Z"
|
||||
}
|
13
token_2.json
13
token_2.json
@ -1,13 +0,0 @@
|
||||
{
|
||||
"token": "ya29.a0AeDClZBnmECQZamkSnv3iKFw5OSabl7Mo7C4dcHwjL0OASSsd6Xla09j18DyEhhJh-3snD0r0UmtYUpcrwgjMwC7xMxJ_QEofSeI5MiHDZcJ5-SLQMYlvd7tF_zXlxVLKlK0JUQfxKhAhOzmBUmOEn--tqNk3yeyHXmhQp7CaCgYKAToSARASFQHGX2Mi25qlKH3lr7qtNx_OsfLG3A0175",
|
||||
"refresh_token": "1//054ZrZWL7352NCgYIARAAGAUSNwF-L9Ir2tEK43tjNsBud-KiOAfizQcoG5DdPvpKy4DQ8wLMN4WnyiPE5zdNbp6qPopfnocPny4",
|
||||
"token_uri": "https://oauth2.googleapis.com/token",
|
||||
"client_id": "629922809906-pl9l1ipout6d5hh19ku50sfvnqgu8ir2.apps.googleusercontent.com",
|
||||
"client_secret": "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",
|
||||
"scopes": [
|
||||
"https://www.googleapis.com/auth/calendar"
|
||||
],
|
||||
"universe_domain": "googleapis.com",
|
||||
"account": "",
|
||||
"expiry": "2024-11-13T01:20:22.642348Z"
|
||||
}
|
15
tokens/gmail_token.json
Normal file
15
tokens/gmail_token.json
Normal file
@ -0,0 +1,15 @@
|
||||
{
|
||||
"token": "ya29.a0AeDClZA5S3XiL0XEA5h7hXqNU1zh-QaK_S2dMmf6QYNiwyewk7dsMd-QzvTpObkFnKT8dJDRW-dNCR9NhmJFD28CgsgIWEBHt4gPjdoVWVLg6YI9CQi3SdAxL2spnbBVugk5rlOOhMSj6O318hM3yCynUN_hQ-szL3nvoTz_aCgYKAbMSARASFQHGX2MinJv72L34S299HgpKR5VM6A0175",
|
||||
"refresh_token": "1//05v6o5t9wAP18CgYIARAAGAUSNwF-L9IrYkA9pEhJx2HtRbKL9rR-HV8N-3H5xMWCdHCf1WgyCMGVa3HLulUoDP6ILW84wOw3zJM",
|
||||
"token_uri": "https://oauth2.googleapis.com/token",
|
||||
"client_id": "123598969297-2686q5bm5ajnbk96dt9u3ajgc3o7441c.apps.googleusercontent.com",
|
||||
"client_secret": "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",
|
||||
"scopes": [
|
||||
"https://www.googleapis.com/auth/calendar",
|
||||
"https://www.googleapis.com/auth/gmail.readonly",
|
||||
"https://www.googleapis.com/auth/gmail.send",
|
||||
"https://www.googleapis.com/auth/calendar.events",
|
||||
"https://www.googleapis.com/auth/calendar.readonly"
|
||||
],
|
||||
"expiry": "2024-11-27T07:44:50.818190"
|
||||
}
|
Loading…
Reference in New Issue
Block a user