From e91ecfbde88af314e7e9a54f64639feb9e24d935 Mon Sep 17 00:00:00 2001 From: Sri Laasya Nutheti Date: Wed, 29 Jan 2025 19:06:07 -0800 Subject: [PATCH 1/2] VC_Pitchdeck_analyzer --- .DS_Store | Bin 0 -> 6148 bytes agentstack.log | 3 + pitchdeck_analyzer/.DS_Store | Bin 0 -> 6148 bytes pitchdeck_analyzer/.env.example | 12 ++ pitchdeck_analyzer/.gitignore | 191 ++++++++++++++++++ pitchdeck_analyzer/LICENSE.md | 10 + pitchdeck_analyzer/README.md | 37 ++++ pitchdeck_analyzer/agentstack.json | 10 + pitchdeck_analyzer/pyproject.toml | 13 ++ pitchdeck_analyzer/src/.DS_Store | Bin 0 -> 6148 bytes pitchdeck_analyzer/src/__init__.py | 0 pitchdeck_analyzer/src/config/agents.yaml | 8 + pitchdeck_analyzer/src/config/inputs.yaml | 0 pitchdeck_analyzer/src/config/tasks.yaml | 10 + pitchdeck_analyzer/src/crew.py | 187 +++++++++++++++++ pitchdeck_analyzer/src/main.py | 58 ++++++ pitchdeck_analyzer/src/tools/__init__.py | 6 + pitchdeck_analyzer/src/tools/fetch_emails.py | 107 ++++++++++ pitchdeck_analyzer/src/tools/pdf_reader.py | 112 ++++++++++ pitchdeck_analyzer/src/tools/write_to_docs.py | 170 ++++++++++++++++ 20 files changed, 934 insertions(+) create mode 100644 .DS_Store create mode 100644 agentstack.log create mode 100644 pitchdeck_analyzer/.DS_Store create mode 100644 pitchdeck_analyzer/.env.example create mode 100644 pitchdeck_analyzer/.gitignore create mode 100644 pitchdeck_analyzer/LICENSE.md create mode 100644 pitchdeck_analyzer/README.md create mode 100644 pitchdeck_analyzer/agentstack.json create mode 100644 pitchdeck_analyzer/pyproject.toml create mode 100644 pitchdeck_analyzer/src/.DS_Store create mode 100644 pitchdeck_analyzer/src/__init__.py create mode 100644 pitchdeck_analyzer/src/config/agents.yaml create mode 100644 pitchdeck_analyzer/src/config/inputs.yaml create mode 100644 pitchdeck_analyzer/src/config/tasks.yaml create mode 100644 pitchdeck_analyzer/src/crew.py create mode 100644 pitchdeck_analyzer/src/main.py create mode 100644 pitchdeck_analyzer/src/tools/__init__.py create mode 100644 pitchdeck_analyzer/src/tools/fetch_emails.py create mode 100644 pitchdeck_analyzer/src/tools/pdf_reader.py create mode 100644 pitchdeck_analyzer/src/tools/write_to_docs.py diff --git a/.DS_Store b/.DS_Store new file mode 100644 index 0000000000000000000000000000000000000000..4db3611c22028eee39acc3bb4bdb19fa8740f204 GIT binary patch literal 6148 zcmeHKQA@)x5WdW*9YYwR;A6noftwqm_)@0+0W12TGFv-bS#8GFS#%8gtbfQ~;_vY; z$;2@QA4Fu_!R5PL?h^8)6{d-hfwG_x)4GKZlMS5 zxoCF$M+WHZ)}ap(q#EF4{d6>J`GZU9ohcOuQ`-;DqfXYYY#ga1^`oRS&RitZI3=glu569R&1SPI8}`f*mXLEb!@U-2F?-TWG2p0G)YFV>5 zg%>n_wATl3ki;sv!}#f3(>WNK0cL<1SOf;_-R6`RVKcl3W`G&^RR-vOkf?;N#oVC2 zIRDG%m6d6Pz;FTv3K0UE!o;Sw>he{67>d^ pgyM38A1P?)QjD=winma;pkI=K=vvGTq6dY41T+oYFav+ez&kAER8Rl_ literal 0 HcmV?d00001 diff --git a/agentstack.log b/agentstack.log new file mode 100644 index 0000000..598c24c --- /dev/null +++ b/agentstack.log @@ -0,0 +1,3 @@ +ERROR: An error occurred: +Could not find agentstack.json, are you in an AgentStack project directory? +INFO: Run again with --debug for more information. diff --git a/pitchdeck_analyzer/.DS_Store b/pitchdeck_analyzer/.DS_Store new file mode 100644 index 0000000000000000000000000000000000000000..de6e6f8b55470c9d66023b26f9c9cbea770bd067 GIT binary patch literal 6148 zcmeHK%}yIJ5Vn&5ju27~R0=nXUL%n}gh0J4Aw5^>Rv=VR$g&Zv*sP;$LJm=+J;QVK z9e4uHJ@w8T^btB^PiRnpV;i9v$^Pv5@z1xGy(5H>jbT_5q9lY+P!MxPs9q4XW0{j2 zX_f&cj*<3)*aXnK4|;RntXM<_@Z04@D*ECg7|njaI7$cA>Mxng7nYV+)QVbFXa1EL z`GfXgm^Rw|@7VihOdL$x?cgZtj9R7jLz4{JQPSz#fGF&M%jt2Hgl5z*!z4^?tnUn} zsEVypc|5My>lM9I+nZGMcz?TE(e<6($)u=0eBAo@C|G4YIfA0I4{k2t`4m% z%m6dM3@jo8bWf?(MO+-Oi5Xx9{wo7`KWHe39%1ItUL8=`6acUQYAevjTmoY>p+}fG z1Ph3Cp@1$_Qi>s6IOsL`d4!om7cL|vAChL4l%YsFJB+WbyO5qku9*R5;5h?%*Dc}u z|M~U&|9KGim;q+sT`?dFO~2WIEz#RLw>f-kHI#EGDCn0td + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. \ No newline at end of file diff --git a/pitchdeck_analyzer/README.md b/pitchdeck_analyzer/README.md new file mode 100644 index 0000000..f268475 --- /dev/null +++ b/pitchdeck_analyzer/README.md @@ -0,0 +1,37 @@ +# pitchdeck_analyzer +This is the start of your AgentStack project. + +## How to build your Crew Agent +### With the CLI +Add an agent using AgentStack with the CLI: +`agentstack generate agent ` +You can also shorten this to `agentstack g a ` +For wizard support use `agentstack g a --wizard` +Finally for creation in the CLI alone, use `agentstack g a --role/-r --goal/-g --backstory/-b --model/-m ` + +This will automatically create a new agent in the `agents.yaml` config as well as in your code. Either placeholder strings will be used, or data included in the wizard. + +Similarly, tasks can be created with `agentstack g t ` + +Add tools with `agentstack tools add` and view tools available with `agentstack tools list` + +## How to use your Agent +In this directory, run `uv pip install --requirements pyproject.toml` + +To run your project, use the following command: +`agentstack run` + +This will initialize your crew of AI agents and begin task execution as defined in your configuration in the main.py file. + +#### Replay Tasks from Latest Crew Kickoff: + +CrewAI now includes a replay feature that allows you to list the tasks from the last run and replay from a specific one. To use this feature, run: +`crewai replay ` +Replace with the ID of the task you want to replay. + +#### Reset Crew Memory +If you need to reset the memory of your crew before running it again, you can do so by calling the reset memory feature: +`crewai reset-memory` +This will clear the crew's memory, allowing for a fresh start. + +> 🪩 Project built with [AgentStack](https://github.com/AgentOps-AI/AgentStack) \ No newline at end of file diff --git a/pitchdeck_analyzer/agentstack.json b/pitchdeck_analyzer/agentstack.json new file mode 100644 index 0000000..8b098bf --- /dev/null +++ b/pitchdeck_analyzer/agentstack.json @@ -0,0 +1,10 @@ +{ + "framework": "crewai", + "tools": [ + "file_read", + "composio" + ], + "agentstack_version": "0.3.0", + "template": "hello_alex", + "template_version": "4" +} \ No newline at end of file diff --git a/pitchdeck_analyzer/pyproject.toml b/pitchdeck_analyzer/pyproject.toml new file mode 100644 index 0000000..9ba47b6 --- /dev/null +++ b/pitchdeck_analyzer/pyproject.toml @@ -0,0 +1,13 @@ +[project] +name = "pitchdeck_analyzer" +version = "0.0.1" +description = "This is the start of your AgentStack project." +authors = [ + { name = "Name " } +] +license = { text = "MIT" } +requires-python = ">=3.10" + +dependencies = [ + "agentstack[crewai]>=0.3.0", +] \ No newline at end of file diff --git a/pitchdeck_analyzer/src/.DS_Store b/pitchdeck_analyzer/src/.DS_Store new file mode 100644 index 0000000000000000000000000000000000000000..65243e51e6c1119962475e15037c7a5b0663c236 GIT binary patch literal 6148 zcmeHKy-EW?5T1=uf(oV-Z1x2d@&umY?1dD9jpj!a@#2N3g;v*E_!t(pzJp?E<0HgJ z5G(}0*&X!OiwF@!W#-uV_GV}H=L@%!AtF;h>C}j-L{vdzEYCuX5gzAOkdj#DfyT|T zPu-+jYqZn;px~_rWk4DDZ4B_YJ405%rQ@cot^K92X(IBq3r3--$O>cwdD@^-E0 z)A`b8(97Qw7E{m?4X}e_?50N@+65laHZ`%cCM9%AXRr-v1-gr88NR&dG`w+2!^vmL zpY7@D{87#};lBFmI}6{RE#zy1`I-h-DV~ImLJk&Q!qNP>?r)y&w)}6Fi_h+lH6K29Oqh#wu8pPewjxX0ww9#bvP9-qB+5s- + You are an expert at processing emails and handling attachments. + goal: >- + Fetch recent emails and download any PDF attachments to a local folder. + backstory: >- + You are specialized in email processing and file handling, with a focus on efficiently managing attachments. + llm: openai/gpt-4 diff --git a/pitchdeck_analyzer/src/config/inputs.yaml b/pitchdeck_analyzer/src/config/inputs.yaml new file mode 100644 index 0000000..e69de29 diff --git a/pitchdeck_analyzer/src/config/tasks.yaml b/pitchdeck_analyzer/src/config/tasks.yaml new file mode 100644 index 0000000..a5be91c --- /dev/null +++ b/pitchdeck_analyzer/src/config/tasks.yaml @@ -0,0 +1,10 @@ +fetch_emails: + description: >- + Check the last 5 emails in the inbox: + 1. Print email details (subject, sender, content) + 2. If email has PDF attachment, download it to local downloads folder + 3. Skip emails without PDF attachments + 4. Report number of PDFs downloaded + expected_output: >- + A summary of processed emails and any downloaded PDFs. + agent: email_agent diff --git a/pitchdeck_analyzer/src/crew.py b/pitchdeck_analyzer/src/crew.py new file mode 100644 index 0000000..edff27a --- /dev/null +++ b/pitchdeck_analyzer/src/crew.py @@ -0,0 +1,187 @@ +from crewai import Agent, Crew, Process, Task +from crewai.project import CrewBase, agent, crew, task +from tools.fetch_emails import get_message_content +from tools.pdf_reader import upload_pdf, ask_question +from tools.write_to_docs import append_row +import os +import base64 +from googleapiclient.discovery import build +from google.oauth2.credentials import Credentials +from google.auth.transport.requests import Request +from google_auth_oauthlib.flow import InstalledAppFlow +from dotenv import load_dotenv + +# Load environment variables from .env file +load_dotenv() + +@CrewBase +class EmailFetcherCrew(): + """Crew for fetching emails, processing PDFs, and updating spreadsheets""" + + @task + def process_emails(self) -> Task: + """Process emails, download PDFs, extract data, and update spreadsheet.""" + self.get_emails() + return Task( + description=""" + 1. Check the last 5 emails in the inbox + 2. Download PDF attachments + 3. Extract required information from PDFs + 4. Append extracted data to the spreadsheet + """, + expected_output=""" + A summary of processed emails, extracted data, and spreadsheet updates. + """, + agent=self.email_agent() + ) + + def get_emails(self): + """Get the last 5 emails from Gmail and process attachments.""" + try: + SCOPES = [ + 'https://www.googleapis.com/auth/spreadsheets', + 'https://www.googleapis.com/auth/gmail.readonly' + ] + creds = None + token_path = "src/token.json" + + if os.path.exists(token_path): + creds = Credentials.from_authorized_user_file(token_path, SCOPES) + if not creds or not creds.valid: + if creds and creds.expired and creds.refresh_token: + creds.refresh(Request()) + else: + flow = InstalledAppFlow.from_client_secrets_file( + "src/tools/credentials.json", SCOPES) + creds = flow.run_local_server(port=0) + with open(token_path, "w") as token: + token.write(creds.to_json()) + + service = build("gmail", "v1", credentials=creds) + results = service.users().messages().list(userId='me', maxResults=5).execute() + messages = results.get('messages', []) + + for message in messages: + msg = service.users().messages().get(userId='me', id=message['id'], format='full').execute() + headers = msg['payload']['headers'] + subject = next((h['value'] for h in headers if h['name'].lower() == 'subject'), 'No Subject') + sender = next((h['value'] for h in headers if h['name'].lower() == 'from'), 'No Sender') + + print(f"\nEmail from: {sender}") + print(f"Subject: {subject}") + + if 'parts' in msg['payload']: + for part in msg['payload']['parts']: + if part.get('filename', '').endswith('.pdf'): + attachment_id = part['body'].get('attachmentId') + if attachment_id: + file_path = self.download_attachment(service, message['id'], attachment_id) + if file_path: + self.process_pdf(file_path) + + except Exception as e: + print(f"Error accessing emails: {str(e)}") + + def download_attachment(self, service, message_id: str, attachment_id: str) -> str: + """Download a PDF attachment from an email.""" + try: + attachment = service.users().messages().attachments().get( + userId='me', messageId=message_id, id=attachment_id).execute() + + file_data = base64.urlsafe_b64decode(attachment['data'].encode('UTF-8')) + + # Save to current directory + file_path = f'attachment_{message_id}.pdf' + with open(file_path, 'wb') as f: + f.write(file_data) + + print(f"Downloaded PDF: {file_path}") + return file_path + + except Exception as e: + print(f"Error downloading attachment: {str(e)}") + return None + + def process_pdf(self, pdf_path: str): + """Process the PDF to extract information and update the spreadsheet.""" + try: + api_key = os.getenv("CHATPDF_API_KEY") + if not api_key: + print("ChatPDF API key not found. Please set the CHATPDF_API_KEY in your .env file.") + return + + # Ensure the correct path is used + pdf_path = os.path.abspath(pdf_path) + print(f"Processing PDF: {pdf_path}") + + source_id = upload_pdf(api_key, pdf_path) + if not source_id: + print("Failed to upload PDF.") + return + + print("PDF uploaded successfully. Extracting information...") + + questions = [ + "Who are the founders of this company?", + "What is the company name?", + "What is the main business idea or summary?", + "What industry or sector is this company in?", + "How much funding are they asking for (the ask amount)?", + "What is the company's current valuation?", + "What previous funding rounds have they had?", + "What is their current revenue or traction?", + "What is the contact email mentioned in the document?" + ] + + results = {} + for question in questions: + print(f"\nAsking: {question}") + answer = ask_question(api_key, source_id, question) + if answer: + results[question] = answer + print(f"Answer: {answer}") + else: + print(f"Failed to get answer for: {question}") + results[question] = "" + + print("\nUpdating spreadsheet with extracted information...") + success = append_row( + founder1=results.get("Who are the founders/team of this company?", ""), + company_name=results.get("What is the company name?", ""), + idea_summary=results.get("What is the main business idea or summary?", ""), + industry=results.get("What industry or sector is this company in?", ""), + ask=results.get("How much funding are they asking for (the ask amount)?", ""), + valuation=results.get("What is the company's current valuation?", ""), + previous_rounds=results.get("What previous funding rounds have they had?", ""), + revenue_traction=results.get("What is their current revenue or traction?", ""), + email=results.get("What is the contact email mentioned in the document?", "") + ) + + if success: + print("Spreadsheet updated successfully.") + else: + print("Failed to update spreadsheet.") + + except Exception as e: + print(f"Error processing PDF: {str(e)}") + + @agent + def email_agent(self) -> Agent: + return Agent( + name="EmailAgent", + role="Email Processing Specialist", + description="Expert at fetching emails, processing PDFs, and updating spreadsheets.", + goal="Process recent emails, extract data from PDFs, and update spreadsheets.", + backstory="An agent specialized in email processing and data extraction.", + verbose=True + ) + + @crew + def crew(self) -> Crew: + """Creates the email processing crew""" + return Crew( + agents=[self.email_agent()], + tasks=[self.process_emails()], + process=Process.sequential, + verbose=True + ) \ No newline at end of file diff --git a/pitchdeck_analyzer/src/main.py b/pitchdeck_analyzer/src/main.py new file mode 100644 index 0000000..2ba0ed0 --- /dev/null +++ b/pitchdeck_analyzer/src/main.py @@ -0,0 +1,58 @@ +#!/usr/bin/env python + +import sys +from crew import EmailFetcherCrew +import agentstack +import agentops + +agentops.init(default_tags=agentstack.get_tags()) + +instance = EmailFetcherCrew().crew() + +def run(): + """ + Run the agent. + """ + instance.kickoff(inputs=agentstack.get_inputs()) + + +def train(): + """ + Train the crew for a given number of iterations. + """ + try: + instance.train( + n_iterations=int(sys.argv[1]), + filename=sys.argv[2], + inputs=agentstack.get_inputs(), + ) + except Exception as e: + raise Exception(f"An error occurred while training the crew: {e}") + + +def replay(): + """ + Replay the crew execution from a specific task. + """ + try: + instance.replay(task_id=sys.argv[1]) + except Exception as e: + raise Exception(f"An error occurred while replaying the crew: {e}") + + +def test(): + """ + Test the crew execution and returns the results. + """ + try: + instance.test( + n_iterations=int(sys.argv[1]), + openai_model_name=sys.argv[2], + inputs=agentstack.get_inputs(), + ) + except Exception as e: + raise Exception(f"An error occurred while replaying the crew: {e}") + + +if __name__ == '__main__': + run() \ No newline at end of file diff --git a/pitchdeck_analyzer/src/tools/__init__.py b/pitchdeck_analyzer/src/tools/__init__.py new file mode 100644 index 0000000..3feeed1 --- /dev/null +++ b/pitchdeck_analyzer/src/tools/__init__.py @@ -0,0 +1,6 @@ +from .fetch_emails import get_message_content +from .pdf_reader import upload_pdf, ask_question +from .write_to_docs import append_row + +# Export the functions directly +__all__ = ['get_message_content', 'upload_pdf', 'ask_question', 'append_row'] \ No newline at end of file diff --git a/pitchdeck_analyzer/src/tools/fetch_emails.py b/pitchdeck_analyzer/src/tools/fetch_emails.py new file mode 100644 index 0000000..7512581 --- /dev/null +++ b/pitchdeck_analyzer/src/tools/fetch_emails.py @@ -0,0 +1,107 @@ +import os.path +import base64 +from email.mime.text import MIMEText + +from google.auth.transport.requests import Request +from google.oauth2.credentials import Credentials +from google_auth_oauthlib.flow import InstalledAppFlow +from googleapiclient.discovery import build +from googleapiclient.errors import HttpError + +# If modifying these scopes, delete the file token.json. +SCOPES = [ + "https://www.googleapis.com/auth/gmail.readonly", + "https://www.googleapis.com/auth/gmail.modify" +] + +# Get the directory where this script is located +SCRIPT_DIR = os.path.dirname(os.path.abspath(__file__)) +# Path to credentials relative to script location +CREDENTIALS_PATH = os.path.join(os.path.dirname(SCRIPT_DIR), "credentials.json") +TOKEN_PATH = os.path.join(os.path.dirname(SCRIPT_DIR), "token.json") + +def get_message_content(service, msg_id): + """Get the content of a specific message.""" + try: + message = service.users().messages().get(userId='me', id=msg_id, format='full').execute() + + # Get headers + headers = message['payload']['headers'] + subject = next((header['value'] for header in headers if header['name'].lower() == 'subject'), 'No Subject') + from_email = next((header['value'] for header in headers if header['name'].lower() == 'from'), 'No Sender') + + # Get message body + if 'parts' in message['payload']: + parts = message['payload']['parts'] + body = '' + has_pdf = False + + for part in parts: + if part['mimeType'] == 'text/plain': + if 'data' in part['body']: + body = base64.urlsafe_b64decode(part['body']['data']).decode('utf-8') + elif part['mimeType'] == 'application/pdf': + has_pdf = True + # Check for PDF in attachments + if 'filename' in part and part['filename'].lower().endswith('.pdf'): + has_pdf = True + else: + # Handle messages with no parts + if 'body' in message['payload'] and 'data' in message['payload']['body']: + body = base64.urlsafe_b64decode(message['payload']['body']['data']).decode('utf-8') + else: + body = 'No content' + has_pdf = False + + return { + 'subject': subject, + 'from': from_email, + 'body': body, + 'has_pdf': has_pdf + } + except Exception as e: + print(f"Error processing message {msg_id}: {str(e)}") + return None + +def main(): + """Fetch last 5 emails and their content.""" + creds = None + if os.path.exists(TOKEN_PATH): + creds = Credentials.from_authorized_user_file(TOKEN_PATH, SCOPES) + if not creds or not creds.valid: + if creds and creds.expired and creds.refresh_token: + creds.refresh(Request()) + else: + flow = InstalledAppFlow.from_client_secrets_file( + CREDENTIALS_PATH, SCOPES + ) + creds = flow.run_local_server(port=0) + with open(TOKEN_PATH, "w") as token: + token.write(creds.to_json()) + + try: + service = build("gmail", "v1", credentials=creds) + + # Get list of messages + results = service.users().messages().list(userId='me', maxResults=5).execute() + messages = results.get('messages', []) + + if not messages: + print("No messages found.") + return + + print("\nFetching last 5 emails:\n") + for message in messages: + msg_data = get_message_content(service, message['id']) + if msg_data: + print(f"\nSubject: {msg_data['subject']}") + print(f"From: {msg_data['from']}") + print(f"Has PDF Attachment: {'Yes' if msg_data['has_pdf'] else 'No'}") + print(f"Content Preview: {msg_data['body'][:200]}...") + print("-" * 80) + + except HttpError as error: + print(f"An error occurred: {error}") + +if __name__ == "__main__": + main() \ No newline at end of file diff --git a/pitchdeck_analyzer/src/tools/pdf_reader.py b/pitchdeck_analyzer/src/tools/pdf_reader.py new file mode 100644 index 0000000..680fe69 --- /dev/null +++ b/pitchdeck_analyzer/src/tools/pdf_reader.py @@ -0,0 +1,112 @@ +import os +import requests +from dotenv import load_dotenv + +# Load environment variables from .env file +load_dotenv() + +def upload_pdf(api_key: str, pdf_path: str): + """Upload a PDF file and return the source ID.""" + if not os.path.exists(pdf_path): + print(f"Error: File not found at {pdf_path}") + return None + + try: + with open(pdf_path, 'rb') as file: + files = {'file': ('file.pdf', file, 'application/octet-stream')} + + response = requests.post( + 'https://api.chatpdf.com/v1/sources/add-file', + headers={'x-api-key': api_key}, + files=files + ) + + if response.status_code == 200: + data = response.json() + source_id = data.get('sourceId') + if source_id: + print('Source ID:', source_id) + return source_id + else: + print('Error: Missing "sourceId" in response') + print('Response:', response.text) + else: + print('Status:', response.status_code) + print('Error:', response.text) + except requests.exceptions.RequestException as e: + print(f"Request Error: {str(e)}") + except Exception as e: + print(f"Unexpected Error: {str(e)}") + + return None + + +def ask_question(api_key: str, source_id: str, question: str) -> str: + """Ask a single question about the PDF.""" + headers = { + 'x-api-key': api_key, + 'Content-Type': 'application/json' + } + + # Format the question to request concise answers + formatted_question = f"Provide a direct, concise answer. If the information is not found in the document, respond with 'None'. {question}" + + data = { + 'sourceId': source_id, + 'messages': [{'role': 'user', 'content': formatted_question}] + } + + try: + response = requests.post( + 'https://api.chatpdf.com/v1/chats/message', + headers=headers, + json=data + ) + + if response.status_code == 200: + json_response = response.json() + answer = json_response.get('content', '').strip() + # If the answer indicates no information was found, return None + if any(phrase in answer.lower() for phrase in [ + "cannot find", "no information", "not mentioned", + "not specified", "not found", "not provided" + ]): + return "None" + return answer + else: + print('Status:', response.status_code) + print('Error:', response.text) + return "None" + except requests.exceptions.RequestException as e: + print(f"Request Error: {str(e)}") + return "None" + except Exception as e: + print(f"Unexpected Error: {str(e)}") + return "None" + + +def main(): + # Use the ChatPDF API key from the environment + API_KEY = os.getenv("CHATPDF_API_KEY") + if not API_KEY: + print("ChatPDF API key not found. Please set the CHATPDF_API_KEY in your .env file.") + return + + pdf_path = os.path.abspath("src/your_pdf.pdf") + + source_id = upload_pdf(API_KEY, pdf_path) + if not source_id: + print("Failed to upload PDF. Exiting.") + return + + questions = ["What is Elon Musk most known for?", "What are the key achievements of Elon Musk?"] + for question in questions: + result = ask_question(API_KEY, source_id, question) + if result: + print('Result:', result) + else: + print(f"Failed to get a response from ChatPDF for the question: {question}") + + +if __name__ == "__main__": + main() diff --git a/pitchdeck_analyzer/src/tools/write_to_docs.py b/pitchdeck_analyzer/src/tools/write_to_docs.py new file mode 100644 index 0000000..1d35ba1 --- /dev/null +++ b/pitchdeck_analyzer/src/tools/write_to_docs.py @@ -0,0 +1,170 @@ +from google.oauth2.credentials import Credentials +from google_auth_oauthlib.flow import InstalledAppFlow +from google.auth.transport.requests import Request +from googleapiclient.discovery import build +import os.path +from typing import Optional, List, Union + +# If modifying these scopes, delete the token.json file. +SCOPES = [ + 'https://www.googleapis.com/auth/spreadsheets', + 'https://www.googleapis.com/auth/gmail.readonly' +] + +# Column structure matching the spreadsheet +COLUMNS = [ + 'Founder Name 1', + 'Founder Name 2', + 'Founder Name 3', + 'Company Name', + 'Summary of Idea', + 'Industry/Sector', + 'Ask', + 'Valuation', + 'Previous Rounds', + 'Revenue/Traction', + 'Email' +] + +def get_google_sheets_service(): + """Gets Google Sheets service with appropriate credentials.""" + creds = None + # The file token.json stores the user's access and refresh tokens + token_path = "src/token.json" + + if os.path.exists(token_path): + try: + os.remove(token_path) # Remove existing token to force new authentication + print("Removed existing token to refresh permissions.") + except: + pass + + if os.path.exists(token_path): + creds = Credentials.from_authorized_user_file(token_path, SCOPES) + + # If there are no (valid) credentials available, let the user log in. + if not creds or not creds.valid: + if creds and creds.expired and creds.refresh_token: + creds.refresh(Request()) + else: + flow = InstalledAppFlow.from_client_secrets_file( + 'src/tools/credentials.json', SCOPES) + creds = flow.run_local_server(port=0) + # Save the credentials for the next run + with open(token_path, 'w') as token: + token.write(creds.to_json()) + + try: + service = build('sheets', 'v4', credentials=creds) + return service + except Exception as e: + print(f"Error building Google Sheets service: {str(e)}") + return None + +def append_row( + founder1: Optional[str] = None, + founder2: Optional[str] = None, + founder3: Optional[str] = None, + company_name: Optional[str] = None, + idea_summary: Optional[str] = None, + industry: Optional[str] = None, + ask: Optional[str] = None, + valuation: Optional[str] = None, + previous_rounds: Optional[str] = None, + revenue_traction: Optional[str] = None, + email: Optional[str] = None +) -> bool: + """ + Appends a row to the specified spreadsheet with the given values. + Any None values will be added as empty cells. + + Args: + founder1 (str, optional): Name of first founder + founder2 (str, optional): Name of second founder + founder3 (str, optional): Name of third founder + company_name (str, optional): Name of the company + idea_summary (str, optional): Summary of the business idea + industry (str, optional): Industry/Sector + ask (str, optional): Ask amount + valuation (str, optional): Company valuation + previous_rounds (str, optional): Previous funding rounds + revenue_traction (str, optional): Revenue or traction metrics + email (str, optional): Contact email + + Returns: + bool: True if successful, False otherwise + """ + try: + SPREADSHEET_ID = '1oOMFd-gijhkDmeYeVRSlvoQaQf5geolLV-N7MdIdYfY' + RANGE_NAME = 'Sheet1!A:K' # A to K for 11 columns + + service = get_google_sheets_service() + if not service: + print("Failed to get Google Sheets service") + return False + + # Create row data with empty strings for None values + row_data = [ + founder1 or '', + founder2 or '', + founder3 or '', + company_name or '', + idea_summary or '', + industry or '', + ask or '', + valuation or '', + previous_rounds or '', + revenue_traction or '', + email or '' + ] + + # Prepare the data for appending + values = [row_data] + body = { + 'values': values + } + + try: + # Append the row + result = service.spreadsheets().values().append( + spreadsheetId=SPREADSHEET_ID, + range=RANGE_NAME, + valueInputOption='USER_ENTERED', + insertDataOption='INSERT_ROWS', + body=body + ).execute() + + updated_rows = result.get('updates', {}).get('updatedRows', 0) + print(f"Row appended successfully: {updated_rows} rows added.") + return True + except Exception as e: + print(f"Error appending to spreadsheet: {str(e)}") + return False + + except Exception as e: + print(f"Error in append_row: {str(e)}") + return False + +def main(): + # Example usage with some null values + success = append_row( + founder1="John Doe", + founder2="Jane Smith", + # founder3 is None/empty + company_name="Tech Startup Inc.", + idea_summary="AI-powered analytics platform", + industry="Technology", + ask="$2M", + valuation="$10M", + # previous_rounds is None/empty + revenue_traction="$100K ARR", + email="contact@techstartup.com" + ) + + if success: + print("Row added successfully!") + else: + print("Failed to add row.") + +if __name__ == '__main__': + main() From 576a84e0e47a30f83ff1a634662c990554471bb3 Mon Sep 17 00:00:00 2001 From: Sri Laasya Nutheti Date: Thu, 30 Jan 2025 12:29:02 -0800 Subject: [PATCH 2/2] Update prompts, spreadsheet columns, add example configuration, env and token files --- pitchdeck_analyzer/.env.example | 4 +-- pitchdeck_analyzer/src/crew.py | 18 ++++++------- pitchdeck_analyzer/src/token.json.example | 14 +++++++++++ .../src/tools/credentials.json.example | 13 ++++++++++ pitchdeck_analyzer/src/tools/write_to_docs.py | 25 ++++--------------- 5 files changed, 42 insertions(+), 32 deletions(-) create mode 100644 pitchdeck_analyzer/src/token.json.example create mode 100644 pitchdeck_analyzer/src/tools/credentials.json.example diff --git a/pitchdeck_analyzer/.env.example b/pitchdeck_analyzer/.env.example index f27c511..355bd1b 100644 --- a/pitchdeck_analyzer/.env.example +++ b/pitchdeck_analyzer/.env.example @@ -8,5 +8,5 @@ CHATPDF_API_KEY=your_chatpdf_api_key_here OPENAI_API_KEY=your_openai_api_key_here -# Google Sheets Configuration -GOOGLE_SHEETS_ID=your_google_sheets_id_here \ No newline at end of file +# Google Sheets Configuration (Can also be added to the configuration.json file) +# GOOGLE_SHEETS_ID=your_google_sheets_id_here \ No newline at end of file diff --git a/pitchdeck_analyzer/src/crew.py b/pitchdeck_analyzer/src/crew.py index edff27a..a0eba30 100644 --- a/pitchdeck_analyzer/src/crew.py +++ b/pitchdeck_analyzer/src/crew.py @@ -122,15 +122,14 @@ def process_pdf(self, pdf_path: str): print("PDF uploaded successfully. Extracting information...") questions = [ - "Who are the founders of this company?", + "Who is the main team behind this company? Could be CTO, CEO, Co-Founder, Founder, Investor, etc. Definitely find upto 1 person. Only respond with name and title", + "Who is the second main team behind this company? Could be CTO, CEO, Co-Founder, Founder, Investor, etc. Definitely find upto 1 person. Only respond with name and title", + "Who is the third main team behind this company? Could be CTO, CEO, Co-Founder, Founder, Investor, etc. Definitely find upto 1 person. Only respond with name and title", "What is the company name?", "What is the main business idea or summary?", "What industry or sector is this company in?", "How much funding are they asking for (the ask amount)?", - "What is the company's current valuation?", - "What previous funding rounds have they had?", - "What is their current revenue or traction?", - "What is the contact email mentioned in the document?" + "Who are the competitors of this company and their details? What are other similar companies in the space and how are they doing? What are their names, revenue, valuation, etc?", ] results = {} @@ -146,15 +145,14 @@ def process_pdf(self, pdf_path: str): print("\nUpdating spreadsheet with extracted information...") success = append_row( - founder1=results.get("Who are the founders/team of this company?", ""), + founder1=results.get("Who is the main team behind this company? Could be CTO, CEO, Co-Founder, Founder, Investor, etc. Definitely find upto 1 person. Only respond with name and title", ""), + founder2=results.get("Who is the second main team behind this company? Could be CTO, CEO, Co-Founder, Founder, Investor, etc. Definitely find upto 1 person. Only respond with name and title", ""), + founder3=results.get("Who is the third main team behind this company? Could be CTO, CEO, Co-Founder, Founder, Investor, etc. Definitely find upto 1 person. Only respond with name and title", ""), company_name=results.get("What is the company name?", ""), idea_summary=results.get("What is the main business idea or summary?", ""), industry=results.get("What industry or sector is this company in?", ""), ask=results.get("How much funding are they asking for (the ask amount)?", ""), - valuation=results.get("What is the company's current valuation?", ""), - previous_rounds=results.get("What previous funding rounds have they had?", ""), - revenue_traction=results.get("What is their current revenue or traction?", ""), - email=results.get("What is the contact email mentioned in the document?", "") + competitors=results.get("Who are the competitors of this company?", ""), ) if success: diff --git a/pitchdeck_analyzer/src/token.json.example b/pitchdeck_analyzer/src/token.json.example new file mode 100644 index 0000000..83b6acf --- /dev/null +++ b/pitchdeck_analyzer/src/token.json.example @@ -0,0 +1,14 @@ +{ + "token":..., + "refresh_token":..., + "token_uri":..., + "client_id":..., + "client_secret":..., + "scopes": [ + "https://www.googleapis.com/auth/spreadsheets", + "https://www.googleapis.com/auth/gmail.readonly", + ], + "universe_domain": "googleapis.com", + "account": "", + "expiry":... +} \ No newline at end of file diff --git a/pitchdeck_analyzer/src/tools/credentials.json.example b/pitchdeck_analyzer/src/tools/credentials.json.example new file mode 100644 index 0000000..7f50b9e --- /dev/null +++ b/pitchdeck_analyzer/src/tools/credentials.json.example @@ -0,0 +1,13 @@ +{ + "installed": { + "client_id":..., + "project_id":..., + "auth_uri":..., + "token_uri":..., + "auth_provider_x509_cert_url":..., + "client_secret":..., + "redirect_uris": [ + "http://localhost" + ] + } +} \ No newline at end of file diff --git a/pitchdeck_analyzer/src/tools/write_to_docs.py b/pitchdeck_analyzer/src/tools/write_to_docs.py index 1d35ba1..c52e3bf 100644 --- a/pitchdeck_analyzer/src/tools/write_to_docs.py +++ b/pitchdeck_analyzer/src/tools/write_to_docs.py @@ -20,10 +20,7 @@ 'Summary of Idea', 'Industry/Sector', 'Ask', - 'Valuation', - 'Previous Rounds', - 'Revenue/Traction', - 'Email' + 'Competitors' ] def get_google_sheets_service(): @@ -69,10 +66,7 @@ def append_row( idea_summary: Optional[str] = None, industry: Optional[str] = None, ask: Optional[str] = None, - valuation: Optional[str] = None, - previous_rounds: Optional[str] = None, - revenue_traction: Optional[str] = None, - email: Optional[str] = None + competitors: Optional[str] = None, ) -> bool: """ Appends a row to the specified spreadsheet with the given values. @@ -86,10 +80,7 @@ def append_row( idea_summary (str, optional): Summary of the business idea industry (str, optional): Industry/Sector ask (str, optional): Ask amount - valuation (str, optional): Company valuation - previous_rounds (str, optional): Previous funding rounds - revenue_traction (str, optional): Revenue or traction metrics - email (str, optional): Contact email + competitors (str, optional): Competitors and their details Returns: bool: True if successful, False otherwise @@ -112,10 +103,7 @@ def append_row( idea_summary or '', industry or '', ask or '', - valuation or '', - previous_rounds or '', - revenue_traction or '', - email or '' + competitors or '', ] # Prepare the data for appending @@ -155,10 +143,7 @@ def main(): idea_summary="AI-powered analytics platform", industry="Technology", ask="$2M", - valuation="$10M", - # previous_rounds is None/empty - revenue_traction="$100K ARR", - email="contact@techstartup.com" + competitors="Competitor 1, Competitor 2, Competitor 3" ) if success: