generated from childmindresearch/template-python-repository
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
7b5ec29
commit 19ac1ce
Showing
68 changed files
with
5,044 additions
and
2,418 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,3 +1,2 @@ | ||
local.settings.json | ||
.env | ||
.venv |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,4 +1,3 @@ | ||
.idea | ||
~* | ||
.ruff_cache | ||
.vscode | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,24 @@ | ||
services: | ||
functions: | ||
build: . | ||
ports: | ||
- 8080:80 | ||
env_file: | ||
- .env | ||
develop: | ||
watch: | ||
- action: sync+restart | ||
path: ./src/ctk_functions | ||
target: /home/site/wwwroot/src/ctk_functions | ||
- action: sync+restart | ||
path: ./function_app.py | ||
target: /home/site/wwwroot/function_app.py | ||
- action: sync+restart | ||
path: ./host.json | ||
target: /home/site/wwwroot/host.json | ||
- action: rebuild | ||
path: pyproject.toml | ||
- action: rebuild | ||
path: Dockerfile | ||
- action: rebuild | ||
path: docker-compose.yaml |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,7 +1,204 @@ | ||
"""Entrypoint for Azure Functions.""" | ||
"""Entrypoint for the Azure Functions app.""" | ||
|
||
import azure.functions as func | ||
import http | ||
import json | ||
import typing | ||
|
||
from ctk_functions import app as fastapi_app | ||
from azure import functions | ||
|
||
app = func.AsgiFunctionApp(app=fastapi_app.app, http_auth_level=func.AuthLevel.FUNCTION) | ||
from ctk_functions import config | ||
from ctk_functions.functions.file_conversion import ( | ||
controller as file_conversion_controller, | ||
) | ||
from ctk_functions.functions.intake import controller as intake_controller | ||
from ctk_functions.functions.language_tool import controller as language_tool_controller | ||
from ctk_functions.functions.llm import controller as llm_controller | ||
from ctk_functions.microservices import llm | ||
|
||
logger = config.get_logger() | ||
|
||
app = functions.FunctionApp() | ||
|
||
|
||
@app.function_name(name="llm") | ||
@app.route(route="llm", auth_level=functions.AuthLevel.FUNCTION, methods=["POST"]) | ||
async def llm_endpoint(req: functions.HttpRequest) -> functions.HttpResponse: | ||
"""Runs a large language model. | ||
The request body should contain a JSON object with the following keys: | ||
- system_prompt: The system prompt. | ||
- user_prompt: The user prompt. | ||
- model: The model name. See ctk_functions.microservices.llm.VALID_LLM_MODELS. | ||
Args: | ||
req: The HTTP request object. | ||
Returns: | ||
The HTTP response containing the output text. | ||
""" | ||
body_dict = json.loads(req.get_body().decode("utf-8")) | ||
system_prompt = body_dict.get("system_prompt") | ||
user_prompt = body_dict.get("user_prompt") | ||
model = body_dict.get("model") | ||
|
||
if not system_prompt or not user_prompt or not model: | ||
return functions.HttpResponse( | ||
"Please provide a system prompt, a user prompt, and a model name.", | ||
status_code=http.HTTPStatus.BAD_REQUEST, | ||
) | ||
if model not in typing.get_args(llm.VALID_LLM_MODELS): | ||
return functions.HttpResponse( | ||
"Invalid model, valid models are: " | ||
+ ", ".join(typing.get_args(llm.VALID_LLM_MODELS)) | ||
+ ".", | ||
status_code=http.HTTPStatus.BAD_REQUEST, | ||
) | ||
|
||
logger.info("Running LLM with model: %s", model) | ||
text = await llm_controller.run_llm(model, system_prompt, user_prompt) | ||
return functions.HttpResponse( | ||
body=text, | ||
status_code=http.HTTPStatus.OK, | ||
) | ||
|
||
|
||
@app.function_name(name="IntakeReport") | ||
@app.route( | ||
route="intake-report/{survey_id}", | ||
auth_level=functions.AuthLevel.FUNCTION, | ||
methods=["GET"], | ||
) | ||
async def get_intake_report(req: functions.HttpRequest) -> functions.HttpResponse: | ||
"""Generates an intake report for a survey. | ||
The survey ID should be provided in the URL path. The model should be provided as | ||
an "X-Model" header. See ctk_functions.microservices.llm.VALID_LLM_MODELS for valid | ||
models. | ||
Args: | ||
req: The HTTP request object. | ||
Returns: | ||
The HTTP response containing the .docx file. | ||
""" | ||
survey_id = req.route_params.get("survey_id") | ||
model = req.headers.get("X-Model") | ||
|
||
if not survey_id or not model: | ||
return functions.HttpResponse( | ||
"Please provide a survey ID and model name.", | ||
status_code=http.HTTPStatus.BAD_REQUEST, | ||
) | ||
if model not in typing.get_args(llm.VALID_LLM_MODELS): | ||
return functions.HttpResponse( | ||
"Invalid model, valid models are: " | ||
+ ", ".join(typing.get_args(llm.VALID_LLM_MODELS)) | ||
+ ".", | ||
status_code=http.HTTPStatus.BAD_REQUEST, | ||
) | ||
|
||
logger.info( | ||
"Generating intake report for identifier %s with model %s.", | ||
survey_id, | ||
model, | ||
) | ||
return await intake_controller.get_intake_report(survey_id, model) | ||
|
||
|
||
@app.function_name(name="MarkdownToDocx") | ||
@app.route( | ||
route="markdown2docx", | ||
auth_level=functions.AuthLevel.FUNCTION, | ||
methods=["POST"], | ||
) | ||
async def markdown2docx(req: functions.HttpRequest) -> functions.HttpResponse: | ||
"""Converts a Markdown document to a .docx file. | ||
The request body should contain a JSON object with the following keys: | ||
- markdown: The Markdown document. | ||
- formatting: Formatting options, must abide by cmi_docx.ParagraphStyle arguments. | ||
Args: | ||
req: The HTTP request object. | ||
Returns: | ||
The HTTP response containing the .docx file. | ||
""" | ||
body_dict = json.loads(req.get_body().decode("utf-8")) | ||
formatting = json.loads(body_dict.get("formatting", "{}")) | ||
|
||
markdown = body_dict.get("markdown", None) | ||
if not markdown: | ||
return functions.HttpResponse( | ||
"Please provide a Markdown document.", | ||
status_code=http.HTTPStatus.BAD_REQUEST, | ||
) | ||
logger.info("Converting Markdown to .docx") | ||
docx_bytes = file_conversion_controller.markdown2docx(markdown, formatting) | ||
return functions.HttpResponse( | ||
body=docx_bytes, | ||
status_code=http.HTTPStatus.OK, | ||
mimetype="application/vnd.openxmlformats-officedocument.wordprocessingml.document", | ||
) | ||
|
||
|
||
@app.function_name(name="LanguageTool") | ||
@app.route( | ||
route="language-tool", | ||
auth_level=functions.AuthLevel.FUNCTION, | ||
methods=["POST"], | ||
) | ||
async def language_tool(req: functions.HttpRequest) -> functions.HttpResponse: | ||
"""Runs the LanguageTool grammar checker. | ||
The request body should contain a JSON object with the following keys: | ||
- text: The text to correct. | ||
- rules: The rules to enable c.f. LanguageTool for a list of rules. | ||
Args: | ||
req: The HTTP request object. | ||
Returns: | ||
The HTTP response containing the output text. | ||
""" | ||
body_dict = json.loads(req.get_body().decode("utf-8")) | ||
text = body_dict.get("text") | ||
rules = json.loads(body_dict.get("rules", "[]")) | ||
|
||
if not rules: | ||
return functions.HttpResponse( | ||
"Please provide some rules.", | ||
status_code=http.HTTPStatus.BAD_REQUEST, | ||
) | ||
|
||
if not text: | ||
return functions.HttpResponse( | ||
"Please provide some text.", | ||
status_code=http.HTTPStatus.BAD_REQUEST, | ||
) | ||
|
||
logger.info("Running LanguageTool") | ||
corrected_text = language_tool_controller.language_tool(text, rules) | ||
return functions.HttpResponse( | ||
body=corrected_text, | ||
status_code=http.HTTPStatus.OK, | ||
) | ||
|
||
|
||
@app.function_name(name="Health") | ||
@app.route(route="health", auth_level=functions.AuthLevel.FUNCTION, methods=["GET"]) | ||
async def health(req: functions.HttpRequest) -> functions.HttpResponse: # noqa: ARG001 | ||
"""Health check endpoint. | ||
This endpoint takes no arguments and always returns a 200 OK response. | ||
Args: | ||
req: The HTTP request object. | ||
Returns: | ||
The HTTP response indicating the health of the app. | ||
""" | ||
return functions.HttpResponse( | ||
body="Healthy", | ||
status_code=http.HTTPStatus.OK, | ||
) |
Oops, something went wrong.