diff --git a/README.md b/README.md index 007c046..d119ec0 100644 --- a/README.md +++ b/README.md @@ -82,20 +82,6 @@ This file stores your OpenAI API key: OPENAI_API_KEY=your_openai_api_key ``` -## 🚦 Rate Limiting - -To ensure fair usage and prevent any single user or client from consuming too many resources or making too many requests in a short amount of time, I have implemented rate limiting. - -Each cloud endpoint (AWS, Azure, and GCP) has a rate limit of 1 request per minute. This means that each client can make up to 1 request per minute to each endpoint. - -Here are the rate-limited endpoints: - -- AWS News Report: http://localhost:8000/aws -- Azure News Report: http://localhost:8000/azure -- GCP News Report: http://localhost:8000/gcp - -If a client exceeds this limit, they will receive a `429 Too Many Requests` response until enough time has passed for the rate limit to reset. - ## 🔒 Security This project uses the following open source Static Application Security Testing (SAST) tools to ensure the quality and diff --git a/feeds/AWSFeed.py b/feeds/AWSFeed.py index 83ea97d..199515d 100644 --- a/feeds/AWSFeed.py +++ b/feeds/AWSFeed.py @@ -8,6 +8,7 @@ from feeds.RSSFeed import RSSFeed from summarizer import summarize_text +from summarizers.OpenAISummarizer import OpenAISummarizer class AWSFeed(RSSFeed): @@ -17,6 +18,8 @@ class AWSFeed(RSSFeed): def __init__(self, filtered_entries): super().__init__(filtered_entries) + self.summarizer = OpenAISummarizer() + @overrides def process_entry(self, entry, one_week_ago): @@ -29,7 +32,7 @@ def process_entry(self, entry, one_week_ago): published_date = datetime.strptime(entry.published, '%a, %d %b %Y %H:%M:%S %Z') if published_date >= one_week_ago: - summary = summarize_text(entry.summary) + summary = self.summarizer.summarize_text(entry.summary) return { 'title': entry.title, 'link': entry.link, diff --git a/feeds/AzureFeed.py b/feeds/AzureFeed.py index bf4bfdf..e4e432b 100644 --- a/feeds/AzureFeed.py +++ b/feeds/AzureFeed.py @@ -9,6 +9,7 @@ from feeds.RSSFeed import RSSFeed from summarizer import summarize_text +from summarizers.OpenAISummarizer import OpenAISummarizer class AzureFeed(RSSFeed): @@ -18,6 +19,7 @@ class AzureFeed(RSSFeed): def __init__(self, url): super().__init__(url) + self.summarizer = OpenAISummarizer() @overrides def process_entry(self, entry, one_week_ago): @@ -35,7 +37,7 @@ def process_entry(self, entry, one_week_ago): one_week_ago = one_week_ago.replace(tzinfo=utc) if published_date >= one_week_ago: - summary = summarize_text(entry.summary) + summary = self.summarizer.summarize_text(entry.summary) return { 'title': entry.title, 'link': entry.link, diff --git a/feeds/GCPFeed.py b/feeds/GCPFeed.py index 59d3ff1..18c1222 100644 --- a/feeds/GCPFeed.py +++ b/feeds/GCPFeed.py @@ -10,6 +10,7 @@ from feeds.RSSFeed import RSSFeed from summarizer import summarize_text +from summarizers.OpenAISummarizer import OpenAISummarizer class GCPFeed(RSSFeed): @@ -19,6 +20,8 @@ class GCPFeed(RSSFeed): def __init__(self, filtered_entries): super().__init__(filtered_entries) + self.summarizer = OpenAISummarizer() + @overrides def process_entry(self, entry, one_week_ago): @@ -32,7 +35,7 @@ def process_entry(self, entry, one_week_ago): now = datetime.now(timezone.utc) one_week_ago = now - timedelta(days=7) if updated_date >= one_week_ago: - content_type = summarize_text(entry['content']) + content_type = self.summarizer.summarize_text(entry['content']) return { 'title': entry.title, 'link': entry.link, diff --git a/main.py b/main.py index c14ee63..db56bd9 100644 --- a/main.py +++ b/main.py @@ -1,6 +1,5 @@ import logging -import limiter from fastapi import FastAPI, HTTPException from fastapi.responses import HTMLResponse @@ -34,18 +33,15 @@ def generate_report(feed_class, feed_name): @app.get("/aws", response_class=HTMLResponse) -@limiter.limit("1/minute") def aws_report(): return generate_report(AWSFeed, "AWS") @app.get("/gcp", response_class=HTMLResponse) -@limiter.limit("1/minute") def gcp_report(): return generate_report(GCPFeed, "GCP") @app.get("/azure", response_class=HTMLResponse) -@limiter.limit("1/minute") def azure_report(): return generate_report(AzureFeed, "Azure") diff --git a/summarizers/BaseSummarizer.py b/summarizers/BaseSummarizer.py new file mode 100644 index 0000000..1343cd1 --- /dev/null +++ b/summarizers/BaseSummarizer.py @@ -0,0 +1,12 @@ +from abc import ABC, abstractmethod +from configparser import ConfigParser + + +class BaseSummarizer(ABC): + def __init__(self): + self.config = ConfigParser() + self.config.read('config.ini') + + @abstractmethod + def summarize_text(self, text): + pass diff --git a/summarizers/ClaudeSummarizer.py b/summarizers/ClaudeSummarizer.py new file mode 100644 index 0000000..e0d9c16 --- /dev/null +++ b/summarizers/ClaudeSummarizer.py @@ -0,0 +1,41 @@ +import requests + +from summarizers.BaseSummarizer import BaseSummarizer + + +class ClaudeSummarizer(BaseSummarizer): + def __init__(self): + super().__init__() + self.api_key = self.config.get('CLAUDE', 'api_key') + + def summarize_text(self, text): + """ + This function uses the Claude API to summarize provided text. + + :param text: the text which will be summarized + :return: summarized text + """ + # Define the API endpoint + url = "https://api.claude.ai/summarize" + + # Define the headers for the API request + headers = { + "Content-Type": "application/json", + "Authorization": f"Bearer {self.api_key}" + } + + # Define the body of the API request + data = { + "text": text + } + + # Make the API request + response = requests.post(url, headers=headers, json=data) + + # Check the response status code + if response.status_code == 200: + # If the request was successful, return the summarized text + return response.json()["summary"] + else: + # If the request was not successful, raise an exception + raise Exception(f"Claude API request failed with status code {response.status_code}") diff --git a/summarizers/OpenAISummarizer.py b/summarizers/OpenAISummarizer.py new file mode 100644 index 0000000..a47c590 --- /dev/null +++ b/summarizers/OpenAISummarizer.py @@ -0,0 +1,29 @@ +import openai + +from summarizers.BaseSummarizer import BaseSummarizer + + +class OpenAISummarizer(BaseSummarizer): + def summarize_text(self, text): + model = self.config.get('OPENAI', 'model') + temperature = self.config.getfloat('OPENAI', 'temperature') + max_tokens = self.config.getint('OPENAI', 'max_tokens') + top_p = self.config.getint('OPENAI', 'top_p') + + response = openai.chat.completions.create( + model=model, + messages=[ + { + "role": "system", + "content": "Summarize content you are provided with for a second-grade student." + }, + { + "role": "user", + "content": f"Summarize the following text in one sentence:\n\n{text}" + } + ], + temperature=temperature, + max_tokens=max_tokens, + top_p=top_p + ) + return response.choices[0].message.content diff --git a/summarizers/PerplexitySummarizer.py b/summarizers/PerplexitySummarizer.py new file mode 100644 index 0000000..a5a4cf1 --- /dev/null +++ b/summarizers/PerplexitySummarizer.py @@ -0,0 +1,41 @@ +import requests + +from summarizers.BaseSummarizer import BaseSummarizer + + +class PerplexitySummarizer(BaseSummarizer): + def __init__(self): + super().__init__() + self.api_key = self.config.get('PERPLEXITY', 'api_key') + + def summarize_text(self, text): + """ + This function uses the Perplexity API to summarize provided text. + + :param text: the text which will be summarized + :return: summarized text + """ + # Define the API endpoint + url = "https://api.perplexity.ai/summarize" + + # Define the headers for the API request + headers = { + "Content-Type": "application/json", + "Authorization": f"Bearer {self.api_key}" + } + + # Define the body of the API request + data = { + "text": text + } + + # Make the API request + response = requests.post(url, headers=headers, json=data) + + # Check the response status code + if response.status_code == 200: + # If the request was successful, return the summarized text + return response.json()["summary"] + else: + # If the request was not successful, raise an exception + raise Exception(f"Perplexity API request failed with status code {response.status_code}") diff --git a/summarizers/README.md b/summarizers/README.md new file mode 100644 index 0000000..bb00c3a --- /dev/null +++ b/summarizers/README.md @@ -0,0 +1,27 @@ +# Summarizers + +This directory contains the classes responsible for text summarization in the project. Each class represents a different +summarization API. + +## BaseSummarizer + +`BaseSummarizer` is an abstract base class that defines the common interface for all summarizers. It handles the common +functionality such as reading from the `config.ini` file. Any class that inherits from `BaseSummarizer` must implement +the `summarize_text` method. + +## [OpenAISummarizer](https://openai.com) + +`OpenAISummarizer` is a class that uses the OpenAI API for text summarization. It inherits from `BaseSummarizer` and +implements the `summarize_text` method. + +## [ClaudeSummarizer](https://www.example.com) + +`ClaudeSummarizer` is a class that uses the Claude API for text summarization. It inherits from `BaseSummarizer` and +implements the `summarize_text` method. The implementation is currently a placeholder and needs to be filled in with the +actual code to call the Claude API and summarize the text. + +## [PerplexitySummarizer](https://www.perplexity.ai) + +`PerplexitySummarizer` is a class that uses the Perplexity API for text summarization. It inherits from `BaseSummarizer` and +implements the `summarize_text` method. + diff --git a/summarizers/__init__.py b/summarizers/__init__.py new file mode 100644 index 0000000..e69de29