51 lines
1.6 KiB
Python
51 lines
1.6 KiB
Python
"""Grounded answer generation."""
|
|
|
|
from __future__ import annotations
|
|
|
|
import logging
|
|
from typing import TYPE_CHECKING
|
|
|
|
from python.ebook_search.llm_interface import request_chat_completion
|
|
from python.ebook_search.prompts import load_prompt
|
|
|
|
if TYPE_CHECKING:
|
|
import httpx
|
|
|
|
from python.ebook_search.config import EbookSearchConfig
|
|
from python.ebook_search.search import SearchResult
|
|
|
|
logger = logging.getLogger(__name__)
|
|
|
|
|
|
async def answer_query(
|
|
client: httpx.AsyncClient,
|
|
query: str,
|
|
results: list[SearchResult],
|
|
config: EbookSearchConfig,
|
|
) -> str:
|
|
"""Answer a question using only retrieved chunks."""
|
|
if not config.answer_enabled:
|
|
logger.info("ebook_answer_skipped_disabled")
|
|
return "Answer generation is disabled. Source chunks are shown below."
|
|
|
|
if not results:
|
|
logger.info("ebook_answer_skipped_no_results")
|
|
return "No relevant sources were found."
|
|
|
|
logger.info(
|
|
f"ebook_answer_request_start {config.vllm_base_url=} {config.chat_model=} sources={len(results)} "
|
|
f"query_length={len(query)}"
|
|
)
|
|
context = "\n\n".join(
|
|
f"[{index}] {result.source_title}{' - ' + result.chapter_title if result.chapter_title else ''}\n{result.text}"
|
|
for index, result in enumerate(results, start=1)
|
|
)
|
|
content = await request_chat_completion(
|
|
client,
|
|
config,
|
|
load_prompt("answer").messages(query=query, context=context),
|
|
)
|
|
|
|
logger.info(f"ebook_answer_request_complete {config.chat_model=} answer_length={len(content)}")
|
|
return content or "The model returned an empty answer."
|