benfls commited on
Commit
9333f04
·
1 Parent(s): 062a4b0

Ajout tools de recherche et structuration agent

Browse files
.env-example DELETED
@@ -1,6 +0,0 @@
1
- # Configurez vos clés API ici et renommez ce fichier en .env
2
- OPENAI_API_KEY=votre_clé_api_openai_ici
3
-
4
- # Autres configurations
5
- # TEMPERATURE=0.7
6
- # MAX_TOKENS=4096
 
 
 
 
 
 
 
.gitignore ADDED
@@ -0,0 +1,115 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Byte-compiled / optimized / DLL files
2
+ __pycache__/
3
+ *.py[cod]
4
+ *$py.class
5
+ *.so
6
+
7
+ # Distribution / packaging
8
+ .Python
9
+ build/
10
+ develop-eggs/
11
+ dist/
12
+ downloads/
13
+ eggs/
14
+ .eggs/
15
+ lib/
16
+ lib64/
17
+ parts/
18
+ sdist/
19
+ var/
20
+ wheels/
21
+ *.egg-info/
22
+ .installed.cfg
23
+ *.egg
24
+
25
+ # Virtual environments
26
+ venv/
27
+ ENV/
28
+ env/
29
+ .env
30
+ .venv
31
+ env.bak/
32
+ venv.bak/
33
+ .python-version
34
+
35
+ # Unit test / coverage reports
36
+ htmlcov/
37
+ .tox/
38
+ .nox/
39
+ .coverage
40
+ .coverage.*
41
+ .cache
42
+ nosetests.xml
43
+ coverage.xml
44
+ *.cover
45
+ .hypothesis/
46
+ .pytest_cache/
47
+ pytest-*.xml
48
+
49
+ # Jupyter Notebook
50
+ .ipynb_checkpoints
51
+
52
+ # IPython
53
+ profile_default/
54
+ ipython_config.py
55
+
56
+ # Logs
57
+ *.log
58
+ logs/
59
+ log/
60
+
61
+ # IDE specific files
62
+ .idea/
63
+ .vscode/
64
+ *.swp
65
+ *.swo
66
+ *~
67
+ .DS_Store
68
+ .project
69
+ .pydevproject
70
+ .settings/
71
+ .vs/
72
+ *.sublime-project
73
+ *.sublime-workspace
74
+
75
+ # Database
76
+ *.db
77
+ *.rdb
78
+ *.sqlite
79
+ *.sqlite3
80
+
81
+ # Environment variables
82
+ .env
83
+ .env.local
84
+ .env.development.local
85
+ .env.test.local
86
+ .env.production.local
87
+
88
+ # macOS specific
89
+ .DS_Store
90
+ .AppleDouble
91
+ .LSOverride
92
+ Icon
93
+ ._*
94
+ .DocumentRevisions-V100
95
+ .fseventsd
96
+ .Spotlight-V100
97
+ .TemporaryItems
98
+ .Trashes
99
+ .VolumeIcon.icns
100
+ .com.apple.timemachine.donotpresent
101
+
102
+ # AI/model files
103
+ *.h5
104
+ *.pb
105
+ *.onnx
106
+ *.tflite
107
+ *.pt
108
+ *.pth
109
+ *.weights
110
+
111
+ # Temporary files
112
+ tmp/
113
+ temp/
114
+ .tmp
115
+ *.tmp
agent.py CHANGED
@@ -1,18 +1,18 @@
1
  import os
2
  import logging
3
- from typing import List, Optional, Dict, Any
 
 
 
 
 
 
 
 
4
 
5
- from langchain.agents import AgentExecutor
6
- from langchain.agents.openai_functions_agent.base import OpenAIFunctionsAgent
7
- from langchain.schema import SystemMessage, HumanMessage
8
- from langchain.prompts import MessagesPlaceholder
9
- from langchain_openai import ChatOpenAI
10
- from langchain.memory import ConversationBufferMemory
11
- from langchain_core.messages import AIMessage
12
- from langchain.callbacks.manager import CallbackManager
13
- from langchain.callbacks.streaming_stdout import StreamingStdOutCallbackHandler
14
 
