agentmail 0.1.5 → 0.1.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (55) hide show
  1. package/dist/cjs/Client.js +2 -2
  2. package/dist/cjs/api/resources/domains/types/Domain.d.ts +1 -0
  3. package/dist/cjs/api/resources/domains/types/DomainItem.d.ts +1 -0
  4. package/dist/cjs/api/resources/inboxes/types/Inbox.d.ts +1 -0
  5. package/dist/cjs/api/resources/messages/types/ReplyToMessageRequest.d.ts +1 -0
  6. package/dist/cjs/api/resources/messages/types/SendMessageHeaders.d.ts +4 -0
  7. package/dist/cjs/api/resources/messages/types/SendMessageHeaders.js +3 -0
  8. package/dist/cjs/api/resources/messages/types/SendMessageRequest.d.ts +1 -0
  9. package/dist/cjs/api/resources/messages/types/index.d.ts +1 -0
  10. package/dist/cjs/api/resources/messages/types/index.js +1 -0
  11. package/dist/cjs/serialization/resources/domains/types/Domain.d.ts +2 -0
  12. package/dist/cjs/serialization/resources/domains/types/Domain.js +2 -0
  13. package/dist/cjs/serialization/resources/domains/types/DomainItem.d.ts +2 -0
  14. package/dist/cjs/serialization/resources/domains/types/DomainItem.js +2 -0
  15. package/dist/cjs/serialization/resources/inboxes/types/Inbox.d.ts +2 -0
  16. package/dist/cjs/serialization/resources/inboxes/types/Inbox.js +2 -0
  17. package/dist/cjs/serialization/resources/messages/types/ReplyToMessageRequest.d.ts +2 -0
  18. package/dist/cjs/serialization/resources/messages/types/ReplyToMessageRequest.js +2 -0
  19. package/dist/cjs/serialization/resources/messages/types/SendMessageHeaders.d.ts +7 -0
  20. package/dist/cjs/serialization/resources/messages/types/SendMessageHeaders.js +39 -0
  21. package/dist/cjs/serialization/resources/messages/types/SendMessageRequest.d.ts +2 -0
  22. package/dist/cjs/serialization/resources/messages/types/SendMessageRequest.js +2 -0
  23. package/dist/cjs/serialization/resources/messages/types/index.d.ts +1 -0
  24. package/dist/cjs/serialization/resources/messages/types/index.js +1 -0
  25. package/dist/cjs/version.d.ts +1 -1
  26. package/dist/cjs/version.js +1 -1
  27. package/dist/esm/Client.mjs +2 -2
  28. package/dist/esm/api/resources/domains/types/Domain.d.mts +1 -0
  29. package/dist/esm/api/resources/domains/types/DomainItem.d.mts +1 -0
  30. package/dist/esm/api/resources/inboxes/types/Inbox.d.mts +1 -0
  31. package/dist/esm/api/resources/messages/types/ReplyToMessageRequest.d.mts +1 -0
  32. package/dist/esm/api/resources/messages/types/SendMessageHeaders.d.mts +4 -0
  33. package/dist/esm/api/resources/messages/types/SendMessageHeaders.mjs +2 -0
  34. package/dist/esm/api/resources/messages/types/SendMessageRequest.d.mts +1 -0
  35. package/dist/esm/api/resources/messages/types/index.d.mts +1 -0
  36. package/dist/esm/api/resources/messages/types/index.mjs +1 -0
  37. package/dist/esm/serialization/resources/domains/types/Domain.d.mts +2 -0
  38. package/dist/esm/serialization/resources/domains/types/Domain.mjs +2 -0
  39. package/dist/esm/serialization/resources/domains/types/DomainItem.d.mts +2 -0
  40. package/dist/esm/serialization/resources/domains/types/DomainItem.mjs +2 -0
  41. package/dist/esm/serialization/resources/inboxes/types/Inbox.d.mts +2 -0
  42. package/dist/esm/serialization/resources/inboxes/types/Inbox.mjs +2 -0
  43. package/dist/esm/serialization/resources/messages/types/ReplyToMessageRequest.d.mts +2 -0
  44. package/dist/esm/serialization/resources/messages/types/ReplyToMessageRequest.mjs +2 -0
  45. package/dist/esm/serialization/resources/messages/types/SendMessageHeaders.d.mts +7 -0
  46. package/dist/esm/serialization/resources/messages/types/SendMessageHeaders.mjs +3 -0
  47. package/dist/esm/serialization/resources/messages/types/SendMessageRequest.d.mts +2 -0
  48. package/dist/esm/serialization/resources/messages/types/SendMessageRequest.mjs +2 -0
  49. package/dist/esm/serialization/resources/messages/types/index.d.mts +1 -0
  50. package/dist/esm/serialization/resources/messages/types/index.mjs +1 -0
  51. package/dist/esm/version.d.mts +1 -1
  52. package/dist/esm/version.mjs +1 -1
  53. package/dist/llms-full.txt +593 -41
  54. package/dist/llms.txt +1 -0
  55. package/package.json +1 -1
@@ -171,7 +171,7 @@ This guide will walk you through installing the AgentMail SDK, authenticating wi
171
171
  <Step title="Create an API Key">
172
172
  Now that you're in the console, you'll need to create an API key to
173
173
  authenticate your requests. Navigate to the API Keys section in your console
