Improve behavior of simple reasoning (#157)

* Add base reasoning implementation

* Provide explicit async and streaming capability

* Allow refreshing the information panel
This commit is contained in:
Duc Nguyen (john)
2024-03-12 13:03:38 +07:00
committed by GitHub
parent cb01d27d19
commit 2950e6ed02
7 changed files with 174 additions and 28 deletions

View File

@@ -209,7 +209,10 @@ class ChatPage(BasePage):
if "output" in response:
text += response["output"]
if "evidence" in response:
refs += response["evidence"]
if response["evidence"] is None:
refs = ""
else:
refs += response["evidence"]
if len(refs) > len_ref:
print(f"Len refs: {len(refs)}")

View File

@@ -1,5 +1,49 @@
from typing import Optional
from kotaemon.base import BaseComponent
class BaseReasoning(BaseComponent):
retrievers: list = []
"""The reasoning pipeline that handles each of the user chat messages
This reasoning pipeline has access to:
- the retrievers
- the user settings
- the message
- the conversation id
- the message history
"""
@classmethod
def get_info(cls) -> dict:
"""Get the pipeline information for the app to organize and display
Returns:
a dictionary that contains the following keys:
- "id": the unique id of the pipeline
- "name": the human-friendly name of the pipeline
- "description": the overview short description of the pipeline, for
user to grasp what does the pipeline do
"""
raise NotImplementedError
@classmethod
def get_user_settings(cls) -> dict:
"""Get the default user settings for this pipeline"""
return {}
@classmethod
def get_pipeline(
cls, user_settings: dict, retrievers: Optional[list["BaseComponent"]] = None
) -> "BaseReasoning":
"""Get the reasoning pipeline for the app to execute
Args:
user_setting: user settings
retrievers (list): List of retrievers
"""
return cls()
def run(self, message: str, conv_id: str, history: list, **kwargs): # type: ignore
"""Execute the reasoning pipeline"""
raise NotImplementedError

View File

@@ -200,22 +200,24 @@ class AnswerWithContextPipeline(BaseComponent):
lang=self.lang,
)
citation_task = asyncio.create_task(
self.citation_pipeline.ainvoke(context=evidence, question=question)
)
print("Citation task created")
messages = []
if self.system_prompt:
messages.append(SystemMessage(content=self.system_prompt))
messages.append(HumanMessage(content=prompt))
output = ""
for text in self.llm(messages):
for text in self.llm.stream(messages):
output += text.text
self.report_output({"output": text.text})
await asyncio.sleep(0)
try:
citation = self.citation_pipeline(context=evidence, question=question)
except Exception as e:
print(e)
citation = None
# retrieve the citation
print("Waiting for citation task")
citation = await citation_task
answer = Document(text=output, metadata={"citation": citation})
return answer
@@ -242,6 +244,19 @@ class FullQAPipeline(BaseReasoning):
if doc.doc_id not in doc_ids:
docs.append(doc)
doc_ids.append(doc.doc_id)
for doc in docs:
self.report_output(
{
"evidence": (
"<details open>"
f"<summary>{doc.metadata['file_name']}</summary>"
f"{doc.text}"
"</details><br>"
)
}
)
await asyncio.sleep(0.1)
evidence_mode, evidence = self.evidence_pipeline(docs).content
answer = await self.answering_pipeline(
question=message, evidence=evidence, evidence_mode=evidence_mode
@@ -266,6 +281,7 @@ class FullQAPipeline(BaseReasoning):
id2docs = {doc.doc_id: doc for doc in docs}
lack_evidence = True
not_detected = set(id2docs.keys()) - set(spans.keys())
self.report_output({"evidence": None})
for id, ss in spans.items():
if not ss:
not_detected.add(id)
@@ -282,7 +298,7 @@ class FullQAPipeline(BaseReasoning):
self.report_output(
{
"evidence": (
"<details>"
"<details open>"
f"<summary>{id2docs[id].metadata['file_name']}</summary>"
f"{text}"
"</details><br>"