15
- from tools import WebSearchTool, WebContentTool, CurrentDateTool, JsonParserTool, CalculatorTool
16
  import config
17
 
18
  # Configuration du logging
@@ -23,61 +23,30 @@ logging.basicConfig(
23
  )
24
  logger = logging.getLogger("Agent")
25
 
26
- class AdvancedAgent:
27
  """
28
- Agent avancé utilisant LangChain et des outils personnalisés
29
- pour répondre aux questions de manière plus intelligente.
30
  """
31
 
32
- def __init__(self,
33
- model_name: str = config.DEFAULT_MODEL,
34
- api_key: Optional[str] = None,
35
- temperature: float = config.TEMPERATURE,
36
- system_message: str = config.DEFAULT_SYSTEM_MESSAGE,
37
- verbose: bool = True):
38
  """
39
  Initialise l'agent avec ses outils et sa configuration.
40
-
41
- Args:
42
- model_name: Nom du modèle à utiliser
43
- api_key: Clé API OpenAI (prend celle de l'environnement si non spécifiée)
44
- temperature: Valeur de température pour la génération de texte
45
- system_message: Message système pour l'agent
46
- verbose: Afficher les logs détaillés
47
  """
48
- self.model_name = model_name
49
- self.temperature = temperature
50
- self.system_message = system_message
51
  self.verbose = verbose
52
 
53
- # Utilise la clé API fournie ou celle des variables d'environnement
54
- self.api_key = api_key or config.OPENAI_API_KEY
55
- if not self.api_key:
56
- logger.warning("Aucune clé API OpenAI trouvée. Certaines fonctionnalités peuvent ne pas fonctionner.")
57
-
58
  # Initialisation du modèle
59
- callback_manager = CallbackManager([StreamingStdOutCallbackHandler()]) if verbose else None
60
- self.llm = ChatOpenAI(
61
- model=self.model_name,
62
- temperature=self.temperature,
63
- api_key=self.api_key,
64
- verbose=self.verbose,
65
- callback_manager=callback_manager
66
  )
67
 
68
  # Charger les outils
69
  self.tools = self._setup_tools()
70
 
71
- # Initialiser la mémoire
72
- self.memory = ConversationBufferMemory(
73
- memory_key="chat_history",
74
- return_messages=True
75
- )
76
-
77
- # Créer l'agent
78
- self.agent = self._create_agent()
79
 
80
- def _setup_tools(self) -> List:
81
  """Configure et retourne les outils disponibles pour l'agent"""
82
  tools = []
83
 
@@ -86,47 +55,51 @@ class AdvancedAgent:
86
  tools.append(WebSearchTool())
87
  tools.append(WebContentTool())
88
 
89
- if config.ENABLE_CALCULATOR:
90
- tools.append(CalculatorTool())
91
 
92
- if config.ENABLE_DATE_TOOL:
93
- tools.append(CurrentDateTool())
94
-
95
- # Toujours ajouter l'outil JSON parser
96
- tools.append(JsonParserTool())
97
 
98
  logger.info(f"Agent initialisé avec {len(tools)} outils")
99
  return tools
100
 
101
- def _create_agent(self) -> AgentExecutor:
102
- """Crée et configure l'exécuteur d'agent"""
103
 
104
  # Définir le prompt
105
- prompt = [
106
- SystemMessage(content=self.system_message),
107
  MessagesPlaceholder(variable_name="chat_history"),
108
- HumanMessage(content="{input}"),
109
  MessagesPlaceholder(variable_name="agent_scratchpad"),
110
- ]
111
 
112
- # Créer l'agent
113
- agent = OpenAIFunctionsAgent(
114
- llm=self.llm,
115
- tools=self.tools,
116
- prompt=prompt
117
- )
118
 
119
- # Créer l'exécuteur d'agent
120
- agent_executor = AgentExecutor(
121
- agent=agent,
122
- tools=self.tools,
123
- memory=self.memory,
124
- verbose=self.verbose,
125
- max_iterations=10,
126
- early_stopping_method="generate"
 
 
 
 
 
 
 
 
127
  )
 
128
 
129
- return agent_executor
 
130
 
131
  def __call__(self, question: str) -> str:
132
  """
@@ -144,45 +117,17 @@ class AdvancedAgent:
144
  try:
145
  logger.info(f"Question reçue: {question[:50]}...")
146
 
147
- # Exécuter l'agent
148
- response = self.agent.invoke({"input": question})
 
 
149
 
150
- # Extraire la réponse
151
- answer = response.get("output", "Je n'ai pas pu générer de réponse.")
152
-
153
- logger.info(f"Réponse générée: {answer[:50]}...")
154
- return answer
155
 
156
  except Exception as e:
157
  logger.error(f"Erreur lors du traitement de la question: {str(e)}")
158
- return f"Désolé, une erreur s'est produite: {str(e)}"
159
-
160
- def reset_memory(self):
161
- """Réinitialise la mémoire de l'agent"""
162
- self.memory.clear()
163
- logger.info("Mémoire de l'agent réinitialisée")
164
-
165
- def get_last_interactions(self, count: int = 5) -> List[Dict[str, Any]]:
166
- """
167
- Retourne les dernières interactions de l'agent
168
-
169
- Args:
170
- count: Nombre d'interactions à retourner
171
-
172
- Returns:
173
- Liste des dernières interactions
174
- """
175
- history = self.memory.chat_memory.messages
176
-
177
- interactions = []
178
- for i in range(0, len(history), 2):
179
- if i + 1 < len(history):
180
- human_msg = history[i].content if hasattr(history[i], 'content') else str(history[i])
181
- ai_msg = history[i+1].content if hasattr(history[i+1], 'content') else str(history[i+1])
182
-
183
- interactions.append({
184
- "question": human_msg,
185
- "answer": ai_msg
186
- })
187
-
188
- return interactions[-count:]
 
1
  import os
2
  import logging
3
+ from typing import List, Dict, Any, Tuple
4
+ from langchain.tools import BaseTool
5
+ from langchain_core.messages import HumanMessage, AIMessage, SystemMessage
6
+ from langchain_core.prompts import ChatPromptTemplate, MessagesPlaceholder
7
+ from langchain_core.output_parsers import StrOutputParser
8
+ from langchain_core.runnables import RunnablePassthrough
9
+ from langchain_huggingface import HuggingFaceEndpoint
10
+ from langgraph.graph import START, StateGraph, MessagesState
11
+ from langgraph.prebuilt import tools_condition, ToolNode
12
 
13
+ from tools import WebSearchTool, WebContentTool
14
+ #, AudioToTextTool, SpreadsheetParserTool, StringUtilitiesTool
 
 
 
 
 
 
 
15
 
 
16
  import config
17
 
18
  # Configuration du logging
 
23
  )
24
  logger = logging.getLogger("Agent")
25
 
26
+ class LangGraphAgent:
27
  """
28
+ Agent avancé utilisant LangGraph et le modèle Qwen3-30B-A3B
 
29
  """
30
 
31
+ def __init__(self, verbose: bool = True):
 
 
 
 
 
32
  """
33
  Initialise l'agent avec ses outils et sa configuration.
 
 
 
 
 
 
 
34
  """
 
 
 
35
  self.verbose = verbose
36
 
 
 
 
 
 
37
  # Initialisation du modèle
38
+ self.llm = HuggingFaceEndpoint(
39
+ endpoint_url=f"https://api-inference.huggingface.co/models/{config.MODEL_NAME}",
40
+ huggingfacehub_api_token=config.HUGGINGFACE_API_KEY,
 
 
 
 
41
  )
