Marylene commited on
Commit
43a61d0
·
1 Parent(s): 405e451
Files changed (4) hide show
  1. agent.py +42 -35
  2. app.py +6 -4
  3. email_sender.py +21 -10
  4. requirements.txt +1 -1
agent.py CHANGED
@@ -1,43 +1,50 @@
1
- from smolagent import SmolAgent, OpenAIProvider
2
- from smolagent.schema import ChatPrompt, Prompt
3
-
4
- # Crée le système de prompt soigné pour générer email professionnel
5
- system_prompt = Prompt(
6
- role="system",
7
- content=(
8
- "Tu es un assistant IA qui génère des emails professionnels adaptés au contexte, "
9
- "au destinataire et au ton donné. "
10
- "Rédige d’abord un objet court et pertinent, puis le corps du mail complet."
11
- ),
 
 
 
12
  )
13
 
14
- # Prompt utilisateur (input)
15
- user_prompt = Prompt(
16
- role="user",
17
- content=(
18
- "Destinataire : {to_email}\n"
19
- "Contexte : {context}\n"
20
- "Ton souhaité : {tone}\n\n"
21
- "Génère un objet et un corps d'email."
22
- ),
23
  )
24
 
25
  def build_email_agent():
26
- provider = OpenAIProvider(model="gpt-4o-mini") # ou autre modèle disponible
27
- prompt = ChatPrompt(prompts=[system_prompt, user_prompt])
28
- agent = SmolAgent(provider=provider, prompt=prompt)
29
  return agent
30
 
31
- def generate_email(agent, to_email, context, tone):
32
- # Formate le prompt avec les valeurs utilisateur
33
- inputs = {
34
- "to_email": to_email,
35
- "context": context,
36
- "tone": tone,
37
- }
38
- response = agent.chat(inputs)
39
- # La réponse devrait contenir objet + corps, on sépare à la première ligne vide
40
- parts = response.strip().split('\n\n', 1)
41
- subject = parts[0].strip() if parts else "Objet automatique"
42
- body = parts[1].strip() if len(parts) > 1 else response.strip()
 
 
 
 
 
 
 
 
 
 
43
  return subject, body
 
1
+ from smolagents import tool, CodeAgent, InferenceClientModel, FinalAnswerTool
2
+
3
+ # Définition de l'outil : écrire un email formel, chaleureux, etc.
4
+ @tool
5
+ def write_email(to: str, context: str, tone: str) -> tuple[str, str]:
6
+ """Génère un objet et un corps d'email à destination de quelqu'un dans un certain ton."""
7
+ # La génération sera assurée par le LLM via le prompt, rien n'est codé ici
8
+ pass
9
+
10
+ # Chargement du modèle
11
+ model = InferenceClientModel(
12
+ model_id="Qwen/Qwen2.5-7B-Instruct",
13
+ temperature=0.5,
14
+ max_tokens=2048
15
  )
16
 
17
+ # Définition de l'agent
18
+ agent = CodeAgent(
19
+ model=model,
20
+ tools=[write_email, FinalAnswerTool()],
21
+ max_steps=5,
22
+ verbosity_level=1,
 
 
 
23
  )
24
 
25
  def build_email_agent():
 
 
 
26
  return agent
27
 
28
+ # Génération du mail avec appel à l'agent
29
+ def generate_email(agent, to_email: str, context: str, tone: str) -> tuple[str, str]:
30
+ prompt = f"""Tu es un assistant virtuel. Rédige un email destiné à {to_email}.
31
+
32
+ Contexte : {context}
33
+ Ton : {tone}
34
+
35
+ Réponds en deux parties :
36
+ 1. Objet : ligne d'objet de l'email
37
+ 2. Corps : contenu de l'email bien rédigé, en respectant le ton demandé.
38
+ Commence chaque partie par son étiquette.
39
+ """
40
+ result = agent.chat(prompt)
41
+
42
+ # Extraction simple de l'objet et du corps
43
+ subject, body = "Sans objet", result.strip()
44
+ for line in result.splitlines():
45
+ if line.lower().startswith("objet"):
46
+ subject = line.split(":", 1)[-1].strip()
47
+ body = result.split(line, 1)[-1].replace(line, "").strip()
48
+ break
49
+
50
  return subject, body
app.py CHANGED
@@ -8,17 +8,19 @@ def on_generate(to_email, context, tone):
8
  subject, body = generate_email(agent, to_email, context, tone)
9
  return subject, body
10
 
11
- def on_send(to_email, subject, body):
12
- success, message = send_email(to_email, subject, body)
13
  return message
14
 
15
  with gr.Blocks(title="Assistant Email Pro avec SmolAgent") as demo:
16
  gr.Markdown("# ✉️ Assistant Email Professionnel SmolAgent")
17
 
 
18
  to_email = gr.Textbox(label="Destinataire", placeholder="exemple@exemple.com")
19
  context = gr.Textbox(label="Contexte ou objectif", lines=4,
20
  placeholder="Demander un rendez-vous, relancer un client...")
21
- tone = gr.Dropdown(choices=["Formel", "Chaleureux", "Direct", "Poli"], value="Formel", label="Ton")
 
22
 
23
  subject = gr.Textbox(label="Objet de l'email")
24
  body = gr.Textbox(label="Corps de l'email", lines=10)
@@ -30,6 +32,6 @@ with gr.Blocks(title="Assistant Email Pro avec SmolAgent") as demo:
30
  status = gr.Markdown()
31
 
32
  generate_btn.click(on_generate, inputs=[to_email, context, tone], outputs=[subject, body])
33
- send_btn.click(on_send, inputs=[to_email, subject, body], outputs=status)
34
 
35
  demo.launch()
 
8
  subject, body = generate_email(agent, to_email, context, tone)
9
  return subject, body
10
 
11
+ def on_send(sender_email, to_email, subject, body):
12
+ success, message = send_email(sender_email, to_email, subject, body)
13
  return message
14
 
15
  with gr.Blocks(title="Assistant Email Pro avec SmolAgent") as demo:
16
  gr.Markdown("# ✉️ Assistant Email Professionnel SmolAgent")
17
 
18
+ sender = gr.Textbox(label="Expéditeur (adresse Gmail utilisée)")
19
  to_email = gr.Textbox(label="Destinataire", placeholder="exemple@exemple.com")
20
  context = gr.Textbox(label="Contexte ou objectif", lines=4,
21
  placeholder="Demander un rendez-vous, relancer un client...")
22
+ tone = gr.Dropdown(choices=["Formel", "Chaleureux", "Direct", "Poli"],
23
+ value="Formel", label="Ton")
24
 
25
  subject = gr.Textbox(label="Objet de l'email")
26
  body = gr.Textbox(label="Corps de l'email", lines=10)
 
32
  status = gr.Markdown()
33
 
34
  generate_btn.click(on_generate, inputs=[to_email, context, tone], outputs=[subject, body])
35
+ send_btn.click(on_send, inputs=[sender, to_email, subject, body], outputs=status)
36
 
37
  demo.launch()
email_sender.py CHANGED
@@ -6,38 +6,49 @@ from google_auth_oauthlib.flow import InstalledAppFlow
6
  from google.auth.transport.requests import Request
7
  from googleapiclient.discovery import build
8
 
9
- TOKEN_PATH = "token.pkl"
10
  CREDENTIALS_PATH = "client_secret.json"
11
 
 
 
 
 
 
 
12
  client_secret_content = os.getenv("CLIENT_SECRET_JSON")
13
  if client_secret_content and not os.path.exists(CREDENTIALS_PATH):
14
  with open(CREDENTIALS_PATH, "w") as f:
15
  f.write(client_secret_content)
16
 
17
- def authenticate_gmail():
18
- SCOPES = ["https://www.googleapis.com/auth/gmail.send"]
19
  creds = None
20
- if os.path.exists(TOKEN_PATH):
21
- with open(TOKEN_PATH, "rb") as token:
 
 
22
  creds = pickle.load(token)
 
23
  if not creds or not creds.valid:
24
  if creds and creds.expired and creds.refresh_token:
25
  creds.refresh(Request())
26
  else:
27
  flow = InstalledAppFlow.from_client_secrets_file(CREDENTIALS_PATH, SCOPES)
28
  creds = flow.run_local_server(port=0)
29
- with open(TOKEN_PATH, "wb") as token:
30
  pickle.dump(creds, token)
31
- service = build("gmail", "v1", credentials=creds)
32
- return service
33
 
34
- def send_email(to_email, subject, body):
35
- service = authenticate_gmail()
 
 
 
36
  message = MIMEText(body)
37
  message["to"] = to_email
38
  message["subject"] = subject
 
39
  raw_message = base64.urlsafe_b64encode(message.as_bytes()).decode()
40
  send_message = {"raw": raw_message}
 
41
  try:
42
  sent = service.users().messages().send(userId="me", body=send_message).execute()
43
  return True, f"✅ Email envoyé à {to_email} (ID: {sent['id']})"
 
6
  from google.auth.transport.requests import Request
7
  from googleapiclient.discovery import build
8
 
9
+ SCOPES = ["https://www.googleapis.com/auth/gmail.send"]
10
  CREDENTIALS_PATH = "client_secret.json"
11
 
12
+ # Stocker les tokens par email utilisateur
13
+ def get_token_path(sender_email):
14
+ safe_email = sender_email.replace("@", "_at_").replace(".", "_dot_")
15
+ return f"token_{safe_email}.pkl"
16
+
17
+ # Écrire le client secret s'il est fourni par variable d'env
18
  client_secret_content = os.getenv("CLIENT_SECRET_JSON")
19
  if client_secret_content and not os.path.exists(CREDENTIALS_PATH):
20
  with open(CREDENTIALS_PATH, "w") as f:
21
  f.write(client_secret_content)
22
 
23
+ def authenticate_gmail(sender_email: str):
 
24
  creds = None
25
+ token_path = get_token_path(sender_email)
26
+
27
+ if os.path.exists(token_path):
28
+ with open(token_path, "rb") as token:
29
  creds = pickle.load(token)
30
+
31
  if not creds or not creds.valid:
32
  if creds and creds.expired and creds.refresh_token:
33
  creds.refresh(Request())
34
  else:
35
  flow = InstalledAppFlow.from_client_secrets_file(CREDENTIALS_PATH, SCOPES)
36
  creds = flow.run_local_server(port=0)
37
+ with open(token_path, "wb") as token:
38
  pickle.dump(creds, token)
 
 
39
 
40
+ return build("gmail", "v1", credentials=creds)
41
+
42
+ def send_email(sender_email: str, to_email: str, subject: str, body: str):
43
+ service = authenticate_gmail(sender_email)
44
+
45
  message = MIMEText(body)
46
  message["to"] = to_email
47
  message["subject"] = subject
48
+ message["from"] = sender_email
49
  raw_message = base64.urlsafe_b64encode(message.as_bytes()).decode()
50
  send_message = {"raw": raw_message}
51
+
52
  try:
53
  sent = service.users().messages().send(userId="me", body=send_message).execute()
54
  return True, f"✅ Email envoyé à {to_email} (ID: {sent['id']})"
requirements.txt CHANGED
@@ -1,5 +1,5 @@
1
  markdownify
2
- git+https://github.com/jtmuller5/smol-agent.git
3
  requests
4
  duckduckgo_search
5
  pandas
 
1
  markdownify
2
+ smolagents==1.13.0
3
  requests
4
  duckduckgo_search
5
  pandas