174
- dashboard. ![API Key Creation Screenshot](file:70efc386-e5f7-4897-a497-515e32142b26) Click
174
+ dashboard. ![API Key Creation Screenshot](file:f8c5320f-b15c-44e7-a569-670c5f1d2a88) Click
175
175
  "Create New API Key" and give it a descriptive name. Once created, copy the
176
176
  API key and store it securely. Create a `.env` file in your project's root
177
177
  directory and add your key to it. We recommend using environment variables to
@@ -312,7 +312,7 @@ Unlike traditional email providers that are designed for human scale, AgentMail
312
312
 
313
313
  As the diagram below illustrates, your `organization` is the top-level container that holds all your resources. You can provision many `Inboxes` within your `organization`, each with its own `Threads`, `Messages`, and `Attachments`, allowing you to manage a large fleet of agents seamlessly.
314
314
 
315
- <img src="file:2d3ffce3-a340-42d2-9a67-f0be52f7500d" alt="AgentMail Organizational Hierarchy" />
315
+ <img src="file:ac426e5d-2c1a-40b1-bf52-5c97883187df" alt="AgentMail Organizational Hierarchy" />
316
316
 
317
317
  <Steps>
318
318
  <Step title="Organization">
@@ -732,7 +732,7 @@ Here is an example of a well-structured and styled HTML header:
732
732
  </CodeBlocks>
733
733
 
734
734
  <Frame caption="Look how pretty this message looks!">
735
- <img src="file:42af328f-ce02-484c-a5b5-1566ac786ee2" alt="rendered css" />
735
+ <img src="file:3e435f8a-5583-4ddd-83a3-c3f87c5f3aac" alt="rendered css" />
736
736
  </Frame>
737
737
 
738
738
  ## Receiving `Messages`
@@ -1759,7 +1759,7 @@ Configuring your domain is a three-step process: add the domain via API, copy th
1759
1759
  After creating your domain in the AgentMail Console, click the "Download BIND Zone File" button to get the complete zone file.
1760
1760
 
1761
1761
  <Frame caption="Downloading BIND zone file from AgentMail Console">
1762
- <img src="file:b3ff2a02-c596-4554-be3d-1e54811dfa55" alt="Download BIND Zone File from Console" />
1762
+ <img src="file:fdcf475d-78f0-49a1-aa38-5bd877368f56" alt="Download BIND Zone File from Console" />
1763
1763
  </Frame>
1764
1764
 
1765
1765
  <Tabs>
@@ -1770,13 +1770,13 @@ Configuring your domain is a three-step process: add the domain via API, copy th
1770
1770
  2. Click **"Import zone file"** in the top right corner
1771
1771
 
1772
1772
  <Frame caption="Importing BIND zone file in AWS Route 53">
1773
- <img src="file:e7101e7a-5590-4fe1-b1d4-733c24ec310f" alt="AWS Route 53 BIND Import" />
1773
+ <img src="file:01476e8c-76ae-4414-b008-0547631c47f1" alt="AWS Route 53 BIND Import" />
1774
1774
  </Frame>
1775
1775
 
1776
1776
  3. Paste the CONTENTS of downloaded BIND zone file
1777
1777
 
1778
1778
  <Frame caption="Open the file with text editor and paste the contents. It should look similar to what we have in this image.">
1779
- <img src="file:fe2e9d0f-f350-479c-b634-5477599d3053" alt="AWS Route 53 BIND Import" />
1779
+ <img src="file:86767929-19ef-4ded-b528-ed42ede218fe" alt="AWS Route 53 BIND Import" />
1780
1780
  </Frame>
1781
1781
 
1782
1782
  4. Review the records and click **"Import"**
@@ -1789,13 +1789,13 @@ Configuring your domain is a three-step process: add the domain via API, copy th
1789
1789
  2. Navigate to **DNS > Records**
1790
1790
 
1791
1791
  <Frame caption="This is what the page looks like">
1792
- <img src="file:b8cfed63-5fea-420f-ac7f-9731a80e840d" alt="Cloudflare BIND Import" />
1792
+ <img src="file:1886368e-04ae-4bfe-a117-83246de25118" alt="Cloudflare BIND Import" />
1793
1793
  </Frame>
1794
1794
 
1795
1795
  3. Click **"Import and Export"**
1796
1796
 
1797
1797
  <Frame caption="You should be able to just drop the file in">
1798
- <img src="file:d8762904-358d-432e-99cf-4e4b4763fed5" alt="Cloudflare BIND Import" />
1798
+ <img src="file:b966738e-c0e6-425d-86d1-0ce95b9a326d" alt="Cloudflare BIND Import" />
1799
1799
  </Frame>
1800
1800
 
1801
1801
  4. Upload the downloaded BIND zone file as is
@@ -1808,13 +1808,13 @@ Configuring your domain is a three-step process: add the domain via API, copy th
1808
1808
  2. Navigate to the **DNS** subtab of the domain you want to send from
1809
1809
 
1810
1810
  <Frame caption="Click on this button!">
1811
- <img src="file:58e638a2-7862-451e-9c69-2705ad1f6022" alt="Porkbun DNS Management" />
1811
+ <img src="file:57da42f8-9541-4e8a-9b20-7c81ca585355" alt="Porkbun DNS Management" />
1812
1812
  </Frame>
1813
1813
 
1814
1814
  3. Scroll down to the quick upload section
1815
1815
 
1816
1816
  <Frame caption="Upload your BIND zone file here">
