From 12773f89306cef47f96c4340490c2c62237aff6f Mon Sep 17 00:00:00 2001 From: omkarmamid Date: Sat, 11 Jan 2025 18:46:16 -0500 Subject: [PATCH 1/2] gemini python endpoint --- .dockerignore | 8 ++++ .gcloudignore | 22 ++++++++++ .gitignore | 16 +++++++- Dockerfile | 22 ++++++++++ app.py | 11 +++++ app.yaml | 9 +++++ routes.py | 109 ++++++++++++++++++++++++++++++++++++++++++++++++++ 7 files changed, 196 insertions(+), 1 deletion(-) create mode 100644 .dockerignore create mode 100644 .gcloudignore create mode 100644 Dockerfile create mode 100644 app.py create mode 100644 app.yaml create mode 100644 routes.py 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..97e4c95 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,15 @@ -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/ +.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/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 From 331533a81c989c1b86c8e3fc9de4bb0f219cdb95 Mon Sep 17 00:00:00 2001 From: omkarmamid Date: Sat, 11 Jan 2025 18:54:54 -0500 Subject: [PATCH 2/2] requirements.txt --- .gitignore | 1 + requirements.txt | Bin 0 -> 1488 bytes 2 files changed, 1 insertion(+) create mode 100644 requirements.txt diff --git a/.gitignore b/.gitignore index 97e4c95..b90401f 100644 --- a/.gitignore +++ b/.gitignore @@ -8,6 +8,7 @@ semantic_retrieval.py node_modules knowledgebase.py env/ +venv .env Lib Scripts diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000000000000000000000000000000000000..72621a13c518ab3675ccf622ffc66b23f63c5e93 GIT binary patch literal 1488 zcmaKsL66f=41|3~;-{qArrW|ExN<<^#DQa#P1_LYCLv7_@Z*7R?7Swd3X0Mf)%JKi zw%_kxg*7~_4OZ|T?O++_59|28*c+ZZTiGk#J9}v_?9t9v6Y&Lm@AfsehpW8=r?gL4 z2T)tQ17(Yc-asimdI0Ift1LN|-0tyMbL#ir^9idU)5Q@p=i1v7|B1)XD~k;=5iORb zTT569`?ce(IKw7ZEtrMlq#6~ybC0*!3o=HukX2(aue_=Xk0VMIaLBCUyKyGcu(oe} z3!%X>a>|~v>>TYjR_AD?bBhQm5vu$zqN85}I<;iXrrO15@T{Tc)>XKTGJ14$VP%^| zx1U!Vo*B6Yo{lPOCL+I9r(Cyk+)_x|B~{d+jcdQmSdgKn52~r9S5H%g9lg`Uw$p5> zJ3F2^cTZ$VZ0i}qKF?@He6nX?#O#lMq0Qd^u63@qW^Wogt$Jc!LWLUa3$~aAZA{BV z#70DK6X$oZZR|6ip=O%8t*i2$|9j?fg!c!&Pt!!nOZ%C-*Q`s1-}#zJ_2-N2oBbZ~ zIXP?XX6}0&eb7v42O4HSbtz_7JG8X!y)Orlv=j54F>j;1Lj!F`>3A`f;vN+~_t|^T z`>D>*O1Ds|R5w)I3(~D}OsNsL;;7iKuo5SAVV-aKM3uT%qHALN;3^J89jHnEaVIG6 zda@~RWZZ-N9b|3V#b-@tii%q-*p6_~Bpg9iU*e{nJrlb8&OF+w&4O>5k&hr~3Uup@ Ki<;HnsOJwWMc)em literal 0 HcmV?d00001