diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000..5bbea42 --- /dev/null +++ b/.dockerignore @@ -0,0 +1,8 @@ +Dockerfile +README.md +*.pyc +*.pyo +*.pyd +__pycache__ +.pytest_cache +.env \ No newline at end of file diff --git a/.gcloudignore b/.gcloudignore new file mode 100644 index 0000000..0076c83 --- /dev/null +++ b/.gcloudignore @@ -0,0 +1,22 @@ +# This file specifies files that are *not* uploaded to Google Cloud +# using gcloud. It follows the same syntax as .gitignore, with the addition of +# "#!include" directives (which insert the entries of the given .gitignore-style +# file at that point). +# +# For more information, run: +# $ gcloud topic gcloudignore +# +.gcloudignore +# If you would like to upload your .git directory, .gitignore file or files +# from your .gitignore file, remove the corresponding line +# below: +.git +.gitignore + +# Python pycache: +__pycache__/ +# Ignored by the build system +/setup.cfg +env/ +.env +service_account_key.json \ No newline at end of file diff --git a/.gitignore b/.gitignore index 40b878d..b90401f 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,16 @@ -node_modules/ \ No newline at end of file +node_modules/ +*.pyc +__pycache__/ +instance/ +.db +service_account_key.json +semantic_retrieval.py +node_modules +knowledgebase.py +env/ +venv +.env +Lib +Scripts +data/ +AQA.py \ No newline at end of file diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..2090f93 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,22 @@ +# Dockerfile +FROM python:3.9.17-bookworm +# Allow statements and log messages to immediately appear in the logs +ENV PYTHONUNBUFFERED True +# Copy local code to the container image. +ENV APP_HOME /back-end +WORKDIR $APP_HOME +COPY . ./ + +RUN apt-get update && apt-get install -y \ + libzbar0 \ + && rm -rf /var/lib/apt/lists/* + +RUN pip install --no-cache-dir --upgrade pip +RUN pip install --no-cache-dir -r requirements.txt + +# Run the web service on container startup. Here we use the gunicorn +# webserver, with one worker process and 8 threads. +# For environments with multiple CPU cores, increase the number of workers +# to be equal to the cores available. +# Timeout is set to 0 to disable the timeouts of the workers to allow Cloud Run to handle instance scaling. +CMD exec gunicorn --bind :$PORT --workers 1 --threads 8 --timeout 0 app:app \ No newline at end of file diff --git a/app.py b/app.py new file mode 100644 index 0000000..2cf90ff --- /dev/null +++ b/app.py @@ -0,0 +1,11 @@ +from flask import Flask +import os +from .routes import main +# this file is to run the app, it calls the blueprint of main from init, which calls from route. All this is done so it is modular. + +app = Flask(__name__) +app.secret_key = os.getenv('FLASK_SECRET_KEY', 'your_default_secret_key') +app.register_blueprint(main) + +if __name__ == "__main__": + app.run(debug=True) # run the app after it's created diff --git a/app.yaml b/app.yaml new file mode 100644 index 0000000..7bd4857 --- /dev/null +++ b/app.yaml @@ -0,0 +1,9 @@ +runtime: python39 + +entrypoint: gunicorn -b :$PORT app:app + +handlers: +- url: /static + static_dir: appp/static +- url: /.* + script: auto \ No newline at end of file diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..72621a1 Binary files /dev/null and b/requirements.txt differ diff --git a/routes.py b/routes.py new file mode 100644 index 0000000..21a8dd9 --- /dev/null +++ b/routes.py @@ -0,0 +1,109 @@ +import os +import json +from datetime import datetime +from flask import Flask, request, jsonify, Blueprint +import google.generativeai as genai +from google.ai.generativelanguage_v1beta.types import content + +main = Blueprint('main', __name__) + +#gemini +GENAI_API_KEY = os.getenv("GEMINI_API_KEY") +if not GENAI_API_KEY: + raise ValueError("GEMINI_API_KEY is not set in the environment variables.") + +genai.configure(api_key=GENAI_API_KEY) + +def send_prompt_to_gemini(prompt): + """ + Sends a prompt to the Gemini API and returns the structured JSON response. + """ + try: + generation_config = { + "temperature": 1, + "top_p": 0.95, + "top_k": 40, + "max_output_tokens": 8192, + "response_schema": content.Schema( + type=content.Type.OBJECT, + properties={ + "title": content.Schema(type=content.Type.STRING), + "timestamp_start": content.Schema(type=content.Type.STRING), + "timestamp_end": content.Schema(type=content.Type.STRING), + "location": content.Schema(type=content.Type.STRING), + "description": content.Schema(type=content.Type.STRING), + }, + ), + "response_mime_type": "application/json", + } + + model = genai.GenerativeModel( + model_name="gemini-1.5-flash-8b", + generation_config=generation_config, + ) + + chat_session = model.start_chat(history=[]) + response = chat_session.send_message(prompt) + + return response.to_dict() + except Exception as e: + return {"error": str(e)} + +@main.route("/process_text", methods=["POST"]) +def process_text(): + """ + Endpoint to process the selected text from the Chrome extension. + """ + try: + data = request.json + selected_text = data.get("selected_text", "") + if not selected_text: + return jsonify({"error": "selected_text is required"}), 400 + + today = datetime.now().strftime("%B %d, %Y") + prompt = f""" + I will give you some text that I want you to parse. The text should describe an event. + Please provide a raw JSON response with the following fields: + + title: the title of the event. + timestamp_start: a UTC timestamp of when the event starts in the format YYYYMMDDTHHMMSS. If no year is given, default to the upcoming instance of that date. + timestamp_end: a UTC timestamp of when the event ends in the format YYYYMMDDTHHMMSS. (If not given, default to one hour after start.) + location: the location of the event. + description: a short 2-3 sentence description of the event containing any pertinent information or links. + + Please parse the following text: + Today's date is {today}. {selected_text} + """ + + gemini_response = send_prompt_to_gemini(prompt) + + if "error" in gemini_response: + return jsonify(gemini_response), 500 + + # parse that jawn + title = gemini_response.get("title", "Untitled Event") + timestamp_start = gemini_response.get("timestamp_start") + timestamp_end = gemini_response.get("timestamp_end") + location = gemini_response.get("location", "") + description = gemini_response.get("description", "No description provided.") + + if not timestamp_start: + return jsonify({"error": "timestamp_start is missing from the response."}), 400 + + gcal_link = ( + f"https://www.google.com/calendar/render?action=TEMPLATE&text={title}" \ + f"&dates={timestamp_start}/{timestamp_end or ''}" \ + f"&details={description}" \ + f"&location={location}" + ) + + return jsonify({ + "title": title, + "timestamp_start": timestamp_start, + "timestamp_end": timestamp_end, + "location": location, + "description": description, + "gcal_link": gcal_link + }) + except Exception as e: + return jsonify({"error": str(e)}), 500 \ No newline at end of file