1817
- <img src="file:a33096b6-9f1c-40f0-9e3d-71c7aa850d30" alt="Porkbun Zone File Import" />
1817
+ <img src="file:2824bdf3-c4c7-4dfe-b67a-1373865ecf22" alt="Porkbun Zone File Import" />
1818
1818
  </Frame>
1819
1819
 
1820
1820
  4. Upload the downloaded BIND zone file as is
@@ -1898,7 +1898,7 @@ Configuring your domain is a three-step process: add the domain via API, copy th
1898
1898
  * **Value:** Can directly copy paste the `value` from the API response (e.g., `{random_letters_numbers}.dkim.amazonses.com`).
1899
1899
 
1900
1900
  <Frame caption="Example of adding a CNAME record in Route 53. Notice that AWS already appends the root domain (agentmail.cc) to the end of the name value!">
1901
- <img src="file:b6048450-c831-4d87-96bf-7f62d837ef90" alt="AWS Route 53 Record Configuration" />
1901
+ <img src="file:62bea4af-5b5e-4f88-86ce-b569f26f8a74" alt="AWS Route 53 Record Configuration" />
1902
1902
  </Frame>
1903
1903
 
1904
1904
  * **TXT (DMARC/SPF):**
@@ -2008,7 +2008,7 @@ DNS can be tricky. Here are some common issues and how to resolve them.
2008
2008
 
2009
2009
  ## Best Practices for Domain Management
2010
2010
 
2011
- Check out our guide on [Email Deliverability](/best-practices/email-deliverability) for tips on warming up your new domain and maintaining a healthy sender reputation.
2011
+ Check out our guide on [Email Deliverability](/email-deliverability) for tips on warming up your new domain and maintaining a healthy sender reputation.
2012
2012
 
2013
2013
 
2014
2014
  # Managing Your Domains
@@ -2434,13 +2434,13 @@ Ngrok creates a secure tunnel from a public URL to your local development server
2434
2434
 
2435
2435
  Visit [ngrok.com](https://ngrok.com/) and click "Sign up" to create a free account.
2436
2436
 
2437
- <img src="file:2b9efd50-bc45-4c1d-be5d-0a0e2451fc05" alt="Ngrok homepage" />
2437
+ <img src="file:845efbdc-7a3b-44be-b7b6-d85c15baabfd" alt="Ngrok homepage" />
2438
2438
 
2439
2439
  ### 1.2 Choose your platform and install
2440
2440
 
2441
2441
  After logging in, ngrok will guide you through the setup process. Select your operating system and follow the installation instructions.
2442
2442
 
2443
- <img src="file:53878264-f22e-45d0-b6e5-3deace921b2d" alt="Ngrok setup instructions" />
2443
+ <img src="file:6bf31cc4-f08c-4b45-9abd-b93966def3ab" alt="Ngrok setup instructions" />
2444
2444
 
2445
2445
  For macOS, you can install ngrok via Homebrew:
2446
2446
 
@@ -2486,7 +2486,7 @@ ngrok http 3000
2486
2486
 
2487
2487
  You should see output similar to this:
2488
2488
 
2489
- <img src="file:da7bae5a-8b4d-4657-9da6-ffe312457580" alt="Ngrok terminal output" />
2489
+ <img src="file:190c3920-7025-4399-8718-b2b9a4895382" alt="Ngrok terminal output" />
2490
2490
 
2491
2491
  Copy the **Forwarding URL** (e.g., `https://your-subdomain.ngrok-free.app`). This is the public URL that AgentMail will use to send webhooks.
2492
2492
 
@@ -2583,7 +2583,7 @@ python webhook_receiver.py
2583
2583
 
2584
2584
  Open your browser and visit `http://127.0.0.1:3000` to see the status page confirming your webhook receiver is running:
2585
2585
 
2586
- <img src="file:a246e89b-1dc2-4f91-b1b7-ebe6527efb10" alt="Webhook receiver status page" />
2586
+ <img src="file:933ee151-1684-4e56-b2a9-4dd7e81f9f5d" alt="Webhook receiver status page" />
2587
2587
 
2588
2588
  ## Testing Your Setup
2589
2589
 
@@ -2705,7 +2705,7 @@ How you send your emails is just as important as what you send. If you're sendin
2705
2705
  more natural to email providers. AgentMail's ability to create inboxes at
2706
2706
  scale makes this strategy easy to implement.
2707
2707
 
2708
- <img src="file:1c500820-48d4-480b-8df5-7f6b0c12fc34" alt="Diagram comparing one inbox sending 1000 emails vs. five inboxes sending 200 each." />
2708
+ <img src="file:256ac0e7-42b8-4fdd-a3f1-6136fbec2725" alt="Diagram comparing one inbox sending 1000 emails vs. five inboxes sending 200 each." />
2709
2709
  </Step>
2710
2710
 
2711
2711
  <Step title="Protect Your Reputation with Multiple Domains">
@@ -3293,6 +3293,7 @@ Create a file named `agent.py` and paste the following code:
3293
3293
  from flask import Flask, request, Response
3294
3294
  import ngrok
3295
3295
  from agentmail import AgentMail
3296
+ import threading
3296
3297
 
3297
3298
  # Configuration
3298
3299
  PORT = 8080
@@ -3302,6 +3303,7 @@ Create a file named `agent.py` and paste the following code:
3302
3303
  # Initialize Flask app and AgentMail client
3303
3304
  app = Flask(__name__)
3304
3305
  client = AgentMail()
3306
+ processed_messages = set() # Track processed message IDs to prevent duplicates
3305
3307
 
3306
3308
 
3307
3309
  def setup_agentmail():
@@ -3334,6 +3336,7 @@ Create a file named `agent.py` and paste the following code:
3334
3336
  webhook = client.webhooks.create(
3335
3337
  url=f"{listener.url()}/webhook/agentmail",
3336
3338
  event_types=["message.received"],
3339
+ inbox_ids=[inbox.inbox_id],
3337
3340
  client_id=f"{INBOX_USERNAME}-webhook"
3338
3341
  )
3339
3342
  print(f"✓ Webhook created")
@@ -3361,25 +3364,8 @@ Create a file named `agent.py` and paste the following code:
3361
3364
  )
