Nancy1906 commited on
Commit
f05f423
·
verified ·
1 Parent(s): 0160046

dddddddddd

Browse files
Files changed (1) hide show
  1. my_tools.py +110 -30
my_tools.py CHANGED
@@ -3,92 +3,172 @@ import math
3
  import pandas as pd
4
  from duckduckgo_search import DDGS
5
  import wikipedia
 
6
  from llama_index.core.tools import FunctionTool
7
  from llama_index.core.agent import ReActAgent
8
- from llama_index.core.llms.types import ChatMessage, LLMMetadata, LLM
9
- from llama_index.core.callbacks import CallbackManager, LlamaDebugHandler
 
 
 
10
  import google.generativeai as genai
11
 
12
- print("LlamaIndex version:", llama_index.__version__)
13
 
14
  # --- Gemini LLM personalizado ---
15
  class GeminiLLM(LLM):
16
  def __init__(self, model="models/gemini-1.5-flash-latest"):
17
- genai.configure(api_key=os.getenv("GEMINI_API_KEY"))
 
 
 
 
18
  self.model = genai.GenerativeModel(model)
 
 
 
19
 
20
  def chat(self, messages: list[ChatMessage], **kwargs):
21
- prompt = "\n".join([
22
- ("User: " if m.role == "user" else "Assistant: ") + m.content
23
- for m in messages
24
- ]) + "\nAssistant:"
25
- resp = self.model.generate_content(prompt)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
26
  return ChatMessage(role="assistant", content=resp.text)
27
 
28
  @property
29
  def metadata(self) -> LLMMetadata:
30
  return LLMMetadata(
31
- context_window=8192,
32
- num_output=1024,
33
  is_chat_model=True,
34
- is_function_calling_model=False,
35
  model_name="gemini-1.5-flash-latest"
36
  )
37
 
38
  @property
39
- def callback_manager(self):
40
- return CallbackManager([LlamaDebugHandler()])
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
41
 
42
  llm = GeminiLLM()
43
 
44
  # --- Herramientas ---
45
  def buscar_web(query: str) -> str:
46
- with DDGS() as ddgs:
47
- results = list(ddgs.text(query, region='es-es', safesearch='moderate', timelimit='y', max_results=3))
48
- if results:
49
- return "\n".join([f"{r['title']}: {r['body']}" for r in results])
50
- return "No se encontraron resultados."
 
 
 
 
 
51
 
52
  search_tool = FunctionTool.from_defaults(
53
  fn=buscar_web,
54
  name="web_search",
55
- description="Busca en la web utilizando DuckDuckGo."
56
  )
57
 
58
  def get_wikipedia_summary(query: str) -> str:
 
59
  try:
 
60
  return wikipedia.summary(query, sentences=3, auto_suggest=False)
 
 
 
 
61
  except Exception as e:
62
  return f"Error al buscar en Wikipedia: {e}"
63
 
64
  wikipedia_tool = FunctionTool.from_defaults(
65
  fn=get_wikipedia_summary,
66
  name="wikipedia_lookup",
67
- description="Busca un resumen breve de un tema en Wikipedia."
68
  )
69
 
70
  def calcular_expresion(expr: str) -> str:
 
 
 
 
 
71
  try:
72
- result = eval(expr, {"__builtins__": {}}, math.__dict__)
 
 
 
 
73
  return str(result)
74
  except Exception as e:
75
- return f"Error de cálculo: {e}"
76
 
77
  calculator_tool = FunctionTool.from_defaults(
78
  fn=calcular_expresion,
79
  name="calculadora",
80
- description="Resuelve expresiones matemáticas, incluyendo funciones trigonométricas, logaritmos, potencias, etc."
81
  )
82
 
83
  # --- Agente ---
84
  alfred_agent = ReActAgent.from_tools(
85
  tools=[search_tool, wikipedia_tool, calculator_tool],
86
  llm=llm,
87
- verbose=False
88
  )
89
 
90
- def basic_agent_response(question):
91
- response = alfred_agent.query(question)
92
- return str(response)
93
-
94
-
 
 
 
 
 
3
  import pandas as pd
4
  from duckduckgo_search import DDGS
5
  import wikipedia
6
+ import llama_index # <--- AÑADIR ESTA LÍNEA
7
  from llama_index.core.tools import FunctionTool
8
  from llama_index.core.agent import ReActAgent