42
 
43
  # Charger les outils
44
  self.tools = self._setup_tools()
45
 
46
+ # Créer le graphe d'exécution
47
+ self.workflow = self._create_workflow()
 
 
 
 
 
 
48
 
49
+ def _setup_tools(self) -> List[BaseTool]:
50
  """Configure et retourne les outils disponibles pour l'agent"""
51
  tools = []
52
 
 
55
  tools.append(WebSearchTool())
56
  tools.append(WebContentTool())
57
 
58
+ # if config.ENABLE_AUDIO_TO_TEXT:
59
+ # tools.append(AudioToTextTool())
60
 
61
+ # if config.ENABLE_SPREADSHEET_PARSER:
62
+ # tools.append(SpreadsheetParserTool())
63
+
64
+ # if config.ENABLE_STRING_UTILITIES:
65
+ # tools.append(StringUtilitiesTool())
66
 
67
  logger.info(f"Agent initialisé avec {len(tools)} outils")
68
  return tools
69
 
70
+ def _create_workflow(self) -> StateGraph:
71
+ """Crée le graphe d'exécution de l'agent"""
72
 
73
  # Définir le prompt
74
+ prompt = ChatPromptTemplate.from_messages([
75
+ ("system", config.DEFAULT_SYSTEM_MESSAGE),
76
  MessagesPlaceholder(variable_name="chat_history"),
77
+ ("human", "{input}"),
78
  MessagesPlaceholder(variable_name="agent_scratchpad"),
79
+ ])
80
 
 
 
 
 
 
 
81
 
82
+ # Définir les nodes
83
+ def assistant(state: MessagesState):
84
+ return {"messages": [self.llm.invoke(state["messages"])]}
85
+
86
+ # Créer le graphe
87
+ builder = StateGraph(MessagesState)
88
+
89
+ # Ajouter les nodes
90
+ builder.add_node("assistant", assistant)
91
+ builder.add_node("tools", ToolNode(self.tools))
92
+
93
+ # Ajouter les edges
94
+ builder.add_edge(START, "assistant")
95
+ builder.add_conditional_edges(
96
+ "assistant",
97
+ tools_condition,
98
  )
99
+ builder.add_edge("tools", "assistant")
100
 
101
+ # Compiler le graphe
102
+ return builder.compile()
103
 
104
  def __call__(self, question: str) -> str:
105
  """
 
117
  try:
118
  logger.info(f"Question reçue: {question[:50]}...")
119
 
120
+ # Exécuter le workflow
121
+ result = self.workflow.invoke({
122
+ "messages": [HumanMessage(content=question)]
123
+ })
124
 
125
+ # Extraire la réponse finale
126
+ final_message = result["messages"][-1].content
127
+ if "FINAL ANSWER:" in final_message:
128
+ return final_message.split("FINAL ANSWER:")[1].strip()
129
+ return final_message
130
 
131
  except Exception as e:
132
  logger.error(f"Erreur lors du traitement de la question: {str(e)}")
133
+ return f"Désolé, une erreur s'est produite: {str(e)}"
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
app.py CHANGED
@@ -6,7 +6,7 @@ import pandas as pd
6
  from dotenv import load_dotenv
7
 
8
  # Importer notre agent avancé
9
- from agent import AdvancedAgent
10
  import config
11
 
12
  # Charger les variables d'environnement
@@ -23,7 +23,7 @@ def initialize_agent():
23
  global agent
24
  if agent is None:
25
  try:
26
- agent = AdvancedAgent(verbose=False)
27
  return True
28
  except Exception as e:
29
  print(f"Erreur lors de l'initialisation de l'agent: {e}")
 
6
  from dotenv import load_dotenv
7
 
8
  # Importer notre agent avancé
9
+ from agent import LangGraphAgent
10
  import config
11
 
12
  # Charger les variables d'environnement
 
23
  global agent
24
  if agent is None:
25
  try:
26
+ agent = LangGraphAgent(verbose=False)
27
  return True
28
  except Exception as e:
29
  print(f"Erreur lors de l'initialisation de l'agent: {e}")
config.py CHANGED
@@ -7,9 +7,9 @@ load_dotenv()
7
  # Configuration de l'API
8
  DEFAULT_API_URL = "https://agents-course-unit4-scoring.hf.space"
9
 
10
- # Configuration de l'agent
11
- OPENAI_API_KEY = os.getenv("OPENAI_API_KEY", "")
12
- DEFAULT_MODEL = "gpt-3.5-turbo" # Modèle par défaut
13
 
14
  # Configurations diverses
15
  MAX_TOKENS = 4096
@@ -18,13 +18,13 @@ RETRY_ATTEMPTS = 3
18
 
19
  # Configuration des outils
20
  ENABLE_WEB_SEARCH = True
21
- ENABLE_CALCULATOR = True
22
- ENABLE_DATE_TOOL = True
 
23
 
24
  # Message système par défaut pour l'agent
25
- DEFAULT_SYSTEM_MESSAGE = """You are a general AI assistant. I will ask you a question.
26
- Report your thoughts, and finish your answer with the following template: FINAL ANSWER: [YOUR FINAL ANSWER].
27
- YOUR FINAL ANSWER should be a number OR as few words as possible OR a comma separated list of numbers and/or strings.
28
- If you are asked for a number, don't use comma to write your number neither use units such as $ or percent sign unless specified otherwise.
29
- If you are asked for a string, don't use articles, neither abbreviations (e.g. for cities), and write the digits in plain text unless specified otherwise.
30
- If you are asked for a comma separated list, apply the above rules depending of whether the element to be put in the list is a number or a string. """
 
7
  # Configuration de l'API
8
  DEFAULT_API_URL = "https://agents-course-unit4-scoring.hf.space"
9
 
10
+ # Configuration du modèle
11
+ MODEL_NAME = "Qwen/Qwen3-30B-A3B"
12
+ HUGGINGFACE_API_KEY = os.getenv("HUGGINGFACE_API_KEY", "")
13
 
14
  # Configurations diverses
15
  MAX_TOKENS = 4096
 
18
 
19
  # Configuration des outils
20
  ENABLE_WEB_SEARCH = True
21
+ ENABLE_AUDIO_TO_TEXT = False
22
+ ENABLE_SPREADSHEET_PARSER = False
23
+ ENABLE_STRING_UTILITIES = False
24
 
25
  # Message système par défaut pour l'agent
26
+ DEFAULT_SYSTEM_MESSAGE = """You are a helpful assistant tasked with answering questions using a set of tools.
27
+ Now, I will ask you a question. Report your thoughts, and finish your answer with the following template:
28
+ FINAL ANSWER: [YOUR FINAL ANSWER].
29
+ YOUR FINAL ANSWER should be a number OR as few words as possible OR a comma separated list of numbers and/or strings. If you are asked for a number, don't use comma to write your number neither use units such as $ or percent sign unless specified otherwise. If you are asked for a string, don't use articles, neither abbreviations (e.g. for cities), and write the digits in plain text unless specified otherwise. If you are asked for a comma separated list, apply the above rules depending of whether the element to be put in the list is a number or a string.
30
+ Your answer should only start with "FINAL ANSWER: ", then follows with the answer. """
 
requirements.txt CHANGED
@@ -1,8 +1,14 @@
1
  gradio
 
2
  requests
3
  langchain
4
- langchain-openai
5
  langchain-core
 
 
6
  python-dotenv
7
  bs4
8
- pandas
 
 
 
 
1
  gradio
2
+ gradio[oauth]
3
  requests
4
  langchain
5
+ langchain-community
6
  langchain-core
7
+ langchain-huggingface
8
+ langgraph
9
  python-dotenv
10
  bs4
11
+ pandas
12
+ openpyxl
13
+ pydub
14
+ huggingface-hub
tools/__init__.py CHANGED
@@ -1,10 +1,12 @@
1
- from tools.web_tools import WebSearchTool, WebContentTool
2
- from tools.utils import CurrentDateTool, JsonParserTool, CalculatorTool
 
 
3
 
4
  __all__ = [
5
- "WebSearchTool",
6
- "WebContentTool",
7
- "CurrentDateTool",
8
- "JsonParserTool",
9
- "CalculatorTool"
10
- ]
 
1
+ from .web_tools import WebSearchTool, WebContentTool
2
+ # from .audio_tools import AudioToTextTool
3
+ # from .spreadsheet_tools import SpreadsheetParserTool
4
+ # from .string_tools import StringUtilitiesTool
5
 
6
  __all__ = [
7
+ 'WebSearchTool',
8
+ 'WebContentTool',
9
+ # 'AudioToTextTool',
10
+ # 'SpreadsheetParserTool',
11
+ # 'StringUtilitiesTool'
12
+ ]
tools/audio_tools.py ADDED
@@ -0,0 +1,47 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
+ from typing import Optional
3
+ from langchain.tools import BaseTool
4
+ import requests
5
+ from pydub import AudioSegment
6
+ import tempfile
7
+
8
+ class AudioToTextTool(BaseTool):
9
+ name = "audio_to_text"
10
+ description = "Convertit un fichier audio en texte en utilisant l'API Hugging Face"
11
+
12
+ def _run(self, audio_path: str) -> str:
13
+ """Convertit un fichier audio en texte"""
14
+ try:
15
+ # Vérifier si le fichier existe
16
+ if not os.path.exists(audio_path):
17
+ return f"Erreur: Le fichier {audio_path} n'existe pas"
18
+
19
+ # Convertir le fichier audio en format WAV si nécessaire
20
+ audio = AudioSegment.from_file(audio_path)
21
+ with tempfile.NamedTemporaryFile(suffix=".wav", delete=False) as temp_file:
22
+ audio.export(temp_file.name, format="wav")
23
+ temp_path = temp_file.name
24
+
25
+ # Appeler l'API Hugging Face pour la transcription
26
+ API_URL = "https://api-inference.huggingface.co/models/facebook/wav2vec2-large-960h-lv60-self"
27
+ headers = {"Authorization": f"Bearer {os.getenv('HUGGINGFACE_API_KEY')}"}
28
+
29
+ with open(temp_path, "rb") as f:
30
+ data = f.read()
31
+
32
+ response = requests.post(API_URL, headers=headers, data=data)
33
+
34
+ # Nettoyer le fichier temporaire
35
+ os.unlink(temp_path)
36
+
37
+ if response.status_code != 200:
38
+ return f"Erreur lors de la transcription: {response.text}"
39
+
40
+ return response.json().get("text", "Aucun texte transcrit")
41
+
42
+ except Exception as e:
43
+ return f"Erreur lors de la conversion audio en texte: {str(e)}"
44
+
45
+ async def _arun(self, audio_path: str) -> str:
46
+ """Version asynchrone de l'outil"""
47
+ return self._run(audio_path)
tools/spreadsheet_tools.py ADDED
@@ -0,0 +1,43 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import pandas as pd
2
+ from typing import List, Dict, Any
3
+ from langchain.tools import BaseTool
4
+
5
+ class SpreadsheetParserTool(BaseTool):
6
+ name = "spreadsheet_parser"
7
+ description = "Analyse un fichier Excel et extrait des informations spécifiques"
8
+
9
+ def _run(self, file_path: str, query: str) -> str:
10
+ """Analyse un fichier Excel et répond à une requête spécifique"""
11
+ try:
12
+ # Lire le fichier Excel
13
+ df = pd.read_excel(file_path)
14
+
15
+ # Analyser la requête pour déterminer l'action à effectuer
16
+ if "somme" in query.lower() or "total" in query.lower():
17
+ # Trouver les colonnes numériques
18
+ numeric_cols = df.select_dtypes(include=['number']).columns
19
+ if len(numeric_cols) > 0:
20
+ return f"Somme des colonnes numériques:\n{df[numeric_cols].sum().to_string()}"
21
+ else:
22
+ return "Aucune colonne numérique trouvée dans le fichier"
23
+
24
+ elif "moyenne" in query.lower() or "average" in query.lower():
25
+ numeric_cols = df.select_dtypes(include=['number']).columns
26
+ if len(numeric_cols) > 0:
27
+ return f"Moyenne des colonnes numériques:\n{df[numeric_cols].mean().to_string()}"
28
+ else:
29
+ return "Aucune colonne numérique trouvée dans le fichier"
30
+
31
+ elif "premières lignes" in query.lower() or "first rows" in query.lower():
32
+ return f"Premières lignes du fichier:\n{df.head().to_string()}"
33
+
34
+ else:
35
+ # Par défaut, retourner un résumé du fichier
36
+ return f"Résumé du fichier:\nNombre de lignes: {len(df)}\nColonnes: {', '.join(df.columns)}\nTypes de données:\n{df.dtypes.to_string()}"
37
+
38
+ except Exception as e:
39
+ return f"Erreur lors de l'analyse du fichier Excel: {str(e)}"
40
+
41
+ async def _arun(self, file_path: str, query: str) -> str:
42
+ """Version asynchrone de l'outil"""
43
+ return self._run(file_path, query)
tools/string_tools.py ADDED
@@ -0,0 +1,35 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from typing import List, Dict, Any
2
+ from langchain.tools import BaseTool
3
+
4
+ class StringUtilitiesTool(BaseTool):
5
+ name = "string_utilities"
6
+ description = "Fournit des utilitaires pour manipuler les chaînes de caractères"
7
+
8
+ def _run(self, text: str, operation: str) -> str:
9
+ """Effectue une opération sur une chaîne de caractères"""
10
+ try:
11
+ if operation == "reverse":
12
+ return text[::-1]
13
+ elif operation == "uppercase":
14
+ return text.upper()
15
+ elif operation == "lowercase":
16
+ return text.lower()
17
+ elif operation == "capitalize":
18
+ return text.capitalize()
19
+ elif operation == "title":
20
+ return text.title()
21
+ elif operation == "strip":
22
+ return text.strip()
23
+ elif operation == "split":
24
+ return str(text.split())
25
+ elif operation == "join":
26
+ return " ".join(text.split())
27
+ else:
28
+ return f"Opération non supportée: {operation}. Opérations disponibles: reverse, uppercase, lowercase, capitalize, title, strip, split, join"
29
+
30
+ except Exception as e:
31
+ return f"Erreur lors de la manipulation de la chaîne: {str(e)}"
32
+
33
+ async def _arun(self, text: str, operation: str) -> str:
34
+ """Version asynchrone de l'outil"""
35
+ return self._run(text, operation)
tools/web_tools.py CHANGED
@@ -1,61 +1,30 @@
 
 
 