3362
3365
 
3363
3366
 
3364
- @app.route('/webhook/agentmail', methods=['POST'])
3365
- def receive_webhook():
3366
- """Webhook endpoint to receive incoming email notifications."""
3367
- payload = request.json
3368
- event_type = payload.get('type') or payload.get('event_type')
3369
-
3370
- # Ignore outgoing messages
3371
- if event_type == 'message.sent':
3372
- return Response(status=200)
3373
-
3374
- message = payload.get('message', {})
3375
- message_id = message.get('message_id')
3376
- inbox_id = message.get('inbox_id')
3377
- from_field = message.get('from_', '') or message.get('from', '')
3378
-
3379
- # Validate required fields
3380
- if not message_id or not inbox_id or not from_field:
3381
- return Response(status=200)
3382
-
3367
+ def process_and_reply(message_id, inbox_id, from_field, subject, message):
3368
+ """Process incoming message and send reply in background."""
3383
3369
  # Extract sender email and name
3384
3370
  if '<' in from_field and '>' in from_field:
3385
3371
  sender_email = from_field.split('<')[1].split('>')[0].strip()
@@ -3390,10 +3376,8 @@ Create a file named `agent.py` and paste the following code:
3390
3376
  sender_email = from_field.strip()
3391
3377
  sender_name = sender_email.split('@')[0].title() if '@' in sender_email else 'Friend'
3392
3378
 
3393
- subject = message.get('subject', '(no subject)')
3394
-
3395
3379
  # Log incoming email
3396
- print(f"Email from {sender_email}: {subject}")
3380
+ print(f"Processing email from {sender_email}: {subject}")
3397
3381
 
3398
3382
  # Generate and send auto-reply
3399
3383
  try:
@@ -3408,6 +3392,41 @@ Create a file named `agent.py` and paste the following code:
3408
3392
  except Exception as e:
3409
3393
  print(f"Error: {e}\n")
3410
3394
 
3395
+
3396
+ @app.route('/webhook/agentmail', methods=['POST'])
3397
+ def receive_webhook():
3398
+ """Webhook endpoint to receive incoming email notifications."""
3399
+ payload = request.json
3400
+ event_type = payload.get('type') or payload.get('event_type')
3401
+
3402
+ # Ignore outgoing messages
3403
+ if event_type == 'message.sent':
3404
+ return Response(status=200)
3405
+
3406
+ message = payload.get('message', {})
3407
+ message_id = message.get('message_id')
3408
+ inbox_id = message.get('inbox_id')
3409
+ from_field = message.get('from_', '') or message.get('from', '')
3410
+
3411
+ # Validate required fields
3412
+ if not message_id or not inbox_id or not from_field:
3413
+ return Response(status=200)
3414
+
3415
+ # prevent duplicate
3416
+ if message_id in processed_messages:
3417
+ return Response(status=200)
3418
+ processed_messages.add(message_id)
3419
+
3420
+ subject = message.get('subject', '(no subject)')
3421
+
3422
+ # Process in background thread and return immediately
3423
+ thread = threading.Thread(
3424
+ target=process_and_reply,
3425
+ args=(message_id, inbox_id, from_field, subject, message)
3426
+ )
3427
+ thread.daemon = True
3428
+ thread.start()
3429
+
3411
3430
  return Response(status=200)
3412
3431
 
3413
3432
 
@@ -4705,7 +4724,7 @@ Done
4705
4724
 
4706
4725
  Go to your AgentMail inbox and filter by labels to organize your emails:
4707
4726
 
4708
- <img src="file:879ff90e-9416-4b31-a009-2054c03d170d" alt="Test image" />
4727
+ <img src="file:0cf3d744-bb78-46de-acc7-e3de13a39cf9" alt="Test image" />
4709
4728
 
4710
4729
  **Filter by sentiment:**
4711
4730
 
