-
Notifications
You must be signed in to change notification settings - Fork 6
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
persistent storage + Transformers AI (#123)
An example of a Nextcloud Talk bot that uses a very small language model, and the cache with this model is in the application's persistent storage and persists between application updates. --------- Signed-off-by: Alexander Piskun <bigcat88@icloud.com>
- Loading branch information
Showing
13 changed files
with
279 additions
and
0 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 |
---|---|---|
@@ -0,0 +1,29 @@ | ||
<component name="ProjectRunConfigurationManager"> | ||
<configuration default="false" name="TalkBotAI" type="PythonConfigurationType" factoryName="Python"> | ||
<module name="nc_py_api" /> | ||
<option name="INTERPRETER_OPTIONS" value="" /> | ||
<option name="PARENT_ENVS" value="true" /> | ||
<envs> | ||
<env name="APP_ID" value="talk_bot_ai" /> | ||
<env name="APP_PORT" value="9034" /> | ||
<env name="APP_SECRET" value="12345" /> | ||
<env name="APP_VERSION" value="1.0.0" /> | ||
<env name="NEXTCLOUD_URL" value="http://nextcloud.local" /> | ||
<env name="PYTHONUNBUFFERED" value="1" /> | ||
</envs> | ||
<option name="SDK_HOME" value="" /> | ||
<option name="WORKING_DIRECTORY" value="$PROJECT_DIR$" /> | ||
<option name="IS_MODULE_SDK" value="true" /> | ||
<option name="ADD_CONTENT_ROOTS" value="true" /> | ||
<option name="ADD_SOURCE_ROOTS" value="true" /> | ||
<EXTENSION ID="PythonCoverageRunConfigurationExtension" runner="coverage.py" /> | ||
<option name="SCRIPT_NAME" value="$PROJECT_DIR$/examples/as_app/talk_bot_AI/src/main.py" /> | ||
<option name="PARAMETERS" value="" /> | ||
<option name="SHOW_COMMAND_LINE" value="false" /> | ||
<option name="EMULATE_TERMINAL" value="false" /> | ||
<option name="MODULE_MODE" value="false" /> | ||
<option name="REDIRECT_INPUT" value="false" /> | ||
<option name="INPUT_FILE" value="" /> | ||
<method v="2" /> | ||
</configuration> | ||
</component> |
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,10 @@ | ||
FROM python:3.11-bookworm | ||
|
||
COPY requirements.txt / | ||
ADD /src/ /app/ | ||
|
||
RUN \ | ||
python3 -m pip install -r requirements.txt && rm -rf ~/.cache && rm requirements.txt | ||
|
||
WORKDIR /app | ||
ENTRYPOINT ["python3", "main.py"] |
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,28 @@ | ||
How To Install | ||
============== | ||
|
||
Currently, while AppAPI hasn't been published on the App Store, and App Store support hasn't been added yet, | ||
installation is a little bit tricky. | ||
|
||
Steps to Install: | ||
|
||
1. [Install AppAPI](https://cloud-py-api.github.io/app_api/Installation.html) | ||
2. Create a deployment daemon according to the [instructions](https://cloud-py-api.github.io/app_api/CreationOfDeployDaemon.html#create-deploy-daemon) of the AppPI | ||
3. php occ app_api:app:deploy talk_bot_ai "daemon_deploy_name" \ | ||
--info-xml https://raw.githubusercontent.com/cloud-py-api/nc_py_api/main/examples/as_app/talk_bot_ai/appinfo/info.xml | ||
|
||
to deploy a docker image with Bot to docker. | ||
|
||
4. php occ app_api:app:register talk_bot_ai "daemon_deploy_name" -e --force-scopes \ | ||
--info-xml https://raw.githubusercontent.com/cloud-py-api/nc_py_api/main/examples/as_app/talk_bot_ai/appinfo/info.xml | ||
|
||
to call its **enable** handler and accept all required API scopes by default. | ||
|
||
|
||
In a few months | ||
=============== | ||
|
||
1. Install AppAPI from App Store | ||
2. Configure Deploy Daemon with GUI provided by AppAPI | ||
3. Go to External Applications page in Nextcloud UI | ||
4. Find this bot in a list and press "Install" and "Enable" buttons, like with usual Applications. |
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,49 @@ | ||
.DEFAULT_GOAL := help | ||
|
||
.PHONY: help | ||
help: | ||
@echo "Welcome to TalkBotAI example. Please use \`make <target>\` where <target> is one of" | ||
@echo " " | ||
@echo " Next commands are only for dev environment with nextcloud-docker-dev!" | ||
@echo " They should run from the host you are developing on(with activated venv) and not in the container with Nextcloud!" | ||
@echo " " | ||
@echo " build-push build image and upload to ghcr.io" | ||
@echo " " | ||
@echo " deploy deploy example to registered 'docker_dev'" | ||
@echo " " | ||
@echo " run28 install TalkBotAI for Nextcloud 28" | ||
@echo " run27 install TalkBotAI for Nextcloud 27" | ||
@echo " " | ||
@echo " For development of this example use PyCharm run configurations. Development is always set for last Nextcloud." | ||
@echo " First run 'TalkBotAI' and then 'make manual_register', after that you can use/debug/develop it and easy test." | ||
@echo " " | ||
@echo " manual_register perform registration of running 'TalkBotAI' into the 'manual_install' deploy daemon." | ||
|
||
.PHONY: build-push | ||
build-push: | ||
docker login ghcr.io | ||
docker buildx build --push --platform linux/arm64/v8,linux/amd64 --tag ghcr.io/cloud-py-api/talk_bot_ai:latest . | ||
|
||
.PHONY: deploy | ||
deploy: | ||
docker exec master-nextcloud-1 sudo -u www-data php occ app_api:app:deploy talk_bot_ai docker_dev \ | ||
--info-xml https://raw.githubusercontent.com/cloud-py-api/nc_py_api/main/examples/as_app/talk_bot_ai/appinfo/info.xml | ||
|
||
.PHONY: run28 | ||
run28: | ||
docker exec master-nextcloud-1 sudo -u www-data php occ app_api:app:unregister talk_bot_ai --silent || true | ||
docker exec master-nextcloud-1 sudo -u www-data php occ app_api:app:register talk_bot_ai docker_dev -e --force-scopes \ | ||
--info-xml https://raw.githubusercontent.com/cloud-py-api/nc_py_api/main/examples/as_app/talk_bot_ai/appinfo/info.xml | ||
|
||
.PHONY: run27 | ||
run27: | ||
docker exec master-stable27-1 sudo -u www-data php occ app_api:app:unregister talk_bot_ai --silent || true | ||
docker exec master-stable27-1 sudo -u www-data php occ app_api:app:register talk_bot_ai docker_dev -e --force-scopes \ | ||
--info-xml https://raw.githubusercontent.com/cloud-py-api/nc_py_api/main/examples/as_app/talk_bot_ai/appinfo/info.xml | ||
|
||
.PHONY: manual_register | ||
manual_register: | ||
docker exec master-nextcloud-1 sudo -u www-data php occ app_api:app:unregister talk_bot_ai --silent || true | ||
docker exec master-nextcloud-1 sudo -u www-data php occ app_api:app:register talk_bot_ai manual_install --json-info \ | ||
"{\"appid\":\"talk_bot_ai\",\"name\":\"TalkBotAI\",\"daemon_config_name\":\"manual_install\",\"version\":\"1.0.0\",\"secret\":\"12345\",\"host\":\"host.docker.internal\",\"port\":9034,\"scopes\":{\"required\":[\"TALK\", \"TALK_BOT\"],\"optional\":[]},\"protocol\":\"http\",\"system_app\":0}" \ | ||
-e --force-scopes |
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,38 @@ | ||
<?xml version="1.0"?> | ||
<info> | ||
<id>talk_bot_ai</id> | ||
<name>TalkBotAI</name> | ||
<summary>Nextcloud TalkBotAI Example</summary> | ||
<description> | ||
<![CDATA[Example of the Nextcloud Talk Bot + LLM written in python]]> | ||
</description> | ||
<version>1.0.0</version> | ||
<licence>MIT</licence> | ||
<author mail="andrey18106x@gmail.com" homepage="https://github.com/andrey18106">Andrey Borysenko</author> | ||
<author mail="bigcat88@icloud.com" homepage="https://github.com/bigcat88">Alexander Piskun</author> | ||
<namespace>TalkBotAIExample</namespace> | ||
<category>tools</category> | ||
<website>https://github.com/cloud-py-api/nc_py_api</website> | ||
<bugs>https://github.com/cloud-py-api/nc_py_api/issues</bugs> | ||
<repository type="git">https://github.com/cloud-py-api/nc_py_api</repository> | ||
<dependencies> | ||
<nextcloud min-version="27" max-version="28"/> | ||
</dependencies> | ||
<ex-app> | ||
<docker-install> | ||
<registry>ghcr.io</registry> | ||
<image>cloud-py-api/talk_bot_ai</image> | ||
<image-tag>latest</image-tag> | ||
</docker-install> | ||
<scopes> | ||
<required> | ||
<value>TALK</value> | ||
<value>TALK_BOT</value> | ||
</required> | ||
<optional> | ||
</optional> | ||
</scopes> | ||
<protocol>http</protocol> | ||
<system>0</system> | ||
</ex-app> | ||
</info> |
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,5 @@ | ||
nc_py_api[app]>=0.2.1 | ||
transformers>=4.33 | ||
torch | ||
torchvision | ||
torchaudio |
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
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,72 @@ | ||
"""Example of an application that uses Python Transformers library with Talk Bot APIs.""" | ||
|
||
# This line should be on top before any import of the "Transformers" library. | ||
from nc_py_api.ex_app import persist_transformers_cache # noqa # isort:skip | ||
import re | ||
from threading import Thread | ||
from typing import Annotated | ||
|
||
import requests | ||
from fastapi import BackgroundTasks, Depends, FastAPI | ||
from transformers import pipeline | ||
|
||
from nc_py_api import NextcloudApp, talk_bot | ||
from nc_py_api.ex_app import run_app, set_handlers, talk_bot_app | ||
|
||
APP = FastAPI() | ||
AI_BOT = talk_bot.TalkBot("/ai_talk_bot", "AI talk bot", "Usage: `@ai What sounds do cats make?`") | ||
MODEL_NAME = "MBZUAI/LaMini-Flan-T5-77M" | ||
MODEL_INIT_THREAD = None | ||
|
||
|
||
def ai_talk_bot_process_request(message: talk_bot.TalkBotMessage): | ||
r = re.search(r"@ai\s(.*)", message.object_content["message"], re.IGNORECASE) | ||
if r is None: | ||
return | ||
model = pipeline("text2text-generation", model="MBZUAI/LaMini-Flan-T5-77M") | ||
response_text = model(r.group(1), max_length=64, do_sample=True)[0]["generated_text"] | ||
AI_BOT.send_message(response_text, message) | ||
|
||
|
||
@APP.post("/ai_talk_bot") | ||
async def ai_talk_bot( | ||
message: Annotated[talk_bot.TalkBotMessage, Depends(talk_bot_app)], | ||
background_tasks: BackgroundTasks, | ||
): | ||
if message.object_name == "message": | ||
background_tasks.add_task(ai_talk_bot_process_request, message) | ||
return requests.Response() | ||
|
||
|
||
def enabled_handler(enabled: bool, nc: NextcloudApp) -> str: | ||
print(f"enabled={enabled}") | ||
try: | ||
AI_BOT.enabled_handler(enabled, nc) | ||
except Exception as e: | ||
return str(e) | ||
return "" | ||
|
||
|
||
def download_models(): | ||
pipeline("text2text-generation", model=MODEL_NAME) | ||
|
||
|
||
def heartbeat_handler() -> str: | ||
global MODEL_INIT_THREAD | ||
print("heartbeat_handler: called") | ||
if MODEL_INIT_THREAD is None: | ||
MODEL_INIT_THREAD = Thread(target=download_models) | ||
MODEL_INIT_THREAD.start() | ||
print("heartbeat_handler: started initialization thread") | ||
r = "init" if MODEL_INIT_THREAD.is_alive() else "ok" | ||
print(f"heartbeat_handler: result={r}") | ||
return r | ||
|
||
|
||
@APP.on_event("startup") | ||
def initialization(): | ||
set_handlers(APP, enabled_handler, heartbeat_handler) | ||
|
||
|
||
if __name__ == "__main__": | ||
run_app("main:APP", log_level="trace") |
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,5 +1,6 @@ | ||
"""All possible ExApp stuff for NextcloudApp that can be used.""" | ||
from .defs import ApiScope, LogLvl | ||
from .integration_fastapi import nc_app, set_handlers, talk_bot_app | ||
from .misc import persistent_storage | ||
from .ui.files import UiActionFileInfo, UiFileActionHandlerInfo | ||
from .uvicorn_fastapi import run_app |
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,25 @@ | ||
"""Different miscellaneous optimization/helper functions for the Nextcloud Applications.""" | ||
|
||
import os | ||
from sys import platform | ||
|
||
|
||
def persistent_storage() -> str: | ||
"""Returns the path to directory, which is permanent storage and is not deleted when the application is updated.""" | ||
return os.getenv("APP_PERSISTENT_STORAGE", _get_app_cache_dir()) | ||
|
||
|
||
def _get_app_cache_dir() -> str: | ||
sys_platform = platform.lower() | ||
root_cache_path = ( | ||
os.path.normpath(os.environ["LOCALAPPDATA"]) | ||
if sys_platform == "win32" | ||
else ( | ||
os.path.expanduser("~/Library/Caches") | ||
if sys_platform == "darwin" | ||
else os.getenv("XDG_CACHE_HOME", os.path.expanduser("~/.cache")) | ||
) | ||
) | ||
r = os.path.join(root_cache_path, os.environ["APP_ID"]) | ||
os.makedirs(r, exist_ok=True) | ||
return r |
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,7 @@ | ||
"""Import this file to automatically point TRANSFORMERS_CACHE to your application's persistent storage.""" | ||
|
||
import os | ||
|
||
from .misc import persistent_storage | ||
|
||
os.environ["TRANSFORMERS_CACHE"] = persistent_storage() |
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