-
-
Notifications
You must be signed in to change notification settings - Fork 134
Llm flask api #1789
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Llm flask api #1789
Changes from all commits
3c2a1e5
83d0cbc
b254730
e6a6e59
641ca2e
82bb272
d2a07be
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -4,11 +4,20 @@ | |
"cleanUrls": true, | ||
"ignore": ["firebase.json", "**/.*", "**/node_modules/**"] | ||
}, | ||
"functions": { | ||
"predeploy": ["yarn build:functions"], | ||
"source": "functions", | ||
"runtime": "nodejs18" | ||
}, | ||
"functions": [ | ||
{ | ||
"predeploy": ["yarn build:functions"], | ||
"source": "functions", | ||
"codebase": "maple", | ||
"runtime": "nodejs18" | ||
}, | ||
{ | ||
"predeploy": ["source /app/llm/venv/bin/activate && python3.10 -m pip install -r /app/llm/requirements.txt"], | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. IIUC, this one should be used by docker compose and should run inside the docker enviroment (note this doesn't exactly work for reasons I don't understand) |
||
"source": "llm", | ||
"codebase": "maple-llm", | ||
"runtime": "python310" | ||
} | ||
], | ||
"firestore": { | ||
"rules": "firestore.rules", | ||
"indexes": "firestore.indexes.json" | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
venv/ | ||
__pycache__/ | ||
databases/ |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,51 @@ | ||
from flask import Flask, jsonify, abort, request | ||
from llm_functions import get_summary_api_function, get_tags_api_function | ||
import json | ||
from firebase_admin import initialize_app | ||
from firebase_functions import https_fn | ||
|
||
initialize_app() | ||
app = Flask(__name__) | ||
|
||
|
||
def is_intersection(keys, required_keys): | ||
return (keys & required_keys) == required_keys | ||
|
||
|
||
@app.route("/summary", methods=["POST"]) | ||
def summary(): | ||
body = json.loads(request.data) | ||
# We require bill_id, bill_title, bill_text to exist as keys in the POST | ||
if not is_intersection(body.keys(), {"bill_id", "bill_title", "bill_text"}): | ||
abort(404, description="requires bill_id, bill_title, and bill_text") | ||
Comment on lines
+15
to
+20
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Note: currently this API takes a json payload like {
"bill_id": "...",
"bill_title": "...",
"bill_text": "..."
} In the future, since this has access to firebase, you could also offer an API which takes the |
||
|
||
summary = get_summary_api_function( | ||
body["bill_id"], body["bill_title"], body["bill_text"] | ||
) | ||
|
||
if summary["status"] in [-1, -2]: | ||
abort(500, description="Unable to generate summary") | ||
Comment on lines
+26
to
+27
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The status field returns either -1 or -2 if it fails... This is probably somewhat unfortunate here because if this API ever changes I will not be able to know about it. I'll add a note to There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Or, maybe I should just check that this value is negative? technically the return type is |
||
|
||
return jsonify(summary["summary"]) | ||
|
||
|
||
@app.route("/tags", methods=["POST"]) | ||
def tags(): | ||
body = json.loads(request.data) | ||
# We require bill_id, bill_title, bill_text to exist as keys in the POST | ||
# Note: & is essentially set intersection | ||
if not is_intersection(body.keys(), {"bill_id", "bill_title", "bill_text"}): | ||
abort(404, description="requires bill_id, bill_title, and bill_text") | ||
|
||
tags = get_tags_api_function(body["bill_id"], body["bill_title"], body["bill_text"]) | ||
|
||
if tags["status"] in [-1, -2]: | ||
abort(500, description="Unable to generate tags") | ||
Comment on lines
+42
to
+43
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Probably check for negative return type? See above https://github.com/codeforboston/maple/pull/1789/files#r2055086855 |
||
|
||
return jsonify(tags["tags"]) | ||
|
||
|
||
@https_fn.on_request(secrets=["OPENAI_DEV"]) | ||
def httpsflaskexample(req: https_fn.Request) -> https_fn.Response: | ||
with app.request_context(req.environ): | ||
return app.full_dispatch_request() | ||
Comment on lines
+48
to
+51
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is the magic sauce to expose these routes to firebase |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -122,3 +122,21 @@ This project uses OpenAI's API for various language processing tasks. To use the | |
```python | ||
import os | ||
print(os.environ.get('OPENAI_API_KEY')) | ||
|
||
# Running the API | ||
|
||
Set up a virtual environment and run the Flask app | ||
|
||
``` | ||
python3 -m venv venv | ||
source venv/bin/activate # .fish if using fish | ||
pip3 install -r requirements.txt | ||
python3 -m flask --app main run | ||
``` | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Need to add more details here and remove everything below it |
||
|
||
TODO: | ||
|
||
- [x] Get an OpenAPI key `firebase functions:secrets:access OPENAI_DEV` | ||
- [ ] Need to get docker running locally `yarn compose up --build` | ||
- [ ] Try to deploy the function `firebase deploy --only functions:maple-llm` | ||
- [ ] Alphabetize the requirements.txt file |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,183 +1,16 @@ | ||
absl-py==2.1.0 | ||
aiohttp==3.9.5 | ||
aiosignal==1.3.1 | ||
altair==5.3.0 | ||
annotated-types==0.7.0 | ||
anyio==4.4.0 | ||
asgiref==3.8.1 | ||
asttokens==2.4.1 | ||
attrs==23.2.0 | ||
backoff==2.2.1 | ||
bcrypt==4.1.3 | ||
blinker==1.8.2 | ||
blis==0.7.11 | ||
build==1.2.1 | ||
cachetools==5.3.3 | ||
catalogue==2.0.10 | ||
certifi==2024.6.2 | ||
charset-normalizer==3.3.2 | ||
chroma-hnswlib==0.7.3 | ||
Flask==3.1.0 | ||
chromadb==0.5.0 | ||
click==8.1.7 | ||
cloudpathlib==0.18.1 | ||
coloredlogs==15.0.1 | ||
confection==0.1.5 | ||
cymem==2.0.8 | ||
dataclasses-json==0.6.6 | ||
decorator==5.1.1 | ||
Deprecated==1.2.14 | ||
distro==1.9.0 | ||
dnspython==2.6.1 | ||
email_validator==2.1.1 | ||
executing==2.0.1 | ||
fastapi==0.111.0 | ||
fastapi-cli==0.0.4 | ||
filelock==3.14.0 | ||
flatbuffers==24.3.25 | ||
frozenlist==1.4.1 | ||
fsspec==2024.6.0 | ||
git-filter-repo==2.45.0 | ||
gitdb==4.0.11 | ||
GitPython==3.1.43 | ||
google-auth==2.29.0 | ||
googleapis-common-protos==1.63.1 | ||
grpcio==1.64.1 | ||
h11==0.14.0 | ||
httpcore==1.0.5 | ||
httptools==0.6.1 | ||
httpx==0.27.0 | ||
huggingface-hub==0.25.2 | ||
humanfriendly==10.0 | ||
idna==3.7 | ||
importlib_metadata==7.1.0 | ||
importlib_resources==6.4.0 | ||
ipdb==0.13.13 | ||
ipython==8.25.0 | ||
jedi==0.19.1 | ||
Jinja2==3.1.4 | ||
joblib==1.4.2 | ||
jsonpatch==1.33 | ||
jsonpointer==2.4 | ||
jsonschema==4.22.0 | ||
jsonschema-specifications==2023.12.1 | ||
kubernetes==29.0.0 | ||
langchain==0.2.1 | ||
firebase-admin==6.7.0 | ||
firebase-functions==0.4.2 | ||
langchain-community==0.2.1 | ||
langchain-core==0.2.3 | ||
langchain-openai==0.1.8 | ||
langchain-text-splitters==0.2.0 | ||
langcodes==3.4.0 | ||
langsmith==0.1.69 | ||
language_data==1.2.0 | ||
marisa-trie==1.2.0 | ||
markdown-it-py==3.0.0 | ||
MarkupSafe==2.1.5 | ||
marshmallow==3.21.2 | ||
matplotlib-inline==0.1.7 | ||
mdurl==0.1.2 | ||
mmh3==4.1.0 | ||
monotonic==1.6 | ||
mpmath==1.3.0 | ||
multidict==6.0.5 | ||
murmurhash==1.0.10 | ||
mypy-extensions==1.0.0 | ||
networkx==3.3 | ||
nltk==3.8.1 | ||
langchain==0.2.1 | ||
numpy==1.26.4 | ||
oauthlib==3.2.2 | ||
onnxruntime==1.18.0 | ||
openai==1.31.0 | ||
opentelemetry-api==1.25.0 | ||
opentelemetry-exporter-otlp-proto-common==1.25.0 | ||
opentelemetry-exporter-otlp-proto-grpc==1.25.0 | ||
opentelemetry-instrumentation==0.46b0 | ||
opentelemetry-instrumentation-asgi==0.46b0 | ||
opentelemetry-instrumentation-fastapi==0.46b0 | ||
opentelemetry-proto==1.25.0 | ||
opentelemetry-sdk==1.25.0 | ||
opentelemetry-semantic-conventions==0.46b0 | ||
opentelemetry-util-http==0.46b0 | ||
orjson==3.10.3 | ||
overrides==7.7.0 | ||
packaging==23.2 | ||
pandas==2.2.2 | ||
parso==0.8.4 | ||
pexpect==4.9.0 | ||
pillow==10.3.0 | ||
posthog==3.5.0 | ||
preshed==3.0.9 | ||
prompt_toolkit==3.0.45 | ||
protobuf==4.25.3 | ||
ptyprocess==0.7.0 | ||
pure-eval==0.2.2 | ||
pyarrow==16.1.0 | ||
pyasn1==0.6.0 | ||
pyasn1_modules==0.4.0 | ||
pydantic==2.7.3 | ||
pydantic_core==2.18.4 | ||
pydeck==0.9.1 | ||
Pygments==2.18.0 | ||
PyPika==0.48.9 | ||
pyproject_hooks==1.1.0 | ||
python-dateutil==2.9.0.post0 | ||
python-dotenv==1.0.1 | ||
python-multipart==0.0.9 | ||
pytz==2024.1 | ||
PyYAML==6.0.1 | ||
redis==5.0.7 | ||
referencing==0.35.1 | ||
regex==2024.5.15 | ||
pytest==8.3.5 | ||
requests==2.32.3 | ||
requests-oauthlib==2.0.0 | ||
rich==13.7.1 | ||
rouge_score==0.1.2 | ||
rpds-py==0.18.1 | ||
rsa==4.9 | ||
safetensors==0.4.3 | ||
scikit-learn==1.5.0 | ||
scipy==1.13.1 | ||
sentence-transformers==3.0.0 | ||
setuptools==70.0.0 | ||
shellingham==1.5.4 | ||
six==1.16.0 | ||
smart-open==7.0.4 | ||
smmap==5.0.1 | ||
sniffio==1.3.1 | ||
spacy==3.7.5 | ||
spacy-legacy==3.0.12 | ||
spacy-loggers==1.0.5 | ||
SQLAlchemy==2.0.30 | ||
srsly==2.4.8 | ||
stack-data==0.6.3 | ||
starlette==0.37.2 | ||
streamlit==1.35.0 | ||
sympy==1.12.1 | ||
tenacity==8.3.0 | ||
thinc==8.2.5 | ||
threadpoolctl==3.5.0 | ||
tiktoken==0.7.0 | ||
tokenizers==0.20.1 | ||
toml==0.10.2 | ||
toolz==0.12.1 | ||
torch==2.4.1 | ||
tornado==6.4 | ||
tqdm==4.66.4 | ||
traitlets==5.14.3 | ||
transformers==4.45.2 | ||
typer==0.12.3 | ||
typing-inspect==0.9.0 | ||
typing_extensions==4.12.1 | ||
tzdata==2024.1 | ||
ujson==5.10.0 | ||
urllib3==2.2.1 | ||
uvicorn==0.30.1 | ||
uvloop==0.19.0 | ||
wasabi==1.1.3 | ||
watchfiles==0.22.0 | ||
wcwidth==0.2.13 | ||
weasel==0.4.1 | ||
websocket-client==1.8.0 | ||
websockets==12.0 | ||
wrapt==1.16.0 | ||
yarl==1.9.4 | ||
zipp==3.19.1 |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,27 @@ | ||
import pytest | ||
from main import app | ||
|
||
|
||
@pytest.fixture | ||
def client(): | ||
return app.test_client() | ||
|
||
|
||
def test_summary_returns_404_with_empty_body(client): | ||
response = client.post("/summary", json={}) | ||
assert response.status_code == 404 | ||
|
||
|
||
def test_summary_returns_404_with_partial_body(client): | ||
response = client.post("/summary", json={"bill_id": "bill"}) | ||
assert response.status_code == 404 | ||
|
||
|
||
def test_tags_returns_404_with_empty_body(client): | ||
response = client.post("/tags", json={}) | ||
assert response.status_code == 404 | ||
|
||
|
||
def test_tags_returns_404_with_partial_body(client): | ||
response = client.post("/tags", json={"bill_id": "bill"}) | ||
assert response.status_code == 404 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
IIUC, this file is mainly used when you type
firebase deploy ...
so this pre-deploy will not happen inside a Docker environment