9
+ # --- CORREGIR ESTA IMPORTACIÓN ---
10
+ # from llama_index.core.llms.types import ChatMessage, LLMMetadata, LLM
11
+ from llama_index.core.llms import ChatMessage, LLMMetadata, LLM # <--- NUEVA IMPORTACIÓN
12
+ from llama_index.core.callbacks import CallbackManager # LlamaDebugHandler no se usa directamente aquí, pero sí en el property
13
+ from llama_index.core.callbacks.llama_debug import LlamaDebugHandler # Importación completa si se usa directamente
14
  import google.generativeai as genai
15
 
16
+ print("LlamaIndex version:", llama_index.__version__) # Ahora esto debería funcionar
17
 
18
  # --- Gemini LLM personalizado ---
19
  class GeminiLLM(LLM):
20
  def __init__(self, model="models/gemini-1.5-flash-latest"):
21
+ super().__init__() # <--- Buena práctica llamar al __init__ de la clase base
22
+ gemini_api_key = os.getenv("GEMINI_API_KEY")
23
+ if not gemini_api_key:
24
+ raise ValueError("GEMINI_API_KEY environment variable not set.")
25
+ genai.configure(api_key=gemini_api_key)
26
  self.model = genai.GenerativeModel(model)
27
+ # El callback_manager se puede inicializar aquí si siempre será el mismo
28
+ self._callback_manager = CallbackManager([LlamaDebugHandler()])
29
+
30
 
31
  def chat(self, messages: list[ChatMessage], **kwargs):
32
+ # Construir el prompt compatible con Gemini
33
+ # Gemini espera una lista de mensajes con roles 'user' y 'model' (para historial)
34
+ # El último mensaje de 'user' es el prompt actual.
35
+ gemini_messages = []
36
+ system_prompt = "" # LlamaIndex podría pasar un system_prompt vía kwargs o primer mensaje
37
+
38
+ prompt_parts = []
39
+ for m in messages:
40
+ # Adaptar roles si es necesario, Gemini usa 'user' y 'model'
41
+ role = "user" if m.role == "user" else "model" # Asumimos que 'assistant' mapea a 'model'
42
+ prompt_parts.append(f"{role}: {m.content}")
43
+
44
+ # El prompt final para generate_content usualmente es solo el último mensaje del usuario,
45
+ # con el historial implícito si el modelo es conversacional.
46
+ # Para el modelo genai.GenerativeModel, el historial se maneja a través de start_chat.
47
+ # Si solo hacemos una llamada, el prompt concatenado está bien, pero es mejor usar la API de chat.
48
+
49
+ # Usando start_chat para mantener el contexto si es una conversación más larga
50
+ # Esto es más robusto si el agente hace múltiples llamadas al LLM con historial
51
+ chat_session = self.model.start_chat(
52
+ history=[{'role': msg.role if msg.role in ['user', 'model'] else ('user' if msg.role == 'assistant' else 'user'), 'parts': [msg.content]} for msg in messages[:-1]]
53
+ )
54
+ resp = chat_session.send_message(messages[-1].content)
55
+
56
+ # Alternativa simple si solo es un turno (como en el prompt original construido)
57
+ # prompt_str = "\n".join(prompt_parts) + "\nAssistant:" # O model:
58
+ # resp = self.model.generate_content(prompt_str)
59
+
60
  return ChatMessage(role="assistant", content=resp.text)
61
 
62
  @property
63
  def metadata(self) -> LLMMetadata:
64
  return LLMMetadata(
65
+ context_window=8192, # Consultar la documentación de gemini-1.5-flash para el valor exacto
66
+ num_output=1024, # Consultar la documentación de gemini-1.5-flash para el valor exacto
67
  is_chat_model=True,
68
+ is_function_calling_model=False, # Gemini tiene su propio sistema de tools/function calling
69
  model_name="gemini-1.5-flash-latest"
70
  )
71
 
72
  @property
73
+ def callback_manager(self): # Propiedad para acceder al callback_manager
74
+ return self._callback_manager
75
+
76
+ # Necesario para LLM. LlamaIndex espera estos métodos async aunque no los usemos activamente para todo.
77
+ async def achat(self, messages: list[ChatMessage], **kwargs):
78
+ # Implementación asíncrona simple (puede ser igual a la síncrona si la SDK no es async nativa fácil)
79
+ # o usar asyncio.to_thread si la SDK de Gemini es bloqueante
80
+ return self.chat(messages, **kwargs)
81
+
82
+ async def astream_chat(self, messages: list[ChatMessage], **kwargs):
83
+ # Placeholder para streaming asíncrono
84
+ # Por ahora, podemos hacer que devuelva un generador que produce la respuesta completa de una vez.
85
+ response = self.chat(messages, **kwargs)
86
+ async def gen():
87
+ yield response
88
+ return gen()
89
+
90
+ def stream_chat(self, messages: list[ChatMessage], **kwargs):
91
+ # Placeholder para streaming síncrono
92
+ response = self.chat(messages, **kwargs)
93
+ def gen():
94
+ yield response
95
+ return gen()
96
+
97
 
