From 0574ed4a24502f36c78317cf61bedaa8995e9dc2 Mon Sep 17 00:00:00 2001 From: Young-Ho Kim Date: Wed, 13 Mar 2024 17:51:30 +0900 Subject: [PATCH] Implementing python processors. --- .editorconfig | 3 +- .gitignore | 5 +- apps/backend/poetry.lock | 16 ++++--- apps/backend/tests/test_hello.py | 13 +++++- libs/py_core/poetry.lock | 31 +++++++------ libs/py_core/py_core/system/__init__.py | 0 libs/py_core/py_core/system/model.py | 47 +++++++++++++++++++ libs/py_core/py_core/system/processor.py | 37 +++++++++++++++ libs/py_core/pyproject.toml | 3 +- libs/py_core/test.py | 15 ++++++ libs/py_core/tests/test_hello.py | 3 ++ package-lock.json | 59 +++++++++++++++++++----- 12 files changed, 194 insertions(+), 38 deletions(-) create mode 100644 libs/py_core/py_core/system/__init__.py create mode 100644 libs/py_core/py_core/system/model.py create mode 100644 libs/py_core/py_core/system/processor.py create mode 100644 libs/py_core/test.py diff --git a/.editorconfig b/.editorconfig index 6e87a00..c4c9ee7 100644 --- a/.editorconfig +++ b/.editorconfig @@ -4,7 +4,8 @@ root = true [*] charset = utf-8 indent_style = space -indent_size = 2 +indent_size = + insert_final_newline = true trim_trailing_whitespace = true diff --git a/.gitignore b/.gitignore index 98a6e38..087d355 100644 --- a/.gitignore +++ b/.gitignore @@ -38,4 +38,7 @@ testem.log .DS_Store Thumbs.db -.nx/cache \ No newline at end of file +.nx/cache + +.cache +unittests/ diff --git a/apps/backend/poetry.lock b/apps/backend/poetry.lock index d557a0c..7f2b101 100644 --- a/apps/backend/poetry.lock +++ b/apps/backend/poetry.lock @@ -331,7 +331,7 @@ files = [ [[package]] name = "chatlib" -version = "0.6.8" +version = "0.7.0" description = "A generalizable Python Chatbot Library" optional = false python-versions = "^3.10" @@ -360,8 +360,8 @@ yaspin = "^2.3.0" [package.source] type = "git" url = "https://github.com/yghokim/chatlib.git" -reference = "0.6.8" -resolved_reference = "9b15067cecca7e43da12ec546ec3da0dfb11709c" +reference = "0.7.0" +resolved_reference = "55c77ec41967d2471cb3fa324d3b366f5c52bdf3" [[package]] name = "cohere" @@ -1413,6 +1413,7 @@ description = "Nvidia JIT LTO Library" optional = false python-versions = ">=3" files = [ + {file = "nvidia_nvjitlink_cu12-12.4.99-py3-none-manylinux2014_aarch64.whl", hash = "sha256:75d6498c96d9adb9435f2bbdbddb479805ddfb97b5c1b32395c694185c20ca57"}, {file = "nvidia_nvjitlink_cu12-12.4.99-py3-none-manylinux2014_x86_64.whl", hash = "sha256:c6428836d20fe7e327191c175791d38570e10762edc588fb46749217cd444c74"}, {file = "nvidia_nvjitlink_cu12-12.4.99-py3-none-win_amd64.whl", hash = "sha256:991905ffa2144cb603d8ca7962d75c35334ae82bf92820b6ba78157277da1ad2"}, ] @@ -1648,8 +1649,9 @@ files = [] develop = true [package.dependencies] -chatlib = {git = "https://github.com/yghokim/chatlib.git", tag = "0.6.8"} +chatlib = {git = "https://github.com/yghokim/chatlib.git", tag = "0.7.0"} pendulum = "^3.0.0" +pydantic = "^2.6.4" [package.source] type = "directory" @@ -1693,13 +1695,13 @@ files = [ [[package]] name = "pydantic" -version = "2.6.3" +version = "2.6.4" description = "Data validation using Python type hints" optional = false python-versions = ">=3.8" files = [ - {file = "pydantic-2.6.3-py3-none-any.whl", hash = "sha256:72c6034df47f46ccdf81869fddb81aade68056003900a8724a4f160700016a2a"}, - {file = "pydantic-2.6.3.tar.gz", hash = "sha256:e07805c4c7f5c6826e33a1d4c9d47950d7eaf34868e2690f8594d2e30241f11f"}, + {file = "pydantic-2.6.4-py3-none-any.whl", hash = "sha256:cc46fce86607580867bdc3361ad462bab9c222ef042d3da86f2fb333e1d916c5"}, + {file = "pydantic-2.6.4.tar.gz", hash = "sha256:b1704e0847db01817624a6b86766967f552dd9dbf3afba4004409f908dcc84e6"}, ] [package.dependencies] diff --git a/apps/backend/tests/test_hello.py b/apps/backend/tests/test_hello.py index e337217..21878c9 100644 --- a/apps/backend/tests/test_hello.py +++ b/apps/backend/tests/test_hello.py @@ -1,7 +1,18 @@ """Hello unit test module.""" - +import pytest from libs.py_core.py_core.hello import hello +from libs.py_core.py_core.system.processor import ChildCardRecommendationGenerator + def test_hello(): """Test the hello function.""" assert hello() == "Hello aacesstalk-backend" + + +@pytest.mark.asyncio +async def test_processor(): + child_card_recommender = ChildCardRecommendationGenerator() + + card_recommendation_result = await child_card_recommender.generate() + + print(card_recommendation_result) diff --git a/libs/py_core/poetry.lock b/libs/py_core/poetry.lock index 13c8fef..ba2cb0d 100644 --- a/libs/py_core/poetry.lock +++ b/libs/py_core/poetry.lock @@ -331,7 +331,7 @@ files = [ [[package]] name = "chatlib" -version = "0.6.8" +version = "0.7.0" description = "A generalizable Python Chatbot Library" optional = false python-versions = "^3.10" @@ -360,18 +360,18 @@ yaspin = "^2.3.0" [package.source] type = "git" url = "https://github.com/yghokim/chatlib.git" -reference = "0.6.8" -resolved_reference = "9b15067cecca7e43da12ec546ec3da0dfb11709c" +reference = "0.7.0" +resolved_reference = "55c77ec41967d2471cb3fa324d3b366f5c52bdf3" [[package]] name = "cohere" -version = "4.53" +version = "4.54" description = "Python SDK for the Cohere API" optional = false python-versions = ">=3.8,<4.0" files = [ - {file = "cohere-4.53-py3-none-any.whl", hash = "sha256:ecee9ac80f87b66b85237cdea23c1de8a3d9980c0cd0bf205145b821c4c52c96"}, - {file = "cohere-4.53.tar.gz", hash = "sha256:292afe75d0a1d3e5a201bd26f0610980e252d02aca6e589b8b319081890b0754"}, + {file = "cohere-4.54-py3-none-any.whl", hash = "sha256:6f83bd2530d461f91dfea828c1a7162b37cf03bdad4d801a218681064821f167"}, + {file = "cohere-4.54.tar.gz", hash = "sha256:c4bd84f0d766575430db91262e3ba0f4b655e40fec8a08fde98652e49a18608d"}, ] [package.dependencies] @@ -1413,6 +1413,7 @@ description = "Nvidia JIT LTO Library" optional = false python-versions = ">=3" files = [ + {file = "nvidia_nvjitlink_cu12-12.4.99-py3-none-manylinux2014_aarch64.whl", hash = "sha256:75d6498c96d9adb9435f2bbdbddb479805ddfb97b5c1b32395c694185c20ca57"}, {file = "nvidia_nvjitlink_cu12-12.4.99-py3-none-manylinux2014_x86_64.whl", hash = "sha256:c6428836d20fe7e327191c175791d38570e10762edc588fb46749217cd444c74"}, {file = "nvidia_nvjitlink_cu12-12.4.99-py3-none-win_amd64.whl", hash = "sha256:991905ffa2144cb603d8ca7962d75c35334ae82bf92820b6ba78157277da1ad2"}, ] @@ -1676,13 +1677,13 @@ files = [ [[package]] name = "pydantic" -version = "2.6.3" +version = "2.6.4" description = "Data validation using Python type hints" optional = false python-versions = ">=3.8" files = [ - {file = "pydantic-2.6.3-py3-none-any.whl", hash = "sha256:72c6034df47f46ccdf81869fddb81aade68056003900a8724a4f160700016a2a"}, - {file = "pydantic-2.6.3.tar.gz", hash = "sha256:e07805c4c7f5c6826e33a1d4c9d47950d7eaf34868e2690f8594d2e30241f11f"}, + {file = "pydantic-2.6.4-py3-none-any.whl", hash = "sha256:cc46fce86607580867bdc3361ad462bab9c222ef042d3da86f2fb333e1d916c5"}, + {file = "pydantic-2.6.4.tar.gz", hash = "sha256:b1704e0847db01817624a6b86766967f552dd9dbf3afba4004409f908dcc84e6"}, ] [package.dependencies] @@ -2815,20 +2816,20 @@ termcolor = ">=2.3,<3.0" [[package]] name = "zipp" -version = "3.17.0" +version = "3.18.0" description = "Backport of pathlib-compatible object wrapper for zip files" optional = false python-versions = ">=3.8" files = [ - {file = "zipp-3.17.0-py3-none-any.whl", hash = "sha256:0e923e726174922dce09c53c59ad483ff7bbb8e572e00c7f7c46b88556409f31"}, - {file = "zipp-3.17.0.tar.gz", hash = "sha256:84e64a1c28cf7e91ed2078bb8cc8c259cb19b76942096c8d7b84947690cabaf0"}, + {file = "zipp-3.18.0-py3-none-any.whl", hash = "sha256:c1bb803ed69d2cce2373152797064f7e79bc43f0a3748eb494096a867e0ebf79"}, + {file = "zipp-3.18.0.tar.gz", hash = "sha256:df8d042b02765029a09b157efd8e820451045890acc30f8e37dd2f94a060221f"}, ] [package.extras] -docs = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (<7.2.5)", "sphinx (>=3.5)", "sphinx-lint"] -testing = ["big-O", "jaraco.functools", "jaraco.itertools", "more-itertools", "pytest (>=6)", "pytest-black (>=0.3.7)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=2.2)", "pytest-ignore-flaky", "pytest-mypy (>=0.9.1)", "pytest-ruff"] +docs = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-lint"] +testing = ["big-O", "jaraco.functools", "jaraco.itertools", "more-itertools", "pytest (>=6)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=2.2)", "pytest-ignore-flaky", "pytest-mypy", "pytest-ruff (>=0.2.1)"] [metadata] lock-version = "2.0" python-versions = ">=3.11.8,<3.12" -content-hash = "cce45af251a0f07961a681410a3058544a7cd296d3067b9bb38f6e496ec5145a" +content-hash = "6fc6d9905e2b690a6b499f4040f0fb746fbb070627e5e2cffa89bab722675117" diff --git a/libs/py_core/py_core/system/__init__.py b/libs/py_core/py_core/system/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/libs/py_core/py_core/system/model.py b/libs/py_core/py_core/system/model.py new file mode 100644 index 0000000..400cd5d --- /dev/null +++ b/libs/py_core/py_core/system/model.py @@ -0,0 +1,47 @@ +from enum import StrEnum +from typing import Literal, TypeAlias +from nanoid import generate + +from pydantic import BaseModel, ConfigDict, TypeAdapter, Field + + +class ChildCardRecommendationResult(BaseModel): + model_config = ConfigDict(frozen=True) + + nouns: list[str] + emotions: list[str] + actions: list[str] + + +class ParentGuidanceElement(BaseModel): + model_config = ConfigDict(frozen=True) + + example: str + guidance: str + + +class ParentRecommendationResult(BaseModel): + model_config = ConfigDict(frozen=True) + + recommendations: list[ParentGuidanceElement] + + +class DialogueRole(StrEnum): + model_config = ConfigDict(frozen=True) + + Parent = "parent" + Child = "child" + + +class DialogueMessage(BaseModel): + model_config = ConfigDict(frozen=True) + + id: str = Field(default_factory=lambda: generate(size=20)) + speaker: Literal["parent", "child"] + content: str | list[str] + recommendation_id: str + + +Dialogue: TypeAlias = list[DialogueMessage] + +DialogueTypeAdapter = TypeAdapter(Dialogue) diff --git a/libs/py_core/py_core/system/processor.py b/libs/py_core/py_core/system/processor.py new file mode 100644 index 0000000..18b1e08 --- /dev/null +++ b/libs/py_core/py_core/system/processor.py @@ -0,0 +1,37 @@ +from typing import Any + +from chatlib.tool.versatile_mapper import ChatCompletionFewShotMapper, ChatCompletionFewShotMapperParams +from chatlib.tool.converter import generate_pydantic_converter +from chatlib.llm.integration.openai_api import GPTChatCompletionAPI, ChatGPTModel + +from libs.py_core.py_core.system.model import ChildCardRecommendationResult, Dialogue, DialogueMessage, DialogueRole + + +class ChildCardRecommendationGenerator: + + def __init__(self): + str_output_converter, output_str_converter = generate_pydantic_converter(ChildCardRecommendationResult) + + self.__mapper: ChatCompletionFewShotMapper[Dialogue, ChildCardRecommendationResult, Any] = ( + ChatCompletionFewShotMapper(GPTChatCompletionAPI(), + instruction_generator=f""" + You are a helpful assistant that serves as an Alternative Augmented Communication tool. +Suppose that you are helping a communication with a child and parents in Korean. The autistic child has the language proficiency of a 6 to 8-year-old in Korean, so recommendations should consider their cognitive level. +Given the last message of the parents, suggest a list of keywords that can help the child pick to create a sentence as an answer. +Proceed in the following order. +1. [nouns] : Provide nouns that reflect detailed context based on your parents' questions. +2. [actions] : Provide words for the action that can be matched with the nouns suggested in [nouns]. +3. [emotions] : Suggest emotions that the child might want to express in the situation, including both positive and negative emotions and needs. +Note that the output must be JSON, formatted like the following: +{ChildCardRecommendationResult.schema_json()} +The result should be in Korean and please provide up to four options for each element. + """, + input_str_converter=lambda dialogue, params: dialogue.json(), + output_str_converter=output_str_converter, + str_output_converter=str_output_converter + )) + + async def generate(self) -> ChildCardRecommendationResult: + return await self.__mapper.run(None, input=[DialogueMessage(speaker=DialogueRole.Parent, content="오늘 학교에서 뭐 했어?")], + params=ChatCompletionFewShotMapperParams(model = ChatGPTModel.GPT_4_0125, api_params={})) + diff --git a/libs/py_core/pyproject.toml b/libs/py_core/pyproject.toml index d99a1e0..ba5a80f 100644 --- a/libs/py_core/pyproject.toml +++ b/libs/py_core/pyproject.toml @@ -23,7 +23,8 @@ readme = 'README.md' [tool.poetry.dependencies] python = ">=3.11.8,<3.12" pendulum = "^3.0.0" - chatlib = {git = "https://github.com/yghokim/chatlib.git", tag="0.6.8"} + chatlib = {git = "https://github.com/yghokim/chatlib.git", tag="0.7.0"} + pydantic = "^2.6.4" [tool.poetry.group.dev.dependencies] autopep8 = "2.0.2" diff --git a/libs/py_core/test.py b/libs/py_core/test.py new file mode 100644 index 0000000..847d7be --- /dev/null +++ b/libs/py_core/test.py @@ -0,0 +1,15 @@ +from chatlib.llm.integration.openai_api import GPTChatCompletionAPI + +from libs.py_core.py_core.system.processor import ChildCardRecommendationGenerator + + +async def test_processor(): + + GPTChatCompletionAPI.authorize() + + child_card_recommender = ChildCardRecommendationGenerator() + + card_recommendation_result = await child_card_recommender.generate() + + print(card_recommendation_result) + diff --git a/libs/py_core/tests/test_hello.py b/libs/py_core/tests/test_hello.py index 2178ff9..0faa265 100644 --- a/libs/py_core/tests/test_hello.py +++ b/libs/py_core/tests/test_hello.py @@ -1,8 +1,11 @@ """Hello unit test module.""" +import pytest from libs.py_core.py_core.hello import hello +from libs.py_core.py_core.system.processor import ChildCardRecommendationGenerator def test_hello(): """Test the hello function.""" assert hello() == "Hello py-core" + diff --git a/package-lock.json b/package-lock.json index c86da0d..479ca7b 100644 --- a/package-lock.json +++ b/package-lock.json @@ -3582,6 +3582,33 @@ } } }, + "node_modules/@nx/js/node_modules/babel-plugin-macros": { + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/babel-plugin-macros/-/babel-plugin-macros-2.8.0.tgz", + "integrity": "sha512-SEP5kJpfGYqYKpBrj5XU3ahw5p5GOHJ0U5ssOSQ/WBVdwkD2Dzlce95exQTs3jOVWPPKLBN2rlEWkCK7dSmLvg==", + "dev": true, + "dependencies": { + "@babel/runtime": "^7.7.2", + "cosmiconfig": "^6.0.0", + "resolve": "^1.12.0" + } + }, + "node_modules/@nx/js/node_modules/cosmiconfig": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-6.0.0.tgz", + "integrity": "sha512-xb3ZL6+L8b9JLLCx3ZdoZy4+2ECphCMo2PwqgP1tlfVq6M6YReyzBJtvWWtbDSpNr9hn96pkCiZqUcFEc+54Qg==", + "dev": true, + "dependencies": { + "@types/parse-json": "^4.0.0", + "import-fresh": "^3.1.0", + "parse-json": "^5.0.0", + "path-type": "^4.0.0", + "yaml": "^1.7.2" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/@nx/linter": { "version": "18.0.8", "resolved": "https://registry.npmjs.org/@nx/linter/-/linter-18.0.8.tgz", @@ -6044,30 +6071,38 @@ } }, "node_modules/babel-plugin-macros": { - "version": "2.8.0", - "resolved": "https://registry.npmjs.org/babel-plugin-macros/-/babel-plugin-macros-2.8.0.tgz", - "integrity": "sha512-SEP5kJpfGYqYKpBrj5XU3ahw5p5GOHJ0U5ssOSQ/WBVdwkD2Dzlce95exQTs3jOVWPPKLBN2rlEWkCK7dSmLvg==", + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/babel-plugin-macros/-/babel-plugin-macros-3.1.0.tgz", + "integrity": "sha512-Cg7TFGpIr01vOQNODXOOaGz2NpCU5gl8x1qJFbb6hbZxR7XrcE2vtbAsTAbJ7/xwJtUuJEw8K8Zr/AE0LHlesg==", "dev": true, + "optional": true, + "peer": true, "dependencies": { - "@babel/runtime": "^7.7.2", - "cosmiconfig": "^6.0.0", - "resolve": "^1.12.0" + "@babel/runtime": "^7.12.5", + "cosmiconfig": "^7.0.0", + "resolve": "^1.19.0" + }, + "engines": { + "node": ">=10", + "npm": ">=6" } }, "node_modules/babel-plugin-macros/node_modules/cosmiconfig": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-6.0.0.tgz", - "integrity": "sha512-xb3ZL6+L8b9JLLCx3ZdoZy4+2ECphCMo2PwqgP1tlfVq6M6YReyzBJtvWWtbDSpNr9hn96pkCiZqUcFEc+54Qg==", + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-7.1.0.tgz", + "integrity": "sha512-AdmX6xUzdNASswsFtmwSt7Vj8po9IuqXm0UXz7QKPuEUmPB4XyjGfaAr2PSuELMwkRMVH1EpIkX5bTZGRB3eCA==", "dev": true, + "optional": true, + "peer": true, "dependencies": { "@types/parse-json": "^4.0.0", - "import-fresh": "^3.1.0", + "import-fresh": "^3.2.1", "parse-json": "^5.0.0", "path-type": "^4.0.0", - "yaml": "^1.7.2" + "yaml": "^1.10.0" }, "engines": { - "node": ">=8" + "node": ">=10" } }, "node_modules/babel-plugin-polyfill-corejs2": {