@@ -4875,12 +4894,545 @@ Ready to build your own intelligent email agent? Check out our [quickstart guide
4875
4894
  </AccordionGroup>
4876
4895
 
4877
4896
 
4897
+ # Email Reply Extraction with Talon
4898
+
4899
+ > Learn how to use Talon to extract new content from email replies, removing quoted text with 93.8% accuracy.
4900
+
4901
+ ## Why Talon?
4902
+
4903
+ Email threads accumulate quoted replies that clutter the actual content. When processing emails programmatically, you need just the new message, not the entire conversation history.
4904
+
4905
+ **Talon solves this problem** by extracting clean reply content through sophisticated pattern matching and structural analysis.
4906
+
4907
+ ### Use Cases
4908
+
4909
+ * **AI Email Agents**: Extract new user messages without processing entire thread history
4910
+ * **Email Automation**: Parse replies to identify actionable content
4911
+ * **Thread Analysis**: Build conversation flows by isolating individual contributions
4912
+ * **Inbox Management**: Process only new information from replies
4913
+
4914
+ ### Why Choose Talon?
4915
+
4916
+ <CardGroup cols={2}>
4917
+ <Card title="HTML Email Support" icon="fa-regular fa-file-code">
4918
+ Handles Gmail, Outlook, Apple Mail, Thunderbird HTML structures
4919
+ </Card>
4920
+
4921
+ <Card title="High Accuracy" icon="fa-regular fa-bullseye">
4922
+ 93.8% success rate across 64 real-world test cases
4923
+ </Card>
4924
+
4925
+ <Card title="Multi-language" icon="fa-regular fa-globe">
4926
+ Supports English, Japanese, Swedish, Polish, Dutch, German
4927
+ </Card>
4928
+
4929
+ <Card title="Fast Performance" icon="fa-regular fa-gauge-high">
4930
+ 1.92ms average processing time, 488 emails/second
4931
+ </Card>
4932
+ </CardGroup>
4933
+
4934
+ ***
4935
+
4936
+ ## How Talon Works
4937
+
4938
+ Talon uses two complementary approaches depending on email format:
4939
+
4940
+ ### Plain Text Processing (6-Stage Pipeline)
4941
+
4942
+ 1. **Line Classification**: Assigns markers to each line ('t'=text, 'm'=quote marker, 's'=splitter, 'e'=empty)
4943
+ 2. **Pattern Matching**: Applies regex to marker sequences to identify quoted blocks
4944
+ 3. **Content Extraction**: Removes quoted lines and returns clean text
4945
+
4946
+ Recognizes patterns like:
4947
+
4948
+ * Standard quote markers (`>`)
4949
+ * Reply headers ("On \[date] \[name] wrote:")
4950
+ * Forward indicators ("-----Original Message-----")
4951
+
4952
+ ### HTML Processing (8-Stage Pipeline)
4953
+
4954
+ 1. **Structural Removal**: Directly removes known quotation elements (Gmail divs, blockquotes, Outlook markup)
4955
+ 2. **Checkpoint Fallback**: For non-standard HTML, maps elements to text lines, applies text patterns, removes corresponding HTML
4956
+
4957
+ ### Processing Systems
4958
+
4959
+ **Quotation Removal** (Primary)
4960
+
4961
+ * Removes quoted replies from thread
4962
+ * No initialization required
4963
+ * Rule-based pattern matching
4964
+
4965
+ ***
4966
+
4967
+ ## Getting Started
4968
+
4969
+ <Steps>
4970
+ <Step title="Install Talon">
4971
+ Install via pip with required workaround for Python 3.11+:
4972
+
4973
+ <CodeBlocks>
4974
+ ```bash
4975
+ pip install talon
4976
+ ```
4977
+ </CodeBlocks>
4978
+ </Step>
4979
+
4980
+ <Step title="Apply Python 3.11+ Workaround">
4981
+ Required fix for cchardet dependency:
4982
+
4983
+ <CodeBlocks>
4984
+ ```python
4985
+ # Import workaround BEFORE importing talon
4986
+ import sys
4987
+ import chardet
4988
+ sys.modules['cchardet'] = chardet
4989
+
4990
+ # Now safe to import talon
4991
+ import talon
4992
+ from talon import quotations
4993
+ ```
4994
+ </CodeBlocks>
4995
+
4996
+ <Callout intent="warn">
4997
+ This workaround is **required** for Python 3.11+.
4998
+ </Callout>
4999
+ </Step>
5000
+
5001
+ <Step title="Extract Reply Content">
5002
+ Basic usage for plain text and HTML:
5003
+
5004
+ <CodeBlocks>
5005
+ ```python title="Plain Text"
5006
+ from talon import quotations
5007
+
5008
+ email = """Great work on the project!
5009
+
5010
+ On Mon, Apr 11, 2011 at 6:54 PM, Bob wrote:
5011
+ > Can you review the document?
5012
+ > Need feedback by Friday.
5013
+ """
5014
+
5015
+ clean_reply = quotations.extract_from_plain(email)
5016
+ # Result: "Great work on the project!"
5017
+ ```
5018
+
5019
+ ```python title="HTML"
5020
+ from talon import quotations
5021
+
5022
+ html_email = """
5023
+ <html><body>
5024
+ <div>Thanks for the update!</div>
5025
+ <div class="gmail_quote">
5026
+ <div>On Mon, Alice wrote:</div>
5027
+ <blockquote>Original message here</blockquote>
5028
+ </div>
5029
+ </body></html>
5030
+ """
5031
+
5032
+ clean_html = quotations.extract_from_html(html_email)
5033
+ # Returns: <html><body><div>Thanks for the update!</div></body></html>
5034
+ ```
5035
+ </CodeBlocks>
5036
+ </Step>
5037
+ </Steps>
5038
+
5039
+ ***
5040
+
5041
+ ## Performance & Accuracy
5042
+
5043
+ Talon has been tested on 64 real-world emails from various clients and languages.
5044
+
5045
+ ### Test Results Summary
5046
+
5047
+ | Metric | Value |
5048
+ | ----------------------- | ------------------- |
5049
+ | **Total Tests** | 64 emails |
5050
+ | **Passed** | 60 (93.8%) |
5051
+ | **Failed** | 4 (6.2%) |
5052
+ | **Avg Processing Time** | 1.92ms |
5053
+ | **Throughput** | 488.6 emails/second |
5054
+ | **Min/Max Time** | 0.13ms - 21.55ms |
5055
+
5056
+ ### Test Coverage
5057
+
5058
+ * **22 HTML emails**: Gmail, Outlook, Apple Mail, Thunderbird, Mail.ru, Hotmail
5059
+ * **42 plain text emails**: Various formats and reply styles
5060
+ * **6+ languages**: English, Japanese, Swedish, Polish, Dutch, German
5061
+ * **Mobile clients**: iPhone, Android "Sent from" signatures
5062
+
5063
+ ### Processing Time by Complexity
5064
+
5065
+ | Email Type | Avg Time | Complexity |
5066
+ | ------------------ | --------- | ---------- |
5067
+ | Simple text reply | 0.2-0.5ms | Low |
5068
+ | HTML Gmail/Outlook | 2-4ms | Medium |
5069
+ | Complex threads | 4-22ms | High |
5070
+
5071
+ ### Speed vs Accuracy Tradeoff
5072
+
5073
+ | Library | Avg Processing Time | Accuracy | Best For |
5074
+ | ------------ | ------------------- | -------- | --------------------------------------- |
5075
+ | **Talon** | 1.92ms | 93.8% | Production systems needing HTML support |
5076
+ | qutoequail | 0.96ms | \~85% | Moderate accuracy requirements |
5077
+ | Custom regex | 0.1ms | \~70% | Simple plain text, speed critical |
5078
+
5079
+ **Insight**: For production systems, 1.92ms average is negligible. Even at worst case (21.55ms), Talon is faster than most network requests.
5080
+
5081
+ ***
5082
+
5083
+ ## Known Limitations
5084
+
5085
+ Talon failed 4 out of 64 test cases. Here's what didn't work:
5086
+
5087
+ <AccordionGroup>
5088
+ <Accordion title="Failed Test Cases (4 total)">
5089
+ ### Test Case 1: Complex Email Thread with Mixed Content
5090
+
5091
+ **Input**:
5092
+
5093
+ ```text
5094
+ Thank you, Sonya Johnson.
5095
+
5096
+ I have sent an invite for 10:30am Monday PDT (today). I
5097
+ hope you can join.
5098
+
5099
+ Regards,
5100
+
5101
+ Christopher Edwards
5102
+
5103
+
5104
+ On Mon, Jun 3, 2024 at 12:53 AM Cody Hart <omerritt@example.com> wrote:
5105
+
5106
+ > Hi Christopher Edwards,
5107
+ >
5108
+ > 10.30 AM pacific is good for me.
5109
+ >
5110
+ > Thanks & Regards,
5111
+ >
5112
+ > Cody Hart
5113
+ ```
5114
+
5115
+ **Expected Output**: First 5 lines only (up to `Christopher Edwards`)
5116
+
5117
+ **Talon's Output**: Returns entire email including quoted text starting with "On Mon, Jun 3..." and all "> quoted text"
5118
+
5119
+ **Processing Time**: 2.55ms
5120
+
5121
+ **Issue**: Signature placement before quotes confuses detection logic
5122
+
5123
+ ***
5124
+
5125
+ ### Test Case 2: Inline Responses
5126
+
5127
+ **Input**:
5128
+
5129
+ ```text
5130
+ On Tue, Apr 29, 2014 at 4:22 PM, Example Dev <sugar@example.com> wrote:
5131
+
5132
+ > okay. Well, here's some stuff I can write.
5133
+ >
5134
+ > And if I write a 2 second line you and maybe reply under this?
5135
+ >
5136
+ > Or if you didn't really feel like it, you could reply under this line.
5137
+
5138
+ I will reply under this one
5139
+
5140
+ >
5141
+ > okay?
5142
+ >
5143
+
5144
+ and under this.
5145
+
5146
+ >
5147
+ > -- Tim
5148
+ ```
5149
+
5150
+ **Expected Output**: Just the inline responses (`I will reply under this one` and `and under this.`)
5151
+
5152
+ **Talon's Output**: Returns everything including "On Tue, Apr 29..." header and all quoted lines
5153
+
5154
+ **Processing Time**: 0.48ms
5155
+
5156
+ **Issue**: Interleaved inline responses not recognized as the reply pattern
5157
+
5158
+ ***
5159
+
5160
+ ### Test Case 3: Gmail Forward HTML
5161
+
5162
+ **Input**:
5163
+
5164
+ ```html
5165
+ <html><head></head><body><div dir="ltr">test<div><br /></div><div>blah</div>
5166
+ <div><br /><div class="gmail_quote">---------- Forwarded message ----------<br />
5167
+ From: <b class="gmail_sendername">Foo Bar</b>
5168
+ <span dir="ltr">&lt;<a href="mailto:foo@bar.example">foo@bar.example</a>&gt;</span><br />
5169
+ Date: Thu, Mar 24, 2016 at 5:17 PM<br />
5170
+ Subject: The Subject<br />
5171
+ To: John Doe &lt;<a href="mailto:john@doe.example">john@doe.example</a>&gt;<br />
5172
+ <br /><br /><div dir="ltr">Some text<div><br /></div><div><br /></div></div>
5173
+ </div><br /></div></div></body></html>
5174
+ ```
5175
+
5176
+ **Expected Output**: Just `testblah` (before the forward marker)
5177
+
5178
+ **Talon's Output**: Includes "---------- Forwarded message ----------" and forwarded content
5179
+
5180
+ **Processing Time**: 3.41ms
5181
+
5182
+ **Issue**: HTML forward headers not removed by Gmail quote detection
5183
+
5184
+ ***
5185
+
5186
+ ### Test Case 4: Thunderbird Forward HTML
5187
+
5188
+ **Input**:
5189
+
5190
+ ```html
5191
+ <html><body bgcolor="#FFFFFF" text="#000000">
5192
+ <p><br /></p>
5193
+ <div class="moz-forward-container"><br /><br />
5194
+ -------- Forwarded Message --------
5195
+ <table class="moz-email-headers-table">
5196
+ <tbody>
5197
+ <tr><th>Subject:</th><td>Re: Example subject</td></tr>
5198
+ <tr><th>Date:</th><td>Tue, 3 May 2016 14:54:27 +0200 (CEST)</td></tr>
5199
+ <tr><th>From:</th><td>John Doe &lt;johndoe@example.com&gt;</td></tr>
5200
+ </tbody>
5201
+ </table>
5202
+ <br /><br />
5203
+ <div>Dear John,</div>
5204
+ <div><br /></div>
5205
+ <div>This is a test.</div>
5206
+ </div></body></html>
5207
+ ```
5208
+
5209
+ **Expected Output**: Empty (no new content, just forward)
5210
+
5211
+ **Talon's Output**: Includes "-------- Forwarded Message --------" and forwarded content
5212
+
5213
+ **Processing Time**: 4.34ms
5214
+
5215
+ **Issue**: Thunderbird's `moz-forward-container` class not recognized
5216
+
5217
+ ***
5218
+
5219
+ **Summary**: 3 of 4 failures are forwarded messages. Regular replies work with 98%+ accuracy.
5220
+ </Accordion>
5221
+
5222
+ <Accordion title="Success Examples">
5223
+ ### Example 1: Simple Gmail Reply
5224
+
5225
+ **Input**:
5226
+
5227
+ ```text
5228
+ Awesome! I haven't had another problem with it.
5229
+
5230
+ On Aug 22, 2011, at 7:37 PM, defunkt<reply@reply.github.com> wrote:
5231
+
5232
+
5233
+
5234
+
5235
+ > Loader seems to be working well.
5236
+ ```
5237
+
5238
+ **Talon's Output**: `Awesome! I haven't had another problem with it.`
5239
+
5240
+ **Processing Time**: 0.2ms
5241
+
5242
+ **What Worked**: Standard "On \[date] \[name] wrote:" pattern detected, quote marker (>) recognized
5243
+
5244
+ ***
5245
+
5246
+ ### Example 2: Outlook Reply with Separator
5247
+
5248
+ **Input**:
5249
+
5250
+ ```text
5251
+ Outlook with a reply directly above line
5252
+ ________________________________________
5253
+ From: CRM Comments [crm-comment@example.com]
5254
+ Sent: Friday, 23 March 2012 5:08 p.m.
5255
+ To: John S. Greene
5256
+ Subject: [contact:106] John Greene
5257
+
5258
+ A new comment has been added to the Contact named 'John Greene':
5259
+
5260
+ I am replying to a comment.
5261
+ ```
5262
+
5263
+ **Talon's Output**: `Outlook with a reply directly above line`
5264
+
5265
+ **Processing Time**: 0.51ms
5266
+
5267
+ **What Worked**: Outlook separator line (underscores) and "From:"/"Sent:" headers detected as splitter
5268
+
5269
+ ***
5270
+
5271
+ ### Example 3: HTML Outlook Reply
5272
+
5273
+ **Input**:
5274
+
5275
+ ```html
5276
+ <html>
5277
+ <body>
5278
+ <div>Reply</div>
5279
+ <span id="OLK_SRC_BODY_SECTION">
5280
+ <div>
5281
+ <span>From: </span>Bob &lt;<a href="mailto:bob@example.com">bob@example.com</a>&gt;<br />
5282
+ <span>Date: </span>Tue, 01 Nov 2011 18:54:39 -0700<br />
5283
+ <span>To: </span>Rob &lt;<a href="mailto:rob@example.com">rob@example.com</a>&gt;<br />
5284
+ <span>Subject: </span>Test<br />
5285
+ </div>
5286
+ <div>Hi</div>
5287
+ </span>
5288
+ </body>
5289
+ </html>
5290
+ ```
5291
+
5292
+ **Talon's Output**: `Reply`
5293
+
5294
+ **Processing Time**: 4.02ms
5295
+
5296
+ **What Worked**: Outlook's `OLK_SRC_BODY_SECTION` span ID detected and removed structurally
5297
+ </Accordion>
5298
+
5299
+ <Accordion title="Performance vs Simpler Alternatives">
5300
+ **Tradeoff**: Talon is more comprehensive but slower than plain-text-only libraries
5301
+
5302
+ * Talon: 1.92ms average (with HTML support)
5303
+ * email-reply-parser: 0.03ms average (plain text only)
5304
+
5305
+ For production systems, 1.92ms average is negligible. Even at worst case (21.55ms), Talon is faster than most network requests.
5306
+ </Accordion>
5307
+
5308
+ <Accordion title="Forwarded Messages">
5309
+ As shown in test results, forwarded messages (especially HTML) are challenging:
5310
+
5311
+ * Plain text forwards: Generally work well
5312
+ * HTML forwards: May retain forward headers
5313
+ * Workaround: Use plain text extraction or post-process to remove forward markers
5314
+ </Accordion>
5315
+ </AccordionGroup>
5316
+
5317
+ ***
5318
+
5319
+ ### Error Handling
5320
+
5321
+ Always handle potential parsing failures:
5322
+
5323
+ ```python
5324
+ from talon import quotations
5325
+
5326
+ def safe_extract(email_body, is_html=False):
5327
+ try:
5328
+ if is_html:
5329
+ return quotations.extract_from_html(email_body)
5330
+ else:
5331
+ return quotations.extract_from_plain(email_body)
5332
+ except Exception as e:
5333
+ # Fallback to original message if extraction fails
5334
+ print(f"Talon extraction failed: {e}")
5335
+ return email_body
5336
+ ```
5337
+
5338
+ ### Testing Recommendations
5339
+
5340
+ Always test with your specific email formats:
5341
+
5342
+ ```python
5343
+ # Create a test suite with your actual email patterns (Gmail, Outlook, Apple Mail)
5344
+ test_emails = [
5345
+ "path/to/gmail_reply.html",
5346
+ "path/to/outlook_reply.txt",
5347
+ "path/to/forward.html"
5348
+ ]
5349
+
5350
+ for email_file in test_emails:
5351
+ with open(email_file) as f:
5352
+ content = f.read()
5353
+ result = quotations.extract_from(content)
5354
+ print(f"{email_file}: {len(result)} chars extracted")
5355
+ ```
5356
+
5357
+ <Tip>
5358
+ Test with real emails from your users' actual email clients. Talon's accuracy is based on diverse real-world samples, but your specific use case may have unique patterns.
5359
+ </Tip>
5360
+
5361
+ ***
5362
+
5363
+ ## JavaScript Version
5364
+
5365
+ For TypeScript/JavaScript projects, use **[TalonJS](https://github.com/quentez/talonjs)** - a JavaScript port of Talon with similar functionality.
5366
+
5367
+ ### Performance Comparison
5368
+
5369
+ | Solution | Accuracy | Speed | Best For |
5370
+ | ---------------- | -------- | ------ | --------------------------- |
5371
+ | **Python Talon** | 93.8% | 1.92ms | Highest accuracy |
5372
+ | **TalonJS** | 90.6% | 1.88ms | TypeScript/Node.js projects |
5373
+
5374
+ TalonJS provides 90.6% accuracy with slightly faster performance (1.88ms), making it ideal for JavaScript/TypeScript environments without needing Python dependencies.
5375
+
5376
+ ### Quick Start
5377
+
5378
+ <Steps>
5379
+ <Step title="Install TalonJS">
5380
+ ```bash
5381
+ npm install talonjs
5382
+ ```
5383
+ </Step>
5384
+
5385
+ <Step title="Extract Replies">
5386
+ <CodeBlocks>
5387
+ ```typescript title="Plain Text"
5388
+ import * as talon from 'talonjs';
5389
+
5390
+ const email = `Great work on the project!
5391
+
5392
+ On Mon, Apr 11, 2011 at 6:54 PM, Bob wrote:
5393
+ > Can you review the document?
5394
+ > Need feedback by Friday.
5395
+ `;
5396
+
5397
+ const result = talon.quotations.extractFromPlain(email);
5398
+ const cleanReply = result.body.trim();
5399
+ // Output: "Great work on the project!"
5400
+ ```
5401
+
5402
+ ```typescript title="HTML"
5403
+ import * as talon from 'talonjs';
5404
+
5405
+ const htmlEmail = `
5406
+ <div>Thanks for the update!</div>
5407
+ <div class="gmail_quote">
5408
+ On Mon, Alice wrote: <blockquote>Original</blockquote>
5409
+ </div>
5410
+ `;
5411
+
5412
+ const result = talon.quotations.extractFromHtml(htmlEmail);
5413
+ const cleanReply = result.body.trim();
5414
+ // Returns: "<div>Thanks for the update!</div>"
5415
+ // Note: TalonJS returns clean HTML, not plain text
5416
+ ```
5417
+ </CodeBlocks>
5418
+ </Step>
5419
+ </Steps>
5420
+
5421
+ <Tip>
5422
+ **When to use TalonJS vs Python Talon:**
5423
+
5424
+ * Use **TalonJS** if you're building in TypeScript/JavaScript and 90.6% accuracy is sufficient
5425
+ * Use **Python Talon** if you need the highest accuracy (93.8%) or are in a Python environment
5426
+ * The 3.2% accuracy difference is acceptable for most use cases
5427
+ </Tip>
5428
+
5429
+
4878
5430
  # Join the AgentMail Community
4879
5431
 
4880
5432
  > Connect with the AgentMail team and developers, share what you're building, and get support.
4881
5433
 
4882
5434
  <CardGroup>
4883
- <Card title="Join our Discord Server" href="https://discord.gg/your-invite-code" icon="fa-brands fa-discord">
5435
+ <Card title="Join our Discord Server" href="https://discord.com/invite/hTYatWYWBc" icon="fa-brands fa-discord">
4884
5436
  The best place for real-time conversation, getting help with your code, and
4885
5437
  sharing what you're building with AgentMail.
4886
5438
  </Card>