Spaces:
Runtime error
Runtime error
feat(qa): :sparkles: normal qa (#9)
Browse files- .github/workflows/test.yml +2 -2
- README.md +21 -0
- edu_assistant/__init__.py +0 -0
- edu_assistant/learning_cases/__init__.py +0 -0
- edu_assistant/learning_cases/base.py +0 -0
- edu_assistant/learning_cases/context.py +0 -0
- edu_assistant/learning_tasks/__init__.py +3 -0
- edu_assistant/learning_tasks/base.py +2 -0
- edu_assistant/learning_tasks/qa.py +116 -0
- edu_assistant/utils/__init__.py +0 -0
- edu_assistant/utils/common_utils.py +66 -0
- edu_assistant/utils/langchain_utils.py +48 -0
- examples/qa.py +29 -0
- poetry.lock +1 -1
- tests/unit_tests/learning_tasks/test_qa.py +53 -0
.github/workflows/test.yml
CHANGED
|
@@ -27,11 +27,11 @@ jobs:
|
|
| 27 |
poetry-version: '1.5.1'
|
| 28 |
|
| 29 |
- name: Install Dependencies
|
| 30 |
-
run: poetry install
|
| 31 |
|
| 32 |
- name: Run Test
|
| 33 |
run:
|
| 34 |
-
poetry run pytest --junitxml=pytest.xml --cov-report=term-missing:skip-covered --cov=
|
| 35 |
|
| 36 |
- name: Pytest coverage comment
|
| 37 |
id: coverageComment
|
|
|
|
| 27 |
poetry-version: '1.5.1'
|
| 28 |
|
| 29 |
- name: Install Dependencies
|
| 30 |
+
run: poetry install --with dev
|
| 31 |
|
| 32 |
- name: Run Test
|
| 33 |
run:
|
| 34 |
+
poetry run pytest --junitxml=pytest.xml --cov-report=term-missing:skip-covered --cov=edu_assistant tests/ | tee pytest-coverage.txt
|
| 35 |
|
| 36 |
- name: Pytest coverage comment
|
| 37 |
id: coverageComment
|
README.md
CHANGED
|
@@ -3,3 +3,24 @@
|
|
| 3 |
[](https://github.com/codedog-ai/edu-assistant/actions/workflows/flake8.yml)
|
| 4 |
[](https://github.com/codedog-ai/edu-assistant/actions/workflows/test.yml)
|
| 5 |
[](https://github.com/codedog-ai/edu-assistant/actions/workflows/test.yml)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 3 |
[](https://github.com/codedog-ai/edu-assistant/actions/workflows/flake8.yml)
|
| 4 |
[](https://github.com/codedog-ai/edu-assistant/actions/workflows/test.yml)
|
| 5 |
[](https://github.com/codedog-ai/edu-assistant/actions/workflows/test.yml)
|
| 6 |
+
|
| 7 |
+
## config
|
| 8 |
+
|
| 9 |
+
| Environment Variable | Necessary | Default | Description |
|
| 10 |
+
|---|---|---|---|
|
| 11 |
+
| CODEDOG_SERVER | No | 0.0.0.0 | Server address |
|
| 12 |
+
| CODEDOG_PORT | No | 32167 | Server port |
|
| 13 |
+
| CODEDOG_WORKER_NUM | No | 1 | Server worker number |
|
| 14 |
+
| OPENAI_API_KEY | Yes | | Api Key for calling openai api |
|
| 15 |
+
| OPENAI_PROXY | No | | openai proxy |
|
| 16 |
+
| AZURE_OPENAI | No | | use azure openai if not blank |
|
| 17 |
+
| AZURE_OPENAI_API_KEY | No | | azure openai api key |
|
| 18 |
+
| AZURE_OPENAI_API_BASE | No | | azure openai api base |
|
| 19 |
+
| AZURE_OPENAI_DEPLOYMENT_ID | No | | azure openai deployment id for gpt 3.5 |
|
| 20 |
+
| AZURE_OPENAI_EMBEDDING_DEP_ID | No | | azure openai deployment id for embedding |
|
| 21 |
+
|
| 22 |
+
## setup
|
| 23 |
+
|
| 24 |
+
- install python 3.10+
|
| 25 |
+
- install poetry 1.5.1+
|
| 26 |
+
- run: `poetry install`
|
edu_assistant/__init__.py
ADDED
|
File without changes
|
edu_assistant/learning_cases/__init__.py
ADDED
|
File without changes
|
edu_assistant/learning_cases/base.py
ADDED
|
File without changes
|
edu_assistant/learning_cases/context.py
ADDED
|
File without changes
|
edu_assistant/learning_tasks/__init__.py
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from .qa import QaTask
|
| 2 |
+
|
| 3 |
+
__all__ = ["QaTask"]
|
edu_assistant/learning_tasks/base.py
ADDED
|
@@ -0,0 +1,2 @@
|
|
|
|
|
|
|
|
|
|
| 1 |
+
class BaseTask:
|
| 2 |
+
pass
|
edu_assistant/learning_tasks/qa.py
ADDED
|
@@ -0,0 +1,116 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import random
|
| 2 |
+
import sys
|
| 3 |
+
|
| 4 |
+
from langchain import PromptTemplate
|
| 5 |
+
from langchain.chains import ConversationalRetrievalChain, ConversationChain
|
| 6 |
+
from langchain.chains.base import Chain
|
| 7 |
+
from langchain.memory import ConversationBufferMemory
|
| 8 |
+
from langchain.schema import BaseRetriever
|
| 9 |
+
|
| 10 |
+
from edu_assistant.learning_tasks.base import BaseTask
|
| 11 |
+
from edu_assistant.utils.langchain_utils import load_llm, update_chat_memory
|
| 12 |
+
|
| 13 |
+
TEMPLATE = """{instruction}
|
| 14 |
+
The following is a friendly conversation between a human and an AI.
|
| 15 |
+
The AI is talkative and provides lots of specific details from its context.
|
| 16 |
+
If the AI does not know the answer to a question, it truthfully says it does not know.
|
| 17 |
+
|
| 18 |
+
Current conversation:
|
| 19 |
+
{{history}}
|
| 20 |
+
Human: {{input}}
|
| 21 |
+
AI:"""
|
| 22 |
+
|
| 23 |
+
|
| 24 |
+
class QaTask(BaseTask):
|
| 25 |
+
_qa: Chain
|
| 26 |
+
_session_store: dict
|
| 27 |
+
|
| 28 |
+
def __init__(
|
| 29 |
+
self,
|
| 30 |
+
instruction: str = "",
|
| 31 |
+
knowledge: BaseRetriever = None,
|
| 32 |
+
session_store: dict[int, ConversationBufferMemory] = None,
|
| 33 |
+
):
|
| 34 |
+
"""Create a new QaTask service.
|
| 35 |
+
|
| 36 |
+
Args:
|
| 37 |
+
instruction (str, optional): Instruction for this task ai. Defaults to "".
|
| 38 |
+
knowledge (BaseRetriever, optional): Answer question with this knowledge retriever.
|
| 39 |
+
If not set, will not use knowledge to answer question.
|
| 40 |
+
Defaults to None.
|
| 41 |
+
session_store (dict, optional): External chat history store. Defaults to None.
|
| 42 |
+
If not set, will use internal memory to store chat history. Which will be lost after restart and might
|
| 43 |
+
cost huge memory.
|
| 44 |
+
"""
|
| 45 |
+
self._prompt = TEMPLATE.format(instruction=instruction)
|
| 46 |
+
self._qa = self._build_chain(knowledge)
|
| 47 |
+
self._session_store = {} if not session_store else session_store
|
| 48 |
+
|
| 49 |
+
def _build_chain(self, knowledge):
|
| 50 |
+
if not knowledge:
|
| 51 |
+
return ConversationChain(llm=load_llm(), prompt=PromptTemplate.from_template(self._prompt))
|
| 52 |
+
else:
|
| 53 |
+
return ConversationalRetrievalChain.from_llm(
|
| 54 |
+
llm=load_llm(),
|
| 55 |
+
retriever=knowledge,
|
| 56 |
+
condense_question_llm=load_llm(),
|
| 57 |
+
return_source_documents=True,
|
| 58 |
+
combine_docs_chain_kwargs={"prompt": PromptTemplate.from_template(self._prompt)},
|
| 59 |
+
)
|
| 60 |
+
|
| 61 |
+
def ask(self, question: str, session: bool = True, session_id: int = None, session_mem: list | None = None) -> dict:
|
| 62 |
+
"""ask a question with chat history.
|
| 63 |
+
|
| 64 |
+
Args:
|
| 65 |
+
question (str): question to llm.
|
| 66 |
+
session (bool, optional): whether use and store chat history. Defaults to False.
|
| 67 |
+
if session_id is not set, a new session will be created.
|
| 68 |
+
session_id (int, optional): specify a history session. Defaults to None.
|
| 69 |
+
session_mem (list | None, optional): specify chat history. Defaults to None.
|
| 70 |
+
if session_id is also set, chat history will be fully replaced.
|
| 71 |
+
|
| 72 |
+
Returns:
|
| 73 |
+
dict: question answer and metadata.
|
| 74 |
+
contains answer.
|
| 75 |
+
contains session_id if session is True.
|
| 76 |
+
"""
|
| 77 |
+
|
| 78 |
+
if session:
|
| 79 |
+
session_id = session_id or self._create_session_id()
|
| 80 |
+
memory = session_mem or self._get_session_mem(session_id)
|
| 81 |
+
|
| 82 |
+
result = self._ask(question, memory)
|
| 83 |
+
|
| 84 |
+
update_chat_memory(memory, question, result["response"])
|
| 85 |
+
self._update_session_mem(session_id, memory)
|
| 86 |
+
else:
|
| 87 |
+
result = self._ask(question)
|
| 88 |
+
|
| 89 |
+
if session_id:
|
| 90 |
+
result["session_id"] = session_id
|
| 91 |
+
|
| 92 |
+
return result
|
| 93 |
+
|
| 94 |
+
def _ask(self, question, memory: ConversationBufferMemory = None) -> dict:
|
| 95 |
+
if memory is None:
|
| 96 |
+
return self._qa({"input": question, "history": ""})
|
| 97 |
+
else:
|
| 98 |
+
return self._qa({"input": question, "history": memory.chat_memory})
|
| 99 |
+
|
| 100 |
+
def _get_session_mem(self, session_id):
|
| 101 |
+
if session_id not in self._session_store:
|
| 102 |
+
memory = self._init_memory(session_id)
|
| 103 |
+
else:
|
| 104 |
+
memory = self._session_store.get(session_id)
|
| 105 |
+
|
| 106 |
+
return memory
|
| 107 |
+
|
| 108 |
+
def _update_session_mem(self, session_id, memory):
|
| 109 |
+
self._session_store[session_id] = memory
|
| 110 |
+
|
| 111 |
+
def _create_session_id(self):
|
| 112 |
+
return random.randint(1, sys.maxsize)
|
| 113 |
+
|
| 114 |
+
def _init_memory(self, session_id) -> ConversationBufferMemory:
|
| 115 |
+
# TODO: redis memory store
|
| 116 |
+
return ConversationBufferMemory(memory_key="chat_history", return_messages=True, output_key="answer")
|
edu_assistant/utils/__init__.py
ADDED
|
File without changes
|
edu_assistant/utils/common_utils.py
ADDED
|
@@ -0,0 +1,66 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""
|
| 2 |
+
utility functions for codedog
|
| 3 |
+
"""
|
| 4 |
+
import hashlib
|
| 5 |
+
import logging
|
| 6 |
+
import time
|
| 7 |
+
from logging.config import dictConfig
|
| 8 |
+
|
| 9 |
+
# -- Logging ------------------------------------------------------------------
|
| 10 |
+
|
| 11 |
+
|
| 12 |
+
def init_local_logging(level=logging.INFO):
|
| 13 |
+
"""setup logging interface for local debugging"""
|
| 14 |
+
dictConfig(get_logging_config(level))
|
| 15 |
+
|
| 16 |
+
|
| 17 |
+
def get_logging_config(level=logging.INFO):
|
| 18 |
+
return {
|
| 19 |
+
"version": 1,
|
| 20 |
+
"disable_existing_loggers": False,
|
| 21 |
+
"formatters": {
|
| 22 |
+
"plain": {
|
| 23 |
+
"format": "[%(asctime)s][%(levelname)s][%(pathname)s:%(lineno)d][%(name)s]%(message)s",
|
| 24 |
+
"datefmt": "%Y-%m-%d %H:%M:%S",
|
| 25 |
+
},
|
| 26 |
+
},
|
| 27 |
+
"handlers": {
|
| 28 |
+
"console_handler": {
|
| 29 |
+
"level": "DEBUG",
|
| 30 |
+
"formatter": "plain",
|
| 31 |
+
"class": "logging.StreamHandler",
|
| 32 |
+
"stream": "ext://sys.stdout", # Default is stderr
|
| 33 |
+
},
|
| 34 |
+
},
|
| 35 |
+
"loggers": {
|
| 36 |
+
"": {
|
| 37 |
+
"level": level,
|
| 38 |
+
"handlers": ["console_handler"],
|
| 39 |
+
},
|
| 40 |
+
},
|
| 41 |
+
}
|
| 42 |
+
|
| 43 |
+
|
| 44 |
+
# -- exception ----------------------------------------------------------------
|
| 45 |
+
|
| 46 |
+
|
| 47 |
+
class CodedogError(Exception):
|
| 48 |
+
def __init__(self, message: str = None, code: int = -1):
|
| 49 |
+
self.message = "" if not message else str(message)
|
| 50 |
+
self.code = code
|
| 51 |
+
|
| 52 |
+
|
| 53 |
+
# -- utility ------------------------------------------------------------------
|
| 54 |
+
|
| 55 |
+
|
| 56 |
+
def get_ttl_hash(seconds=3600):
|
| 57 |
+
"""Return the same value withing `seconds` time period"""
|
| 58 |
+
return round(time.time() / seconds)
|
| 59 |
+
|
| 60 |
+
|
| 61 |
+
def get_sha256(text: str) -> str:
|
| 62 |
+
return hashlib.sha256(text.encode("utf-8", errors="ignore")).hexdigest()
|
| 63 |
+
|
| 64 |
+
|
| 65 |
+
def get_sha1(text: str) -> str:
|
| 66 |
+
return hashlib.sha1(text.encode("utf-8", errors="ignore")).hexdigest()
|
edu_assistant/utils/langchain_utils.py
ADDED
|
@@ -0,0 +1,48 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import os
|
| 2 |
+
|
| 3 |
+
from langchain.chat_models import AzureChatOpenAI, ChatOpenAI
|
| 4 |
+
from langchain.chat_models.base import BaseChatModel
|
| 5 |
+
from langchain.memory.chat_memory import BaseChatMemory
|
| 6 |
+
|
| 7 |
+
|
| 8 |
+
def load_llm() -> BaseChatModel:
|
| 9 |
+
if os.environ.get("AZURE_OPENAI"):
|
| 10 |
+
llm = AzureChatOpenAI(
|
| 11 |
+
openai_api_type="azure",
|
| 12 |
+
openai_api_key=os.environ.get("AZURE_OPENAI_API_KEY"),
|
| 13 |
+
openai_api_base=os.environ.get("AZURE_OPENAI_API_BASE"),
|
| 14 |
+
openai_api_version="2023-05-15",
|
| 15 |
+
deployment_name=os.environ.get("AZURE_OPENAI_DEPLOYMENT_ID", "gpt-35-turbo"),
|
| 16 |
+
model="gpt-3.5-turbo",
|
| 17 |
+
temperature=0,
|
| 18 |
+
)
|
| 19 |
+
else:
|
| 20 |
+
llm = ChatOpenAI(
|
| 21 |
+
openai_api_key=os.environ.get("OPENAI_API_KEY"),
|
| 22 |
+
openai_proxy=os.environ.get("OPENAI_PROXY", ""),
|
| 23 |
+
model="gpt-3.5-turbo",
|
| 24 |
+
)
|
| 25 |
+
|
| 26 |
+
return llm
|
| 27 |
+
|
| 28 |
+
|
| 29 |
+
def load_gpt4_llm() -> BaseChatModel:
|
| 30 |
+
llm = ChatOpenAI(
|
| 31 |
+
openai_api_key=os.environ.get("OPENAI_API_KEY"),
|
| 32 |
+
openai_proxy=os.environ.get("OPENAI_PROXY", ""),
|
| 33 |
+
model="gpt-4",
|
| 34 |
+
)
|
| 35 |
+
|
| 36 |
+
return llm
|
| 37 |
+
|
| 38 |
+
|
| 39 |
+
def update_chat_memory(memory: BaseChatMemory, user_text: str, ai_text: str):
|
| 40 |
+
"""update langchain memory
|
| 41 |
+
|
| 42 |
+
Args:
|
| 43 |
+
memory (BaseChatMemory): chat history memory
|
| 44 |
+
question (str): qa question str
|
| 45 |
+
result (str): chain answer result
|
| 46 |
+
"""
|
| 47 |
+
memory.chat_memory.add_user_message(user_text)
|
| 48 |
+
memory.chat_memory.add_ai_message(ai_text)
|
examples/qa.py
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import json
|
| 2 |
+
|
| 3 |
+
from edu_assistant.learning_tasks import QaTask
|
| 4 |
+
from edu_assistant.utils.common_utils import init_local_logging
|
| 5 |
+
|
| 6 |
+
init_local_logging()
|
| 7 |
+
|
| 8 |
+
|
| 9 |
+
instruction = """
|
| 10 |
+
Act as a c++ professional to answer student aged 5-10 questions. Answer properly and politely.
|
| 11 |
+
"""
|
| 12 |
+
task = QaTask(instruction=instruction)
|
| 13 |
+
|
| 14 |
+
|
| 15 |
+
def qa_once():
|
| 16 |
+
result = task.ask("请问什么是指针?", session=False)
|
| 17 |
+
print(json.dumps(result, ensure_ascii=False, indent=4))
|
| 18 |
+
|
| 19 |
+
|
| 20 |
+
def qa_twice():
|
| 21 |
+
task = QaTask()
|
| 22 |
+
task.ask("请问如何释放一个数组?")
|
| 23 |
+
result = task.ask("那指针呢?")
|
| 24 |
+
print(json.dumps(result, ensure_ascii=False, indent=4))
|
| 25 |
+
|
| 26 |
+
|
| 27 |
+
if __name__ == "__main__":
|
| 28 |
+
# qa_once()
|
| 29 |
+
qa_twice()
|
poetry.lock
CHANGED
|
@@ -2517,4 +2517,4 @@ reference = "aliyun"
|
|
| 2517 |
[metadata]
|
| 2518 |
lock-version = "2.0"
|
| 2519 |
python-versions = "^3.10"
|
| 2520 |
-
content-hash = "
|
|
|
|
| 2517 |
[metadata]
|
| 2518 |
lock-version = "2.0"
|
| 2519 |
python-versions = "^3.10"
|
| 2520 |
+
content-hash = "bf7411574c4cfde57b9017ce88a88019ca51f05c24b086bb4477e2ff85201b51"
|
tests/unit_tests/learning_tasks/test_qa.py
ADDED
|
@@ -0,0 +1,53 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import unittest
|
| 2 |
+
from unittest.mock import MagicMock, patch
|
| 3 |
+
|
| 4 |
+
from langchain.memory import ConversationBufferMemory
|
| 5 |
+
|
| 6 |
+
from edu_assistant.learning_tasks import QaTask
|
| 7 |
+
|
| 8 |
+
|
| 9 |
+
class TestQaTask(unittest.TestCase):
|
| 10 |
+
@patch.object(QaTask, "_build_chain")
|
| 11 |
+
def test_init(self, mock_build_chain):
|
| 12 |
+
mock_build_chain.return_value = MagicMock()
|
| 13 |
+
QaTask()
|
| 14 |
+
mock_build_chain.assert_called_once()
|
| 15 |
+
|
| 16 |
+
@patch.object(QaTask, "_build_chain")
|
| 17 |
+
def test_ask_with_session(self, mock_build_chain):
|
| 18 |
+
mock_build_chain.return_value = MagicMock()
|
| 19 |
+
qa_task = QaTask()
|
| 20 |
+
qa_task._ask = MagicMock(return_value={"response": "test answer"})
|
| 21 |
+
result = qa_task.ask("test question")
|
| 22 |
+
qa_task._ask.assert_called_once()
|
| 23 |
+
self.assertEqual(result.get("response"), "test answer")
|
| 24 |
+
self.assertIsNotNone(result.get("session_id"))
|
| 25 |
+
|
| 26 |
+
@patch.object(QaTask, "_build_chain")
|
| 27 |
+
def test_ask_without_session(self, mock_build_chain):
|
| 28 |
+
mock_build_chain.return_value = MagicMock()
|
| 29 |
+
qa_task = QaTask()
|
| 30 |
+
qa_task._ask = MagicMock(return_value={"answer": "test answer"})
|
| 31 |
+
result = qa_task.ask("test question", session=False)
|
| 32 |
+
qa_task._ask.assert_called_once_with("test question")
|
| 33 |
+
self.assertEqual(result, {"answer": "test answer"})
|
| 34 |
+
|
| 35 |
+
@patch.object(QaTask, "_build_chain")
|
| 36 |
+
def test__get_session_mem(self, mock_build_chain):
|
| 37 |
+
mock_build_chain.return_value = MagicMock()
|
| 38 |
+
memory = ConversationBufferMemory()
|
| 39 |
+
qa_task = QaTask(session_store={1: memory})
|
| 40 |
+
result = qa_task._get_session_mem(1)
|
| 41 |
+
self.assertEqual(result, memory)
|
| 42 |
+
|
| 43 |
+
@patch.object(QaTask, "_build_chain")
|
| 44 |
+
def test__update_session_mem(self, mock_build_chain):
|
| 45 |
+
mock_build_chain.return_value = MagicMock()
|
| 46 |
+
memory = ConversationBufferMemory()
|
| 47 |
+
qa_task = QaTask()
|
| 48 |
+
qa_task._update_session_mem(1, memory)
|
| 49 |
+
self.assertEqual(qa_task._session_store[1], memory)
|
| 50 |
+
|
| 51 |
+
|
| 52 |
+
if __name__ == "__main__":
|
| 53 |
+
unittest.main()
|