Skip to content

Commit 5ed9880

Browse files
authored
refactor: rephrasing chain and chat graph (#145)
Adjust the rephrasing chain prompt, increase fault tolerance and adjust chat graph connections, so that the nodes are executed sequentially. Adjust determine language node in answer graph. Its now based on llms and has as fallback langdetect and as fallback from langdetect, 'en'.
1 parent 3c01ab6 commit 5ed9880

File tree

19 files changed

+497
-57
lines changed

19 files changed

+497
-57
lines changed

libs/README.md

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -90,12 +90,14 @@ Uploaded documents are required to contain the following metadata:
9090
| composed_retriever | [`rag_core_api.retriever.retriever.Retriever`](./rag-core-api/src/rag_core_api/retriever/retriever.py) | [`rag_core_api.impl.retriever.composite_retriever.CompositeRetriever`](./rag-core-api/src/rag_core_api/impl/retriever/composite_retriever.py) | Handles retrieval, re-ranking, etc. |
9191
| large_language_model | `langchain_core.language_models.llms.BaseLLM` | `langchain_community.llms.vllm.VLLMOpenAI`, `langchain_community.llms.Ollama` or `langchain_community.llms.FakeListLLM` | The LLm that is used for all LLM tasks. The default depends on the value of `rag_core_lib.impl.settings.rag_class_types_settings.RAGClassTypeSettings.llm_type`. The FakeListLLM is used for testing |
9292
| prompt | `str` | [`rag_core_api.prompt_templates.answer_generation_prompt.ANSWER_GENERATION_PROMPT`](./rag-core-api/src/rag_core_api/prompt_templates/answer_generation_prompt.py) | The prompt used for answering the question. |
93-
| rephrasing_prompt | `str` | [`rag_core_api.prompt_templates.question_rephrasing_prompt.ANSWER_REPHRASING_PROMPT`](./rag-core-api/src/rag_core_api/prompt_templates/question_rephrasing_prompt.py) | The prompt used for rephrasing the question. The rephrased question (and the *original* question are both used for retrival of the documents)|
93+
| rephrasing_prompt | `str` | [`rag_core_api.prompt_templates.question_rephrasing_prompt.ANSWER_REPHRASING_PROMPT`](./rag-core-api/src/rag_core_api/prompt_templates/question_rephrasing_prompt.py) | The prompt used for rephrasing the question. The rephrased question (and the *original* question are both used for retrival of the documents) |
94+
| language_detection_prompt | `str` | [`rag_core_api.prompt_templates.language_detection_prompt.LANGUAGE_DETECTION_PROMPT`](./rag-core-api/src/rag_core_api/prompt_templates/language_detection_prompt.py) | Prompt for detecting input language. Enforces structured JSON output `{ "language": "<iso639-1>" }` and defaults to `en` when uncertain. |
9495
| langfuse_manager | [`rag_core_lib.impl.langfuse_manager.langfuse_manager.LangfuseManager`](./rag-core-lib/src/rag_core_lib/impl/langfuse_manager/langfuse_manager.py) | [`rag_core_lib.impl.langfuse_manager.langfuse_manager.LangfuseManager`](./rag-core-lib/src/rag_core_lib/impl/langfuse_manager/langfuse_manager.py) | Retrieves additional settings, as well as the prompt from langfuse if available. |
95-
| answer_generation_chain | [`rag_core_lib.chains.async_chain.AsyncChain[rag_core_api.impl.graph.graph_state.graph_state.AnswerGraphState, str]`](./rag-core-lib/src/rag_core_lib/chains/async_chain.py) | [`rag_core_api.impl.answer_generation_chains.answer_generation_chain.AnswerGenerationChain`](./rag-core-api/src/rag_core_api/impl/answer_generation_chains/answer_generation_chain.py) | LangChain chain used for answering the question. Is part of the *chat_graph*, |
96-
| rephrasing_chain | [`rag_core_lib.chains.async_chain.AsyncChain[rag_core_api.impl.graph.graph_state.graph_state.AnswerGraphState, str]`](./rag-core-lib/src/rag_core_lib/chains/async_chain.py) | [`rag_core_api.impl.answer_generation_chains.rephrasing_chain.RephrasingChain`](./rag-core-api/src/rag_core_api/impl/answer_generation_chains/rephrasing_chain.py) | LangChain chain used for rephrasing the question. Is part of the *chat_graph*. |
96+
| answer_generation_chain | [`rag_core_lib.chains.runnables.AsyncRunnable[rag_core_api.impl.graph.graph_state.graph_state.AnswerGraphState, str]`](./rag-core-lib/src/rag_core_lib/runnables/async_runnable.py) | [`rag_core_api.impl.answer_generation_chains.answer_generation_chain.AnswerGenerationChain`](./rag-core-api/src/rag_core_api/impl/answer_generation_chains/answer_generation_chain.py) | LangChain chain used for answering the question. Is part of the *chat_graph*, |
97+
| rephrasing_chain | [`rag_core_lib.chains.runnables.AsyncRunnable[rag_core_api.impl.graph.graph_state.graph_state.AnswerGraphState, str]`](./rag-core-lib/src/rag_core_lib/runnables/async_runnable.py) | [`rag_core_api.impl.answer_generation_chains.rephrasing_chain.RephrasingChain`](./rag-core-api/src/rag_core_api/impl/answer_generation_chains/rephrasing_chain.py) | LangChain chain used for rephrasing the question. Is part of the *chat_graph*. |
98+
| language_detection_chain | [`rag_core_lib.chains.runnables.AsyncRunnable[rag_core_api.impl.graph.graph_state.graph_state.AnswerGraphState, str]`](./rag-core-lib/src/rag_core_lib/runnables/async_runnable.py) | [`rag_core_api.impl.answer_generation_chains.language_detection_chain.LanguageDetectionChain`](./rag-core-api/src/rag_core_api/impl/answer_generation_chains/language_detection_chain.py) | Detects the language of the question and returns an ISO 639-1 code (e.g., `en`, `de`). Uses structured-output guidance and robust parsing with fallback to `en`. Part of the *chat_graph*. |
9799
| chat_graph | [`rag_core_api.graph.graph_base.GraphBase`](./rag-core-api/src/rag_core_api/graph/graph_base.py) | [`rag_core_api.impl.graph.chat_graph.DefaultChatGraph`](./rag-core-api/src/rag_core_api/impl/graph/chat_graph.py) | Langgraph graph that contains the entire logic for question answering. |
98-
| traced_chat_graph | [`rag_core_lib.chains.async_chain.AsyncChain[Any, Any]`](./rag-core-lib/src/rag_core_lib/chains/async_chain.py)| [`rag_core_lib.impl.tracers.langfuse_traced_chain.LangfuseTracedGraph`](./rag-core-lib/src/rag_core_lib/impl/tracers/langfuse_traced_chain.py) | Wraps around the *chat_graph* and add langfuse tracing. |
100+
| traced_chat_graph | [`rag_core_lib.chains.runnables.AsyncRunnable[Any, Any]`](./rag-core-lib/src/rag_core_lib/runnables/async_runnable.py)| [`rag_core_lib.impl.tracers.langfuse_traced_chain.LangfuseTracedGraph`](./rag-core-lib/src/rag_core_lib/impl/tracers/langfuse_traced_chain.py) | Wraps around the *chat_graph* and add langfuse tracing. |
99101
| evaluator | [`rag_core_api.impl.evaluator.langfuse_ragas_evaluator.LangfuseRagasEvaluator`](./rag-core-api/src/rag_core_api/impl/evaluator/langfuse_ragas_evaluator.py) | [`rag_core_api.impl.evaluator.langfuse_ragas_evaluator.LangfuseRagasEvaluator`](./rag-core-api/src/rag_core_api/impl/evaluator/langfuse_ragas_evaluator.py) | The evaulator used in the evaluate endpoint. |
100102
| chat_endpoint | [`rag_core_api.api_endpoints.chat.Chat`](./rag-core-api/src/rag_core_api/api_endpoints/chat.py) | [`rag_core_api.impl.api_endpoints.default_chat.DefaultChat`](./rag-core-api/src/rag_core_api/impl/api_endpoints/default_chat.py) | Implementation of the chat endpoint. Default implementation just calls the *traced_chat_graph* |
101103
| ragas_llm | `langchain_core.language_models.chat_models.BaseChatModel` | `langchain_openai.ChatOpenAI` or `langchain_ollama.ChatOllama` | The LLM used for the ragas evaluation. |
@@ -191,7 +193,7 @@ The extracted information will be summarized using LLM. The summary, as well as
191193
| langfuse_manager | [`rag_core_lib.impl.langfuse_manager.langfuse_manager.LangfuseManager`](./rag-core-lib/src/rag_core_lib/impl/langfuse_manager/langfuse_manager.py) | [`rag_core_lib.impl.langfuse_manager.langfuse_manager.LangfuseManager`](./rag-core-lib/src/rag_core_lib/impl/langfuse_manager/langfuse_manager.py) | Retrieves additional settings, as well as the prompt from langfuse if available. |
192194
| summarizer | [`admin_api_lib.summarizer.summarizer.Summarizer`](./admin-api-lib/src/admin_api_lib/summarizer/summarizer.py) | [`admin_api_lib.impl.summarizer.langchain_summarizer.LangchainSummarizer`](./admin-api-lib/src/admin_api_lib/impl/summarizer/langchain_summarizer.py) | Creates the summaries. Uses the shared retry decorator with optional per-summarizer overrides (see 2.4). |
193195
| untraced_information_enhancer |[`admin_api_lib.information_enhancer.information_enhancer.InformationEnhancer`](./admin-api-lib/src/admin_api_lib/information_enhancer/information_enhancer.py) | [`admin_api_lib.impl.information_enhancer.general_enhancer.GeneralEnhancer`](./admin-api-lib/src/admin_api_lib/impl/information_enhancer/general_enhancer.py) | Uses the *summarizer* to enhance the extracted documents. |
194-
| information_enhancer | [`rag_core_lib.chains.async_chain.AsyncChain[Any, Any]`](./rag-core-lib/src/rag_core_lib/chains/async_chain.py)| [`rag_core_lib.impl.tracers.langfuse_traced_chain.LangfuseTracedGraph`](./rag-core-lib/src/rag_core_lib/impl/tracers/langfuse_traced_chain.py) |Wraps around the *untraced_information_enhancer* and adds langfuse tracing. |
196+
| information_enhancer | [`rag_core_lib.chains.runnables.AsyncRunnable[Any, Any]`](./rag-core-lib/src/rag_core_lib/runnables/async_runnable.py)| [`rag_core_lib.impl.tracers.langfuse_traced_chain.LangfuseTracedGraph`](./rag-core-lib/src/rag_core_lib/impl/tracers/langfuse_traced_chain.py) |Wraps around the *untraced_information_enhancer* and adds langfuse tracing. |
195197
| document_deleter |[`admin_api_lib.api_endpoints.document_deleter.DocumentDeleter`](./admin-api-lib/src/admin_api_lib/api_endpoints/document_deleter.py) | [`admin_api_lib.impl.api_endpoints.default_document_deleter.DefaultDocumentDeleter`](./admin-api-lib/src/admin_api_lib/impl/api_endpoints/default_document_deleter.py) | Handles deletion of sources. |
196198
| documents_status_retriever | [`admin_api_lib.api_endpoints.documents_status_retriever.DocumentsStatusRetriever`](./admin-api-lib/src/admin_api_lib/api_endpoints/documents_status_retriever.py) | [`admin_api_lib.impl.api_endpoints.default_documents_status_retriever.DefaultDocumentsStatusRetriever`](./admin-api-lib/src/admin_api_lib/impl/api_endpoints/default_documents_status_retriever.py) |Handles return of source status. |
197199
| source_uploader | [`admin_api_lib.api_endpoints.source_uploader.SourceUploader`](./admin-api-lib/src/admin_api_lib/api_endpoints/source_uploader.py) | [`admin_api_lib.impl.api_endpoints.default_source_uploader.DefaultSourceUploader`](./admin-api-lib/src/admin_api_lib/impl/api_endpoints/default_source_uploader.py)| Handles data loading and extraction from various non-file sources. |

libs/admin-api-lib/src/admin_api_lib/impl/file_services/s3_service.py

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
"""Class to handle I/O with S3 storage."""
22

33
import logging
4-
import traceback
54
from pathlib import Path
65
from typing import BinaryIO
76

@@ -125,7 +124,7 @@ def delete_file(self, file_name: str) -> None:
125124
try:
126125
file_name = f"/{file_name}" if not file_name.startswith("/") else file_name
127126
self._s3_client.delete_object(Bucket=self._s3_settings.bucket, Key=file_name)
128-
logger.info(f"File {file_name} successfully deleted.")
129-
except Exception as e:
130-
logger.error("Error deleting file %s: %s %s" % (file_name, e, traceback.format_exc()))
127+
logger.info("File %s successfully deleted.", file_name)
128+
except Exception:
129+
logger.exception("Error deleting file %s", file_name)
131130
raise

libs/extractor-api-lib/src/extractor_api_lib/impl/extractors/file_extractors/pdf_extractor.py

Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -154,8 +154,8 @@ async def aextract_content(self, file_path: Path, name: str) -> list[InternalInf
154154
)
155155
pdf_elements += new_pdf_elements
156156

157-
logger.info(f"Extraction completed. Found {len(pdf_elements)} information pieces.")
158-
return pdf_elements
157+
logger.info("Extraction completed. Found %d information pieces.", len(pdf_elements))
158+
return pdf_elements
159159

160160
def _is_text_based(self, page: Page) -> bool:
161161
"""Classify whether a page is text-based, scanned.
@@ -200,8 +200,8 @@ def _extract_tables_from_text_page(
200200
table_df = pd.DataFrame(table_data)
201201
try:
202202
converted_table = self._dataframe_converter.convert(table_df)
203-
except TypeError as e:
204-
logger.error(f"Error while converting table to string: {e}")
203+
except TypeError:
204+
logger.exception("Error while converting table to string")
205205
continue
206206
if not converted_table.strip():
207207
continue
@@ -215,8 +215,8 @@ def _extract_tables_from_text_page(
215215
information_id=hash_datetime(),
216216
)
217217
)
218-
except Exception as e:
219-
logger.warning(f"Failed to find tables on page {page_index}: {e}")
218+
except Exception:
219+
logger.warning("Failed to find tables on page %d", page_index, exc_info=True)
220220

221221
return table_elements
222222

@@ -321,19 +321,19 @@ def _extract_tables_from_scanned_page(
321321
},
322322
)
323323
)
324-
except Exception as e:
325-
logger.warning(f"Failed to convert Camelot table {i + 1}: {e}")
324+
except Exception:
325+
logger.warning("Failed to convert Camelot table %d", i + 1, exc_info=True)
326326

327-
except Exception as e:
328-
logger.debug(f"Camelot table extraction failed for page {page_index}: {e}")
327+
except Exception:
328+
logger.debug("Camelot table extraction failed for page %d", page_index, exc_info=True)
329329

330330
return table_elements
331331

332332
def _extract_text_from_text_page(self, page: Page) -> str:
333333
try:
334334
return page.extract_text() or ""
335-
except Exception as e:
336-
logger.warning(f"Failed to extract text with pdfplumber: {e}")
335+
except Exception:
336+
logger.warning("Failed to extract text with pdfplumber", exc_info=True)
337337
return ""
338338

339339
def _extract_content_from_page(

libs/extractor-api-lib/tests/pdf_extractor_test.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -551,8 +551,8 @@ async def test_end_to_end_extraction(self, pdf_extractor, test_pdf_files):
551551
text_count = sum(1 for elem in result if elem.type == ContentType.TEXT)
552552
table_count = sum(1 for elem in result if elem.type == ContentType.TABLE)
553553

554-
logger.info(f" Text elements: {text_count}")
555-
logger.info(f" Table elements: {table_count}")
554+
logger.info(" Text elements: %d", text_count)
555+
logger.info(" Table elements: %d", table_count)
556556

557557
# Verify metadata completeness
558558
for i, element in enumerate(result):

libs/rag-core-api/src/rag_core_api/dependency_container.py

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
AnswerGenerationChain,
2020
)
2121
from rag_core_api.impl.answer_generation_chains.rephrasing_chain import RephrasingChain
22+
from rag_core_api.impl.answer_generation_chains.language_detection_chain import LanguageDetectionChain
2223
from rag_core_api.impl.api_endpoints.default_chat import DefaultChat
2324
from rag_core_api.impl.api_endpoints.default_information_pieces_remover import (
2425
DefaultInformationPiecesRemover,
@@ -57,6 +58,7 @@
5758
from rag_core_api.prompt_templates.question_rephrasing_prompt import (
5859
QUESTION_REPHRASING_PROMPT,
5960
)
61+
from rag_core_api.prompt_templates.language_detection_prompt import LANGUAGE_DETECTION_PROMPT
6062
from rag_core_lib.impl.data_types.content_type import ContentType
6163
from rag_core_lib.impl.langfuse_manager.langfuse_manager import LangfuseManager
6264
from rag_core_lib.impl.llms.llm_factory import chat_model_provider
@@ -180,6 +182,7 @@ class DependencyContainer(DeclarativeContainer):
180182

181183
prompt = ANSWER_GENERATION_PROMPT
182184
rephrasing_prompt = QUESTION_REPHRASING_PROMPT
185+
language_detection_prompt = LANGUAGE_DETECTION_PROMPT
183186

184187
langfuse = Singleton(
185188
Langfuse,
@@ -194,6 +197,7 @@ class DependencyContainer(DeclarativeContainer):
194197
managed_prompts={
195198
AnswerGenerationChain.__name__: prompt,
196199
RephrasingChain.__name__: rephrasing_prompt,
200+
LanguageDetectionChain.__name__: language_detection_prompt,
197201
},
198202
llm=large_language_model,
199203
)
@@ -208,10 +212,16 @@ class DependencyContainer(DeclarativeContainer):
208212
langfuse_manager=langfuse_manager,
209213
)
210214

215+
language_detection_chain = Singleton(
216+
LanguageDetectionChain,
217+
langfuse_manager=langfuse_manager,
218+
)
219+
211220
chat_graph = Singleton(
212221
DefaultChatGraph,
213222
composed_retriever=composed_retriever,
214223
rephrasing_chain=rephrasing_chain,
224+
language_detection_chain=language_detection_chain,
215225
mapper=information_piece_mapper,
216226
answer_generation_chain=answer_generation_chain,
217227
error_messages=error_messages,

0 commit comments

Comments
 (0)