1
  import requests
2
  from bs4 import BeautifulSoup
3
- from typing import List, Dict, Any
4
- from langchain.tools import BaseTool
5
 
6
  class WebSearchTool(BaseTool):
7
- name = "web_search"
8
- description = "Recherche des informations sur le web à partir d'un terme de recherche"
 
9
 
10
  def _run(self, query: str) -> str:
11
  """Exécute une recherche web et retourne les résultats pertinents"""
12
  try:
13
- # Cette fonction simule une recherche web
14
- # Dans un cas réel, vous pourriez utiliser l'API Google ou Bing
15
- headers = {
16
- "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36"
17
- }
18
- search_url = f"https://www.google.com/search?q={query.replace(' ', '+')}"
19
- response = requests.get(search_url, headers=headers)
20
-
21
- if response.status_code != 200:
22
- return f"Erreur lors de la recherche: {response.status_code}"
23
-
24
- # Extraction des résultats avec BeautifulSoup
25
- soup = BeautifulSoup(response.text, 'html.parser')
26
- search_results = []
27
-
28
- # Extrait les titres et descriptions des résultats
29
- for result in soup.select("div.g"):
30
- title_elem = result.select_one("h3")
31
- if not title_elem:
32
- continue
33
-
34
- title = title_elem.get_text()
35
- link = result.select_one("a")["href"] if result.select_one("a") else ""
36
- snippet = result.select_one("div.VwiC3b")
37
- description = snippet.get_text() if snippet else ""
38
-
39
- if title and description:
40
- search_results.append(f"Titre: {title}\nLien: {link}\nDescription: {description}\n---")
41
-
42
- if not search_results:
43
- return "Aucun résultat trouvé pour cette recherche."
44
-
45
- return "\n".join(search_results[:3]) # Limite à 3 résultats
46
-
47
  except Exception as e:
