diff --git a/.env.example b/.env.example
index f6ec9ad..5a90091 100644
--- a/.env.example
+++ b/.env.example
@@ -1,11 +1,13 @@
# OpenRouter API Configuration
-OPENROUTER_API_KEY=your_api_key_here
+# No quotes needed - just paste your key directly
+OPENROUTER_API_KEY=sk-or-v1-xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
# Optional: Your site info for OpenRouter rankings
OPENROUTER_SITE_URL=https://your-site.com
OPENROUTER_SITE_NAME=YourSiteName
# SMTP Credentials for your mail server
+# No quotes needed - even if your password contains special characters
SMTP_USERNAME=your-email@yourdomain.com
SMTP_PASSWORD=your-smtp-password
diff --git a/MODELS.md b/MODELS.md
new file mode 100644
index 0000000..62fcc69
--- /dev/null
+++ b/MODELS.md
@@ -0,0 +1,161 @@
+# OpenRouter Model Reference
+
+Quick reference for choosing the right AI model for News Agent.
+
+## Recommended Models
+
+### Best Overall: `google/gemini-flash-1.5-8b`
+- **Cost:** ~$0.05-0.15/day
+- **Quality:** Excellent
+- **Speed:** Very fast
+- **Best for:** Production use, daily digests
+
+### Best Free: `google/gemini-2.0-flash-exp:free`
+- **Cost:** FREE
+- **Quality:** Good (experimental)
+- **Speed:** Fast
+- **Best for:** Testing, low-budget setups
+- **Note:** Experimental, may change
+
+### Budget Option: `meta-llama/llama-3.1-8b-instruct:free`
+- **Cost:** FREE
+- **Quality:** Decent
+- **Speed:** Moderate
+- **Best for:** Testing, development
+
+### High Quality: `anthropic/claude-3.5-haiku`
+- **Cost:** ~$0.10-0.25/day
+- **Quality:** Excellent
+- **Speed:** Fast
+- **Best for:** When quality matters most
+
+### OpenAI Option: `openai/gpt-4o-mini`
+- **Cost:** ~$0.08-0.20/day
+- **Quality:** Very good
+- **Speed:** Fast
+- **Best for:** Balanced quality/cost
+
+## How to Change Model
+
+Edit `config.yaml`:
+
+```yaml
+ai:
+ model: "google/gemini-flash-1.5-8b" # Change this line
+```
+
+## Full Model List
+
+See all available models at: https://openrouter.ai/models
+
+Filter by:
+- **Free models** - Search for `:free` suffix
+- **Context length** - Important for long articles
+- **Supported features** - JSON mode, function calling, etc.
+
+## Cost Comparison (Daily Estimates)
+
+Based on processing ~50 articles/day with 15 summaries:
+
+| Model | Daily Cost | Monthly Cost | Quality |
+|-------|-----------|--------------|---------|
+| `google/gemini-2.0-flash-exp:free` | $0.00 | $0.00 | Good |
+| `meta-llama/llama-3.1-8b-instruct:free` | $0.00 | $0.00 | Decent |
+| `google/gemini-flash-1.5-8b` | $0.05-0.15 | $1.50-4.50 | Excellent |
+| `openai/gpt-4o-mini` | $0.08-0.20 | $2.40-6.00 | Very Good |
+| `anthropic/claude-3.5-haiku` | $0.10-0.25 | $3.00-7.50 | Excellent |
+| `openai/gpt-4o` | $0.50-1.50 | $15-45 | Outstanding |
+
+*Costs vary based on article length and quantity*
+
+## Testing a New Model
+
+1. Update `config.yaml`:
+ ```yaml
+ ai:
+ model: "new-model-name"
+ ```
+
+2. Run a test:
+ ```bash
+ source .venv/bin/activate
+ python -m src.main
+ ```
+
+3. Check quality of summaries in the email
+
+4. Monitor costs at: https://openrouter.ai/activity
+
+## Model Selection Tips
+
+### Choose FREE models if:
+- You're testing the system
+- Cost is a major concern
+- Article quality/accuracy is less critical
+
+### Choose PAID models if:
+- You want best quality summaries
+- You need reliable daily digests
+- You value accurate relevance scoring
+
+### Performance vs Cost:
+- **Gemini Flash 1.5-8b** - Best balance
+- **Claude Haiku** - Best quality for cost
+- **GPT-4o-mini** - Good all-rounder
+- **Free models** - Testing/development
+
+## Troubleshooting
+
+### Error: "No endpoints found for [model]"
+
+The model name is incorrect or not available on OpenRouter.
+
+**Solution:**
+1. Check model name at: https://openrouter.ai/models
+2. Copy exact model ID (e.g., `google/gemini-flash-1.5-8b`)
+3. Update `config.yaml`
+4. Restart the service
+
+### Model Seems Slow
+
+Some models are slower than others.
+
+**Solutions:**
+- Try `google/gemini-flash-1.5-8b` (fastest)
+- Reduce `max_articles` in config (process fewer articles)
+- Use cheaper/faster models for filtering, premium for summarization
+
+### Poor Summary Quality
+
+The model might not be good at summarization.
+
+**Solutions:**
+- Try `anthropic/claude-3.5-haiku` (excellent summarization)
+- Adjust prompts in `src/ai/prompts.py`
+- Increase temperature for more creative summaries
+- Try different models
+
+### High Costs
+
+You're using an expensive model or processing too many articles.
+
+**Solutions:**
+1. Switch to cheaper model (Gemini Flash or free options)
+2. Increase `min_score` to filter more aggressively
+3. Reduce `max_articles` limit
+4. Monitor usage: https://openrouter.ai/activity
+
+## Advanced: Using Different Models for Different Tasks
+
+You could modify the code to use:
+- **Fast/cheap model** for filtering (scoring articles)
+- **High-quality model** for summarization
+
+Edit `src/main.py` to instantiate different clients for different tasks.
+
+## Need Help?
+
+- **Model pricing:** https://openrouter.ai/models (click model for details)
+- **API docs:** https://openrouter.ai/docs
+- **Check costs:** https://openrouter.ai/activity
+- **Model comparison:** Test different models and compare results
diff --git a/README.md b/README.md
index 508ab2c..78ffa0f 100644
--- a/README.md
+++ b/README.md
@@ -102,7 +102,7 @@ email:
- **SendGrid**: `smtp.sendgrid.net:587`, use API key as password
Optionally adjust:
-- AI model (default: `google/gemini-flash-1.5` - fast and cheap)
+- AI model (default: `google/gemini-flash-1.5-8b` - fast and cheap)
- Filtering threshold (default: 6.5/10)
- Max articles per digest (default: 15)
- RSS sources (add/remove feeds)
@@ -204,11 +204,15 @@ sources:
### AI Configuration
**Models** (from cheap to expensive):
-- `google/gemini-flash-1.5` - Fast, cheap, good quality (recommended)
-- `meta-llama/llama-3.1-8b-instruct` - Very cheap
+- `google/gemini-flash-1.5-8b` - Fast, cheap, good quality (recommended)
+- `google/gemini-2.0-flash-exp:free` - Free, experimental (good for testing)
+- `meta-llama/llama-3.1-8b-instruct:free` - Free, decent quality
- `anthropic/claude-3.5-haiku` - Better quality, slightly more expensive
- `openai/gpt-4o-mini` - Good quality, moderate price
+See **[MODELS.md](MODELS.md)** for detailed comparison and selection guide.
+Full list at: https://openrouter.ai/models
+
**Filtering:**
```yaml
ai:
@@ -263,9 +267,16 @@ systemctl --user restart news-agent.timer
### API Errors
+**Error: "No endpoints found for [model]"**
+- The model name is incorrect
+- Check correct model names in `MODELS.md`
+- Update `config.yaml` with correct model ID
+
+**Other API errors:**
1. **Verify API key in `.env`**
2. **Check OpenRouter credit balance:** https://openrouter.ai/credits
3. **Check rate limits in logs**
+4. **Try a different model** (see `MODELS.md`)
### Service Not Running
@@ -290,12 +301,16 @@ python -m src.main
## Cost Estimation
-Using `google/gemini-flash-1.5` (recommended):
+Using `google/gemini-flash-1.5-8b` (recommended):
- **Daily:** ~$0.05-0.15 (varies by article count)
- **Monthly:** ~$1.50-4.50
- **Yearly:** ~$18-54
+**Free Options:**
+- `google/gemini-2.0-flash-exp:free` - Completely free (experimental)
+- `meta-llama/llama-3.1-8b-instruct:free` - Completely free
+
Factors affecting cost:
- Number of new articles
- Content length
diff --git a/SMTP_CONFIG.md b/SMTP_CONFIG.md
index b3aa195..30e7e2c 100644
--- a/SMTP_CONFIG.md
+++ b/SMTP_CONFIG.md
@@ -23,7 +23,21 @@ SMTP_USERNAME=your-email@yourdomain.com
SMTP_PASSWORD=your-password-or-app-password
```
-**Security Note:** The `.env` file is gitignored and should never be committed to version control.
+**Important Notes:**
+- **NO quotes needed** - Values should be plain text without quotes
+- **Special characters are OK** - Passwords with `!@#$%` etc. work fine
+- **Security:** The `.env` file is gitignored and should never be committed to version control
+
+**Examples:**
+```env
+# Correct - no quotes
+SMTP_USERNAME=admin@example.com
+SMTP_PASSWORD=P@ssw0rd!123
+
+# Wrong - don't use quotes
+SMTP_USERNAME="admin@example.com" # ❌
+SMTP_PASSWORD='P@ssw0rd!123' # ❌
+```
### 2. Edit `config.yaml`
diff --git a/config.yaml b/config.yaml
index 0fcc02a..6cba7a0 100644
--- a/config.yaml
+++ b/config.yaml
@@ -61,9 +61,9 @@ sources:
url: "https://www.engadget.com/rss.xml"
category: "gadgets"
- - name: "AnandTech"
- url: "https://www.anandtech.com/rss/"
- category: "gadgets"
+ #- name: "AnandTech"
+ # url: "https://www.anandtech.com/rss/"
+ # category: "gadgets"
- name: "Tom's Hardware"
url: "https://www.tomshardware.com/feeds/all"
@@ -72,10 +72,12 @@ sources:
ai:
provider: "openrouter"
base_url: "https://openrouter.ai/api/v1"
- model: "google/gemini-flash-1.5"
- # Alternative models:
+ model: "google/gemini-flash-1.5-8b"
+ # Alternative models (see https://openrouter.ai/models for full list):
+ # - "google/gemini-2.0-flash-exp:free" (free, experimental)
# - "anthropic/claude-3.5-haiku" (better quality, slightly more expensive)
- # - "meta-llama/llama-3.1-8b-instruct" (very cheap)
+ # - "meta-llama/llama-3.1-8b-instruct:free" (free, good quality)
+ # - "openai/gpt-4o-mini" (good quality, moderate price)
filtering:
enabled: true
diff --git a/src/test_email_simple.py b/src/test_email_simple.py
new file mode 100644
index 0000000..0e968f9
--- /dev/null
+++ b/src/test_email_simple.py
@@ -0,0 +1,109 @@
+#!/usr/bin/env python3
+"""Simple email test - run with: python -m src.test_email_simple"""
+
+from datetime import datetime
+from .config import get_config
+from .logger import setup_logger
+from .email.sender import EmailSender
+
+
+def main():
+ """Send a test email"""
+ setup_logger()
+ config = get_config()
+
+ print("\n" + "=" * 60)
+ print("Testing Email Configuration")
+ print("=" * 60)
+
+ print(f"\nSMTP Server: {config.email.smtp.host}:{config.email.smtp.port}")
+ print(f"TLS: {config.email.smtp.use_tls}")
+ print(f"SSL: {config.email.smtp.use_ssl}")
+ print(f"From: {config.email.from_}")
+ print(f"To: {config.email.to}")
+ print(f"Username: {config.env.smtp_username or '(not set)'}")
+ print(f"Password: {'***' if config.env.smtp_password else '(not set)'}")
+
+ print("\nSending test email...")
+
+ # Create test email content
+ subject = "News Agent Test Email"
+
+ html_content = f"""
+
+
+
+
+
+
+
+
+
Congratulations! Your email configuration is working correctly.
+
+
Test Details:
+
+ - Date: {datetime.now().strftime("%Y-%m-%d %H:%M:%S")}
+ - SMTP Server: {config.email.smtp.host}:{config.email.smtp.port}
+ - Encryption: {"TLS" if config.email.smtp.use_tls else "SSL" if config.email.smtp.use_ssl else "None"}
+
+
+
Your News Agent is now ready to send daily digests!
+
+
+
+ This is a test email from News Agent. If you received this,
+ your SMTP configuration is correct and you should start receiving
+ daily news digests.
+
+
+
+
+ """
+
+ text_content = f"""
+News Agent Email Test
+=====================
+
+✓ Congratulations! Your email configuration is working correctly.
+
+Test Details:
+- Date: {datetime.now().strftime("%Y-%m-%d %H:%M:%S")}
+- SMTP Server: {config.email.smtp.host}:{config.email.smtp.port}
+- Encryption: {"TLS" if config.email.smtp.use_tls else "SSL" if config.email.smtp.use_ssl else "None"}
+
+Your News Agent is now ready to send daily digests!
+
+---
+This is a test email from News Agent. If you received this,
+your SMTP configuration is correct and you should start receiving
+daily news digests.
+ """
+
+ # Send email
+ sender = EmailSender()
+ success = sender.send(subject, html_content, text_content)
+
+ print("\n" + "=" * 60)
+ if success:
+ print("✓ SUCCESS! Test email sent successfully!")
+ print(f"✓ Check your inbox: {config.email.to}")
+ else:
+ print("✗ FAILED! Email could not be sent.")
+ print("✗ Check the error messages above.")
+ print("\nTroubleshooting steps:")
+ print("1. Verify SMTP credentials in .env file")
+ print("2. Check SMTP server settings in config.yaml")
+ print("3. Review logs: cat data/logs/news-agent.log")
+ print("4. See SMTP_CONFIG.md for detailed troubleshooting")
+ print("=" * 60 + "\n")
+
+
+if __name__ == "__main__":
+ main()
diff --git a/test_email.py b/test_email.py
new file mode 100644
index 0000000..bc00678
--- /dev/null
+++ b/test_email.py
@@ -0,0 +1,132 @@
+#!/usr/bin/env python3
+"""Quick email sending test script"""
+
+import sys
+from datetime import datetime
+
+# Add src to path
+sys.path.insert(0, "src")
+
+from src.config import get_config
+from src.logger import setup_logger
+from src.email.sender import EmailSender
+
+
+def test_email():
+ """Send a test email"""
+ setup_logger()
+ config = get_config()
+
+ print("\n" + "=" * 60)
+ print("Testing Email Configuration")
+ print("=" * 60)
+
+ print(f"\nSMTP Server: {config.email.smtp.host}:{config.email.smtp.port}")
+ print(f"TLS: {config.email.smtp.use_tls}")
+ print(f"SSL: {config.email.smtp.use_ssl}")
+ print(f"From: {config.email.from_}")
+ print(f"To: {config.email.to}")
+ print(f"Username: {config.env.smtp_username or '(not set)'}")
+ print(f"Password: {'***' if config.env.smtp_password else '(not set)'}")
+
+ print("\nSending test email...")
+
+ # Create test email content
+ subject = "News Agent Test Email"
+
+ html_content = (
+ """
+
+
+
+
+
+
+
+
+
Congratulations! Your email configuration is working correctly.
+
+
Test Details:
+
+ - Date: """
+ + datetime.now().strftime("%Y-%m-%d %H:%M:%S")
+ + """
+ - SMTP Server: """
+ + f"{config.email.smtp.host}:{config.email.smtp.port}"
+ + """
+ - Encryption: """
+ + ("TLS" if config.email.smtp.use_tls else "SSL" if config.email.smtp.use_ssl else "None")
+ + """
+
+
+
Your News Agent is now ready to send daily digests!
+
+
+
+ This is a test email from News Agent. If you received this,
+ your SMTP configuration is correct and you should start receiving
+ daily news digests.
+
+
+
+
+ """
+ )
+
+ text_content = f"""
+News Agent Email Test
+=====================
+
+✓ Congratulations! Your email configuration is working correctly.
+
+Test Details:
+- Date: {datetime.now().strftime("%Y-%m-%d %H:%M:%S")}
+- SMTP Server: {config.email.smtp.host}:{config.email.smtp.port}
+- Encryption: {"TLS" if config.email.smtp.use_tls else "SSL" if config.email.smtp.use_ssl else "None"}
+
+Your News Agent is now ready to send daily digests!
+
+---
+This is a test email from News Agent. If you received this,
+your SMTP configuration is correct and you should start receiving
+daily news digests.
+ """
+
+ # Send email
+ sender = EmailSender()
+ success = sender.send(subject, html_content, text_content)
+
+ print("\n" + "=" * 60)
+ if success:
+ print("✓ SUCCESS! Test email sent successfully!")
+ print(f"✓ Check your inbox: {config.email.to}")
+ else:
+ print("✗ FAILED! Email could not be sent.")
+ print("✗ Check the error messages above.")
+ print("\nTroubleshooting steps:")
+ print("1. Verify SMTP credentials in .env file")
+ print("2. Check SMTP server settings in config.yaml")
+ print("3. Review logs: cat data/logs/news-agent.log")
+ print("4. See SMTP_CONFIG.md for detailed troubleshooting")
+ print("=" * 60 + "\n")
+
+ return success
+
+
+if __name__ == "__main__":
+ try:
+ success = test_email()
+ sys.exit(0 if success else 1)
+ except Exception as e:
+ print(f"\n✗ ERROR: {e}")
+ import traceback
+
+ traceback.print_exc()
+ sys.exit(1)