diff --git a/README.md b/README.md index 5e56947..e474d9e 100644 --- a/README.md +++ b/README.md @@ -154,6 +154,13 @@ You should see an answer followed by a `Sources:` block listing the URLs used. ## Using it +While inside the interactive CLI (`ask>`), you can use the following commands to control the session and view metadata: +| Command | Description | +| :--- | :--- | +| `:stats` | Toggles the display of performance metrics (e.g., token count, response time) for subsequent prompts. | +| `:verbose` | Toggles verbose mode, displaying detailed internal logs, thought processes, or API interactions. | +| `exit` or `quit` | Safely terminates the interactive session and returns to your terminal shell. (You can also use `Ctrl-D`). | + A grounded answer looks like this: ``` diff --git a/src/apps/dev_cli.py b/src/apps/dev_cli.py index 3245537..324d186 100644 --- a/src/apps/dev_cli.py +++ b/src/apps/dev_cli.py @@ -4,6 +4,7 @@ from src.config.logger import get_logger from src.infrastructure.db import async_session_factory from src.infrastructure.db.repository import Repository +from src.retrieval.services import retrieval_service log = get_logger(__name__) @@ -19,13 +20,33 @@ async def _check_db() -> None: async def _repl() -> None: await _check_db() + verbose = False # flag for :verbose + print("cs-assistant dev CLI. Type 'exit' or Ctrl-D to quit.\n") + print("Type ':stats' or ':verbose' for cmds.\n") + while True: try: question = input("ask> ").strip() except (EOFError, KeyboardInterrupt): print("\nbye") return + + # :stats cmd + if question.lower() in {":stats"}: + async with async_session_factory() as session: + count_sources, count_chunks = await Repository.get_source_and_chunk_counts(session) + print(f"{count_sources} sources, {count_chunks} chunks loaded") + await _check_db() + continue + + # :verbose cmd + if question.lower() in {":verbose"}: + verbose = not verbose + print(f"Verbose mode: {'ON' if verbose else 'OFF'}") + continue + + # exit/quit cmd if question.lower() in {"exit", "quit"}: return if not question: @@ -37,6 +58,20 @@ async def _repl() -> None: print(f"\nError: {e}\n") continue + # printing out chunk content (verbose mode) + if verbose: + retrieved_chunks = await retrieval_service.get_relevant_chunks(question) + for chunk_item in retrieved_chunks: + source_url = chunk_item.chunk.source_url + similarity_score = chunk_item.score + snippet = " ".join( + (chunk_item.chunk.content.split())[:250] + ) # snippet ~250 words (maybe chars instead?) + print(f"URL: {source_url}") + print(f"Similarity score: {similarity_score}") + print(f"Content snippet: {snippet}") + print("-" * 60) + print(f"\n{answer.text}\n") if answer.sources: print("Sources:") diff --git a/src/infrastructure/db/repository.py b/src/infrastructure/db/repository.py index 0dc8571..38c4538 100644 --- a/src/infrastructure/db/repository.py +++ b/src/infrastructure/db/repository.py @@ -14,6 +14,24 @@ async def has_chunks(session: AsyncSession) -> bool: result = await session.execute(select(ChunkRow.id).limit(1)) return result.scalar_one_or_none() is not None + @staticmethod + async def get_source_and_chunk_counts(session: AsyncSession) -> tuple[int, int]: + count_sources = await Repository.count_sources(session) + count_chunks = await Repository.count_chunks(session) + return count_sources, count_chunks + + @staticmethod + async def count_chunks(session: AsyncSession) -> int: + result = await session.execute(select(func.count(ChunkRow.id))) + # if above doesn't work properly + # result = await session.execute(select(func.count().select_from(ChunkRow))) + return result.scalar_one() + + @staticmethod + async def count_sources(session: AsyncSession) -> int: + result = await session.execute(select(func.count(SourceRow.id))) + return result.scalar_one() + @staticmethod async def get_or_create_source( session: AsyncSession, *, name: str, url: str, source_type: str