48
  return f"Erreur lors de la recherche web: {str(e)}"
49
 
50
  async def _arun(self, query: str) -> str:
51
  """Version asynchrone de l'outil"""
52
- # Implémentation asynchrone si nécessaire
53
  return self._run(query)
54
 
55
-
56
  class WebContentTool(BaseTool):
57
- name = "fetch_web_content"
58
- description = "Récupère le contenu d'une page web à partir d'une URL"
 
59
 
60
  def _run(self, url: str) -> str:
61
  """Récupère et nettoie le contenu d'une page web"""
@@ -63,7 +32,7 @@ class WebContentTool(BaseTool):
63
  headers = {
64
  "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36"
65
  }
66
- response = requests.get(url, headers=headers)
67
 
68
  if response.status_code != 200:
69
  return f"Erreur lors de la récupération du contenu: {response.status_code}"
@@ -94,5 +63,4 @@ class WebContentTool(BaseTool):
94
 
95
  async def _arun(self, url: str) -> str:
96
  """Version asynchrone de l'outil"""
97
- # Implémentation asynchrone si nécessaire
98
  return self._run(url)
 
1
+ from langchain_community.tools import DuckDuckGoSearchRun
2
+ from langchain.tools import BaseTool
3
+ from typing import Optional, Type
4
  import requests