98
  llm = GeminiLLM()
99
 
100
  # --- Herramientas ---
101
  def buscar_web(query: str) -> str:
102
+ """Busca en la web utilizando DuckDuckGo y devuelve los 3 primeros resultados."""
103
+ try:
104
+ with DDGS() as ddgs:
105
+ results = list(ddgs.text(query, region='es-es', safesearch='moderate', timelimit='y', max_results=3))
106
+ if results:
107
+ return "\n".join([f"{r['title']}: {r['body']}" for r in results])
108
+ return "No se encontraron resultados."
109
+ except Exception as e:
110
+ return f"Error al buscar en la web: {e}"
111
+
112
 
113
  search_tool = FunctionTool.from_defaults(
114
  fn=buscar_web,
115
  name="web_search",
116
+ description="Busca en la web utilizando DuckDuckGo para obtener información actualizada o temas generales."
117
  )
118
 
119
  def get_wikipedia_summary(query: str) -> str:
120
+ """Busca un resumen breve de un tema en Wikipedia (primeras 3 frases)."""
121
  try:
122
+ wikipedia.set_lang("es")
123
  return wikipedia.summary(query, sentences=3, auto_suggest=False)
124
+ except wikipedia.exceptions.PageError:
125
+ return f"La página '{query}' no existe en Wikipedia."
126
+ except wikipedia.exceptions.DisambiguationError as e:
127
+ return f"La búsqueda '{query}' es ambigua. Opciones posibles: {e.options[:5]}"
128
  except Exception as e:
129
  return f"Error al buscar en Wikipedia: {e}"
130
 
131
  wikipedia_tool = FunctionTool.from_defaults(
132
  fn=get_wikipedia_summary,
133
  name="wikipedia_lookup",
134
+ description="Busca un resumen breve de un tema específico en Wikipedia."
135
  )
136
 
137
  def calcular_expresion(expr: str) -> str:
138
+ """
139
+ Evalúa expresiones matemáticas.
140
+ Ejemplos: '2+2', 'math.sqrt(16)', 'pow(2,3)', '37 * 19'.
141
+ Funciones math disponibles: sqrt, pow, sin, cos, tan, log, log10, etc.
142
+ """
143
  try:
144
+ # Un entorno seguro para eval(), permitiendo solo funciones de math
145
+ allowed_names = {k: v for k, v in math.__dict__.items() if not k.startswith("__")}
146
+ allowed_names["math"] = math # Para poder usar math.sqrt() etc.
147
+
148
+ result = eval(expr, {"__builtins__": {}}, allowed_names)
149
  return str(result)
150
  except Exception as e:
151
+ return f"Error de cálculo al evaluar '{expr}': {e}"
152
 
153
  calculator_tool = FunctionTool.from_defaults(
154
  fn=calcular_expresion,
155
  name="calculadora",
156
+ description="Resuelve expresiones matemáticas. Utiliza 'math.funcion()' para funciones como sqrt, pow, sin, etc. Ej: 'math.sqrt(16)', '37*19'."
157
  )
158
 
159
  # --- Agente ---
160
  alfred_agent = ReActAgent.from_tools(
161
  tools=[search_tool, wikipedia_tool, calculator_tool],
162
  llm=llm,
163
+ verbose=True # <--- Poner a True para debugging inicial es muy útil
164
  )
165
 
166
+ def basic_agent_response(question: str) -> str:
167
+ print(f"🤖 Alfred recibió la pregunta: {question}")
168
+ try:
169
+ response = alfred_agent.query(question)
170
+ print(f"📝 Respuesta de Alfred: {response}")
171
+ return str(response) # <--- CORREGIDO: Eliminado el punto extra
172
+ except Exception as e:
173
+ print(f"💥 Error en Alfred al procesar la pregunta '{question}': {e}")
174
+ return f"Error del agente al procesar la pregunta: {e}"