5
  from bs4 import BeautifulSoup
 
 
6
 
7
  class WebSearchTool(BaseTool):
8
+ name: str = "web_search"
9
+ description: str = "Recherche des informations sur le web à partir d'un terme de recherche"
10
+ args_schema: Optional[Type] = None
11
 
12
  def _run(self, query: str) -> str:
13
  """Exécute une recherche web et retourne les résultats pertinents"""
14
  try:
15
+ search_tool = DuckDuckGoSearchRun()
16
+ return search_tool.run(query)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
17
  except Exception as e:
18
  return f"Erreur lors de la recherche web: {str(e)}"
19
 
20
  async def _arun(self, query: str) -> str:
21
  """Version asynchrone de l'outil"""
 
22
  return self._run(query)
23
 
 
24
  class WebContentTool(BaseTool):
25
+ name: str = "fetch_web_content"
26
+ description: str = "Récupère le contenu d'une page web à partir d'une URL"
27
+ args_schema: Optional[Type] = None
28
 
29
  def _run(self, url: str) -> str:
30
  """Récupère et nettoie le contenu d'une page web"""
 
32
  headers = {
33
  "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36"
34
  }
35
+ response = requests.get(url, headers=headers, timeout=10)
36
 
37
  if response.status_code != 200:
38
  return f"Erreur lors de la récupération du contenu: {response.status_code}"
 
63
 
64
  async def _arun(self, url: str) -> str:
65
  """Version asynchrone de l'outil"""
 
66
  return self._run(url)