-
Notifications
You must be signed in to change notification settings - Fork 60
[LCORE-873] Support Solr Vector I/O Provider, Add Streaming Query Support, and Introduce Solr Keyword Filtering #868
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
baf5337
8dc7a16
12e0318
2138583
f89d205
4618bb5
b8fd792
86c07ad
e37edc6
786aff0
41235b9
06b34f3
4df4bfc
1096c14
2191203
4ca24a7
a534ece
35182dc
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -4,8 +4,10 @@ | |||||||||||||||||||||||||||||||||||||
| import json | ||||||||||||||||||||||||||||||||||||||
| import logging | ||||||||||||||||||||||||||||||||||||||
| import re | ||||||||||||||||||||||||||||||||||||||
| import traceback | ||||||||||||||||||||||||||||||||||||||
| from datetime import UTC, datetime | ||||||||||||||||||||||||||||||||||||||
| from typing import Annotated, Any, Optional, cast | ||||||||||||||||||||||||||||||||||||||
| from urllib.parse import urljoin | ||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||
| from fastapi import APIRouter, Depends, HTTPException, Request | ||||||||||||||||||||||||||||||||||||||
| from litellm.exceptions import RateLimitError | ||||||||||||||||||||||||||||||||||||||
|
|
@@ -15,8 +17,9 @@ | |||||||||||||||||||||||||||||||||||||
| AsyncLlamaStackClient, # type: ignore | ||||||||||||||||||||||||||||||||||||||
| ) | ||||||||||||||||||||||||||||||||||||||
| from llama_stack_client.types import Shield, UserMessage # type: ignore | ||||||||||||||||||||||||||||||||||||||
| from llama_stack_client.types.alpha.agents.turn import Turn | ||||||||||||||||||||||||||||||||||||||
| from llama_stack_client.types.alpha.agents.turn_create_params import ( | ||||||||||||||||||||||||||||||||||||||
| from llama_stack_client.types.agents.turn import Turn | ||||||||||||||||||||||||||||||||||||||
| from llama_stack_client.types.agents.turn_create_params import ( | ||||||||||||||||||||||||||||||||||||||
| Document, | ||||||||||||||||||||||||||||||||||||||
| Toolgroup, | ||||||||||||||||||||||||||||||||||||||
| ToolgroupAgentToolGroupWithArgs, | ||||||||||||||||||||||||||||||||||||||
| ) | ||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+20
to
25
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🧩 Analysis chain🏁 Script executed: cat -n src/app/endpoints/query.py | head -50Repository: lightspeed-core/lightspeed-stack Length of output: 208 🏁 Script executed: rg "Document" src/app/endpoints/query.py -nRepository: lightspeed-core/lightspeed-stack Length of output: 916 🏁 Script executed: fd "llama_stack" --type f | head -20Repository: lightspeed-core/lightspeed-stack Length of output: 971 🏁 Script executed: rg "from llama_stack_client" src/ -n | head -20Repository: lightspeed-core/lightspeed-stack Length of output: 1789 🏁 Script executed: cat -n src/app/endpoints/query.py | head -80Repository: lightspeed-core/lightspeed-stack Length of output: 3331 🏁 Script executed: # Check the correct import paths more thoroughly
rg "Turn|Document" src/app/endpoints/query.py -B2 -A2 -n | head -60Repository: lightspeed-core/lightspeed-stack Length of output: 2104 🏁 Script executed: # Verify the actual import path for Turn and Document in llama_stack_client
rg "from llama_stack_client.types" src/ --type py | grep -E "(turn|agents)" | sort -uRepository: lightspeed-core/lightspeed-stack Length of output: 730 🏁 Script executed: # Check if Document is actually used anywhere in query.py
rg -n "Document" src/app/endpoints/query.pyRepository: lightspeed-core/lightspeed-stack Length of output: 916 🏁 Script executed: # Also check Toolgroup and ToolgroupAgentToolGroupWithArgs usage
rg -n "Toolgroup|ToolgroupAgentToolGroupWithArgs" src/app/endpoints/query.pyRepository: lightspeed-core/lightspeed-stack Length of output: 289 🏁 Script executed: # Verify the full file length to see if Document is used elsewhere
wc -l src/app/endpoints/query.pyRepository: lightspeed-core/lightspeed-stack Length of output: 106 🏁 Script executed: # Double-check that Document isn't used anywhere else in the file besides import and comments
rg -n "Document[^a-zA-Z]" src/app/endpoints/query.pyRepository: lightspeed-core/lightspeed-stack Length of output: 838 🏁 Script executed: # Verify the correct import path by checking what llama_stack_client actually exports
python3 << 'EOF'
# Check if we can find documentation or inspect the actual module structure
import subprocess
result = subprocess.run(
['rg', '-n', 'class Turn|class Document', 'src/', '--type', 'py', '-A', '1'],
capture_output=True,
text=True
)
print("Local class definitions:")
print(result.stdout[:2000] if result.stdout else "No local definitions found")
EOFRepository: lightspeed-core/lightspeed-stack Length of output: 221 Fix import paths for llama_stack_client types. The import paths at lines 20-25 use incorrect module paths. They should use Additionally, remove the unused Corrected imports should be:
🧰 Tools🪛 GitHub Actions: Integration tests[error] 20-20: ImportError: No module named 'llama_stack_client.types.agents'. 🪛 GitHub Actions: Ruff[error] 22-22: F401: Unused import 🪛 GitHub Actions: Unit tests[error] 20-20: ModuleNotFoundError: No module named 'llama_stack_client.types.agents' during pytest collection. Command: 'uv run pytest tests/unit --cov=src --cov=runner --cov-report term-missing'. 🤖 Prompt for AI Agents |
||||||||||||||||||||||||||||||||||||||
|
|
@@ -73,6 +76,10 @@ | |||||||||||||||||||||||||||||||||||||
| logger = logging.getLogger("app.endpoints.handlers") | ||||||||||||||||||||||||||||||||||||||
| router = APIRouter(tags=["query"]) | ||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||
| # When OFFLINE is False, use reference_url for chunk source | ||||||||||||||||||||||||||||||||||||||
| # When OFFLINE is True, use parent_id for chunk source | ||||||||||||||||||||||||||||||||||||||
| # TODO: move this setting to a higher level configuration | ||||||||||||||||||||||||||||||||||||||
| OFFLINE = True | ||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||
| query_response: dict[int | str, dict[str, Any]] = { | ||||||||||||||||||||||||||||||||||||||
| 200: QueryResponse.openapi_response(), | ||||||||||||||||||||||||||||||||||||||
|
|
@@ -312,15 +319,18 @@ async def query_endpoint_handler_base( # pylint: disable=R0914 | |||||||||||||||||||||||||||||||||||||
| user_conversation=user_conversation, query_request=query_request | ||||||||||||||||||||||||||||||||||||||
| ), | ||||||||||||||||||||||||||||||||||||||
| ) | ||||||||||||||||||||||||||||||||||||||
| summary, conversation_id, referenced_documents, token_usage = ( | ||||||||||||||||||||||||||||||||||||||
| await retrieve_response_func( | ||||||||||||||||||||||||||||||||||||||
| client, | ||||||||||||||||||||||||||||||||||||||
| llama_stack_model_id, | ||||||||||||||||||||||||||||||||||||||
| query_request, | ||||||||||||||||||||||||||||||||||||||
| token, | ||||||||||||||||||||||||||||||||||||||
| mcp_headers=mcp_headers, | ||||||||||||||||||||||||||||||||||||||
| provider_id=provider_id, | ||||||||||||||||||||||||||||||||||||||
| ) | ||||||||||||||||||||||||||||||||||||||
| ( | ||||||||||||||||||||||||||||||||||||||
| summary, | ||||||||||||||||||||||||||||||||||||||
| conversation_id, | ||||||||||||||||||||||||||||||||||||||
| referenced_documents, | ||||||||||||||||||||||||||||||||||||||
| token_usage, | ||||||||||||||||||||||||||||||||||||||
| ) = await retrieve_response_func( | ||||||||||||||||||||||||||||||||||||||
| client, | ||||||||||||||||||||||||||||||||||||||
| llama_stack_model_id, | ||||||||||||||||||||||||||||||||||||||
| query_request, | ||||||||||||||||||||||||||||||||||||||
| token, | ||||||||||||||||||||||||||||||||||||||
| mcp_headers=mcp_headers, | ||||||||||||||||||||||||||||||||||||||
| provider_id=provider_id, | ||||||||||||||||||||||||||||||||||||||
| ) | ||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||
| # Get the initial topic summary for the conversation | ||||||||||||||||||||||||||||||||||||||
|
|
@@ -618,7 +628,7 @@ def parse_metadata_from_text_item( | |||||||||||||||||||||||||||||||||||||
| url = data.get("docs_url") | ||||||||||||||||||||||||||||||||||||||
| title = data.get("title") | ||||||||||||||||||||||||||||||||||||||
| if url and title: | ||||||||||||||||||||||||||||||||||||||
| return ReferencedDocument(doc_url=url, doc_title=title) | ||||||||||||||||||||||||||||||||||||||
| return ReferencedDocument(doc_url=url, doc_title=title, doc_id=None) | ||||||||||||||||||||||||||||||||||||||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🧩 Analysis chain🏁 Script executed: # Find ReferencedDocument class definition
rg "class ReferencedDocument" -A 15 --type pyRepository: lightspeed-core/lightspeed-stack Length of output: 938 🏁 Script executed: # Check line 631 in context
head -650 src/app/endpoints/query.py | tail -30Repository: lightspeed-core/lightspeed-stack Length of output: 1195 🏁 Script executed: # Search for the typo "attatchment" mentioned in the scratchpad
rg "attatchment" src/app/endpoints/query.py -nRepository: lightspeed-core/lightspeed-stack Length of output: 238 🏁 Script executed: # Check if ReferencedDocument has any ConfigDict or extra field configuration
rg "class ReferencedDocument" -B 5 -A 20 src/models/responses.pyRepository: lightspeed-core/lightspeed-stack Length of output: 807 🏁 Script executed: # Check if BaseModel is configured to allow extra fields globally
rg "ConfigDict\|config_dict\|extra.*allow" src/models/responses.pyRepository: lightspeed-core/lightspeed-stack Length of output: 58 🏁 Script executed: # Check context around lines 912-913 for doc_ids_from_chunks
sed -n '900,920p' src/app/endpoints/query.pyRepository: lightspeed-core/lightspeed-stack Length of output: 793 🏁 Script executed: # Check more context around the ReferencedDocument instantiation and see where doc_ids_from_chunks is used
sed -n '920,960p' src/app/endpoints/query.pyRepository: lightspeed-core/lightspeed-stack Length of output: 1750 🏁 Script executed: # Check if doc_id is defined anywhere in ReferencedDocument or if there's a mismatch
rg "doc_id" src/models/responses.pyRepository: lightspeed-core/lightspeed-stack Length of output: 58 Remove The 🤖 Prompt for AI Agents |
||||||||||||||||||||||||||||||||||||||
| logger.debug("Invalid metadata block (missing url or title): %s", block) | ||||||||||||||||||||||||||||||||||||||
| except (ValueError, SyntaxError) as e: | ||||||||||||||||||||||||||||||||||||||
| logger.debug("Failed to parse metadata block: %s | Error: %s", block, e) | ||||||||||||||||||||||||||||||||||||||
|
|
@@ -751,19 +761,19 @@ async def retrieve_response( # pylint: disable=too-many-locals,too-many-branche | |||||||||||||||||||||||||||||||||||||
| ), | ||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||
| # Use specified vector stores or fetch all available ones | ||||||||||||||||||||||||||||||||||||||
| if query_request.vector_store_ids: | ||||||||||||||||||||||||||||||||||||||
| vector_db_ids = query_request.vector_store_ids | ||||||||||||||||||||||||||||||||||||||
| else: | ||||||||||||||||||||||||||||||||||||||
| vector_db_ids = [ | ||||||||||||||||||||||||||||||||||||||
| vector_store.id | ||||||||||||||||||||||||||||||||||||||
| for vector_store in (await client.vector_stores.list()).data | ||||||||||||||||||||||||||||||||||||||
| ] | ||||||||||||||||||||||||||||||||||||||
| toolgroups = (get_rag_toolgroups(vector_db_ids) or []) + [ | ||||||||||||||||||||||||||||||||||||||
| mcp_server.name for mcp_server in configuration.mcp_servers | ||||||||||||||||||||||||||||||||||||||
| ] | ||||||||||||||||||||||||||||||||||||||
| # Include RAG toolgroups when vector DBs are available | ||||||||||||||||||||||||||||||||||||||
| vector_dbs = await client.vector_dbs.list() | ||||||||||||||||||||||||||||||||||||||
| vector_db_ids = [vdb.identifier for vdb in vector_dbs] | ||||||||||||||||||||||||||||||||||||||
| mcp_toolgroups = [mcp_server.name for mcp_server in configuration.mcp_servers] | ||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||
| toolgroups = None | ||||||||||||||||||||||||||||||||||||||
| if vector_db_ids: | ||||||||||||||||||||||||||||||||||||||
| toolgroups = get_rag_toolgroups(vector_db_ids) + mcp_toolgroups | ||||||||||||||||||||||||||||||||||||||
| elif mcp_toolgroups: | ||||||||||||||||||||||||||||||||||||||
| toolgroups = mcp_toolgroups | ||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||
| # Convert empty list to None for consistency with existing behavior | ||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+764
to
775
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Guard Here: vector_dbs = await client.vector_dbs.list()
vector_db_ids = [vdb.identifier for vdb in vector_dbs]
mcp_toolgroups = [mcp_server.name for mcp_server in configuration.mcp_servers]
toolgroups = None
if vector_db_ids:
toolgroups = get_rag_toolgroups(vector_db_ids) + mcp_toolgroups
elif mcp_toolgroups:
toolgroups = mcp_toolgroups
Use an intermediate variable and coerce - toolgroups = None
- if vector_db_ids:
- toolgroups = get_rag_toolgroups(vector_db_ids) + mcp_toolgroups
- elif mcp_toolgroups:
- toolgroups = mcp_toolgroups
+ toolgroups = None
+ if vector_db_ids:
+ rag_toolgroups = get_rag_toolgroups(vector_db_ids) or []
+ toolgroups = rag_toolgroups + mcp_toolgroups
+ elif mcp_toolgroups:
+ toolgroups = mcp_toolgroupsThis will satisfy mypy and avoid runtime failures when no RAG toolgroups are available. 🧰 Tools🪛 GitHub Actions: Type checks[error] 748-748: mypy: Unsupported left operand type for + ('None'). (Command: 'uv run mypy --explicit-package-bases --disallow-untyped-calls --disallow-untyped-defs --disallow-incomplete-defs --ignore-missing-imports --disable-error-code attr-defined src/') 🤖 Prompt for AI Agents |
||||||||||||||||||||||||||||||||||||||
| if not toolgroups: | ||||||||||||||||||||||||||||||||||||||
| if toolgroups == []: | ||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+769
to
+776
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Fix type error: The pipeline reports toolgroups = None
if vector_db_ids:
- toolgroups = get_rag_toolgroups(vector_db_ids) + mcp_toolgroups
+ rag_toolgroups = get_rag_toolgroups(vector_db_ids)
+ toolgroups = (rag_toolgroups or []) + mcp_toolgroups
elif mcp_toolgroups:
toolgroups = mcp_toolgroups📝 Committable suggestion
Suggested change
🧰 Tools🪛 GitHub Actions: Pyright[error] 753-753: Python type checker (Pyright): Operator "+" not supported for "None" (reportOptionalOperand) 🪛 GitHub Actions: Type checks[error] 753-753: mypy: Unsupported left operand type for + ("None") 🤖 Prompt for AI Agents |
||||||||||||||||||||||||||||||||||||||
| toolgroups = None | ||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||
| # TODO: LCORE-881 - Remove if Llama Stack starts to support these mime types | ||||||||||||||||||||||||||||||||||||||
|
|
@@ -776,8 +786,107 @@ async def retrieve_response( # pylint: disable=too-many-locals,too-many-branche | |||||||||||||||||||||||||||||||||||||
| # for doc in query_request.get_documents() | ||||||||||||||||||||||||||||||||||||||
| # ] | ||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||
| # Extract RAG chunks from vector DB query response BEFORE calling agent | ||||||||||||||||||||||||||||||||||||||
| rag_chunks = [] | ||||||||||||||||||||||||||||||||||||||
| doc_ids_from_chunks = [] | ||||||||||||||||||||||||||||||||||||||
| retrieved_chunks = [] | ||||||||||||||||||||||||||||||||||||||
| retrieved_scores = [] | ||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||
| try: | ||||||||||||||||||||||||||||||||||||||
| if vector_db_ids: | ||||||||||||||||||||||||||||||||||||||
| vector_db_id = vector_db_ids[0] # Use first available vector DB | ||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||
| params = {"k": 5, "score_threshold": 0.0} | ||||||||||||||||||||||||||||||||||||||
| logger.info(f"Initial params: {params}") | ||||||||||||||||||||||||||||||||||||||
| logger.info(f"query_request.solr: {query_request.solr}") | ||||||||||||||||||||||||||||||||||||||
| if query_request.solr: | ||||||||||||||||||||||||||||||||||||||
| # Pass the entire solr dict under the 'solr' key | ||||||||||||||||||||||||||||||||||||||
| params["solr"] = query_request.solr | ||||||||||||||||||||||||||||||||||||||
| logger.info(f"Final params with solr filters: {params}") | ||||||||||||||||||||||||||||||||||||||
| else: | ||||||||||||||||||||||||||||||||||||||
| logger.info("No solr filters provided") | ||||||||||||||||||||||||||||||||||||||
| logger.info(f"Final params being sent to vector_io.query: {params}") | ||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||
| query_response = await client.vector_io.query( | ||||||||||||||||||||||||||||||||||||||
| vector_db_id=vector_db_id, query=query_request.query, params=params | ||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+795
to
+811
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Fix unbound variable Pipeline failures indicate:
+ # Initialize vector_db_ids before conditional to avoid unbound variable
+ vector_db_ids: list[str] = []
+
# bypass tools and MCP servers if no_tools is True
if query_request.no_tools:
mcp_headers = {}
agent.extra_headers = {}
toolgroups = None
else:
# ... existing code ...
vector_dbs = await client.vector_dbs.list()
vector_db_ids = [vdb.identifier for vdb in vector_dbs]
# ...For the - params = {"k": 5, "score_threshold": 0.0}
+ params: dict[str, Any] = {"k": 5, "score_threshold": 0.0}
🧰 Tools🪛 GitHub Actions: Pyright[error] 778-778: Pyright: "vector_db_ids" is possibly unbound (reportPossiblyUnboundVariable) 🪛 GitHub Actions: Type checks[error] 793-793: mypy: Argument "params" to "query" of "AsyncVectorIoResource" has incompatible type "dict[str, float]"; expected "dict[str, bool | float | str | Iterable[object] | object | None] | NotGiven" |
||||||||||||||||||||||||||||||||||||||
| ) | ||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||
| logger.info(f"The query response total payload: {query_response}") | ||||||||||||||||||||||||||||||||||||||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Avoid logging entire query response payloads at INFO level. Logging the full - logger.info(f"The query response total payload: {query_response}")
+ logger.debug("Query response chunk count: %d", len(query_response.chunks) if query_response.chunks else 0)📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents |
||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||
| if query_response.chunks: | ||||||||||||||||||||||||||||||||||||||
| from models.responses import RAGChunk, ReferencedDocument | ||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+817
to
+818
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🛠️ Refactor suggestion | 🟠 Major Move imports to module level. Importing if query_response.chunks:
- from models.responses import RAGChunk, ReferencedDocument
-
retrieved_chunks = query_response.chunks📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents |
||||||||||||||||||||||||||||||||||||||
| retrieved_chunks = query_response.chunks | ||||||||||||||||||||||||||||||||||||||
| retrieved_scores = ( | ||||||||||||||||||||||||||||||||||||||
| query_response.scores if hasattr(query_response, "scores") else [] | ||||||||||||||||||||||||||||||||||||||
| ) | ||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||
| # Extract doc_ids from chunks for referenced_documents | ||||||||||||||||||||||||||||||||||||||
| metadata_doc_ids = set() | ||||||||||||||||||||||||||||||||||||||
| for chunk in query_response.chunks: | ||||||||||||||||||||||||||||||||||||||
| metadata = getattr(chunk, "metadata", None) | ||||||||||||||||||||||||||||||||||||||
| if metadata and "doc_id" in metadata: | ||||||||||||||||||||||||||||||||||||||
| reference_doc = metadata["doc_id"] | ||||||||||||||||||||||||||||||||||||||
| logger.info(reference_doc) | ||||||||||||||||||||||||||||||||||||||
| if reference_doc and reference_doc not in metadata_doc_ids: | ||||||||||||||||||||||||||||||||||||||
| metadata_doc_ids.add(reference_doc) | ||||||||||||||||||||||||||||||||||||||
| doc_ids_from_chunks.append( | ||||||||||||||||||||||||||||||||||||||
| ReferencedDocument( | ||||||||||||||||||||||||||||||||||||||
| doc_title=metadata.get("title", None), | ||||||||||||||||||||||||||||||||||||||
| doc_url="https://mimir.corp.redhat.com" | ||||||||||||||||||||||||||||||||||||||
| + reference_doc, | ||||||||||||||||||||||||||||||||||||||
| ) | ||||||||||||||||||||||||||||||||||||||
| ) | ||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||
| logger.info( | ||||||||||||||||||||||||||||||||||||||
| f"Extracted {len(doc_ids_from_chunks)} unique document IDs from chunks" | ||||||||||||||||||||||||||||||||||||||
| ) | ||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||
| except Exception as e: | ||||||||||||||||||||||||||||||||||||||
| logger.warning(f"Failed to query vector database for chunks: {e}") | ||||||||||||||||||||||||||||||||||||||
| logger.debug(f"Vector DB query error details: {traceback.format_exc()}") | ||||||||||||||||||||||||||||||||||||||
| # Continue without RAG chunks | ||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||
| # Convert retrieved chunks to RAGChunk format | ||||||||||||||||||||||||||||||||||||||
| for i, chunk in enumerate(retrieved_chunks): | ||||||||||||||||||||||||||||||||||||||
| # Extract source from chunk metadata based on OFFLINE flag | ||||||||||||||||||||||||||||||||||||||
| source = None | ||||||||||||||||||||||||||||||||||||||
| if chunk.metadata: | ||||||||||||||||||||||||||||||||||||||
| if OFFLINE: | ||||||||||||||||||||||||||||||||||||||
| parent_id = chunk.metadata.get("parent_id") | ||||||||||||||||||||||||||||||||||||||
| if parent_id: | ||||||||||||||||||||||||||||||||||||||
| source = urljoin("https://mimir.corp.redhat.com", parent_id) | ||||||||||||||||||||||||||||||||||||||
| else: | ||||||||||||||||||||||||||||||||||||||
| source = chunk.metadata.get("reference_url") | ||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||
| # Get score from retrieved_scores list if available | ||||||||||||||||||||||||||||||||||||||
| score = retrieved_scores[i] if i < len(retrieved_scores) else None | ||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||
| rag_chunks.append( | ||||||||||||||||||||||||||||||||||||||
| RAGChunk( | ||||||||||||||||||||||||||||||||||||||
| content=chunk.content, | ||||||||||||||||||||||||||||||||||||||
| source=source, | ||||||||||||||||||||||||||||||||||||||
| score=score, | ||||||||||||||||||||||||||||||||||||||
| ) | ||||||||||||||||||||||||||||||||||||||
| ) | ||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||
| logger.info(f"Retrieved {len(rag_chunks)} chunks from vector DB") | ||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+851
to
+874
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Normalize Within the loop that builds if OFFLINE:
parent_id = chunk.metadata.get("parent_id")
if parent_id:
source = urljoin("https://mimir.corp.redhat.com", parent_id)
...
rag_chunks.append(
RAGChunk(
content=chunk.content,
source=source,
score=score,
)
)Problems:
Fix: - if OFFLINE:
- parent_id = chunk.metadata.get("parent_id")
- if parent_id:
- source = urljoin("https://mimir.corp.redhat.com", parent_id)
+ if OFFLINE:
+ parent_id = chunk.metadata.get("parent_id")
+ if parent_id and isinstance(parent_id, str):
+ source = urljoin("https://mimir.corp.redhat.com", parent_id)
@@
- rag_chunks.append(
- RAGChunk(
- content=chunk.content,
- source=source,
- score=score,
- )
- )
+ rag_chunks.append(
+ RAGChunk(
+ content=str(chunk.content) if chunk.content else "",
+ source=source if isinstance(source, str) else None,
+ score=score,
+ )
+ )This resolves the Also applies to: 835-835 🧰 Tools🪛 GitHub Actions: Type checks[error] 835-835: mypy: Value of type variable 'AnyStr' of 'urljoin' cannot be 'object'. (Command: 'uv run mypy --explicit-package-bases --disallow-untyped-calls --disallow-untyped-defs --disallow-incomplete-defs --ignore-missing-imports --disable-error-code attr-defined src/') 🤖 Prompt for AI Agents |
||||||||||||||||||||||||||||||||||||||
| # Format RAG context for injection into user message | ||||||||||||||||||||||||||||||||||||||
| rag_context = "" | ||||||||||||||||||||||||||||||||||||||
| if rag_chunks: | ||||||||||||||||||||||||||||||||||||||
| context_chunks = [] | ||||||||||||||||||||||||||||||||||||||
| for chunk in rag_chunks[:5]: # Limit to top 5 chunks | ||||||||||||||||||||||||||||||||||||||
| chunk_text = f"Source: {chunk.source or 'Unknown'}\n{chunk.content}" | ||||||||||||||||||||||||||||||||||||||
| context_chunks.append(chunk_text) | ||||||||||||||||||||||||||||||||||||||
| rag_context = "\n\nRelevant documentation:\n" + "\n\n".join(context_chunks) | ||||||||||||||||||||||||||||||||||||||
| logger.info(f"Injecting {len(context_chunks)} RAG chunks into user message") | ||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||
| # Inject RAG context into user message | ||||||||||||||||||||||||||||||||||||||
| user_content = query_request.query + rag_context | ||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||
| response = await agent.create_turn( | ||||||||||||||||||||||||||||||||||||||
| messages=[UserMessage(role="user", content=query_request.query).model_dump()], | ||||||||||||||||||||||||||||||||||||||
| messages=[UserMessage(role="user", content=user_content)], | ||||||||||||||||||||||||||||||||||||||
| session_id=session_id, | ||||||||||||||||||||||||||||||||||||||
| # documents=documents, | ||||||||||||||||||||||||||||||||||||||
| stream=False, | ||||||||||||||||||||||||||||||||||||||
|
|
@@ -795,12 +904,14 @@ async def retrieve_response( # pylint: disable=too-many-locals,too-many-branche | |||||||||||||||||||||||||||||||||||||
| else "" | ||||||||||||||||||||||||||||||||||||||
| ), | ||||||||||||||||||||||||||||||||||||||
| tool_calls=[], | ||||||||||||||||||||||||||||||||||||||
| tool_results=[], | ||||||||||||||||||||||||||||||||||||||
| rag_chunks=[], | ||||||||||||||||||||||||||||||||||||||
| rag_chunks=rag_chunks, | ||||||||||||||||||||||||||||||||||||||
| ) | ||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||
| referenced_documents = parse_referenced_documents(response) | ||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||
| # Add documents from Solr chunks to referenced_documents | ||||||||||||||||||||||||||||||||||||||
| referenced_documents.extend(doc_ids_from_chunks) | ||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||
| # Update token count metrics and extract token usage in one call | ||||||||||||||||||||||||||||||||||||||
| model_label = model_id.split("/", 1)[1] if "/" in model_id else model_id | ||||||||||||||||||||||||||||||||||||||
| token_usage = extract_and_update_token_metrics( | ||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion | 🟠 Major
Hardcoded Solr URL should use environment variable.
The
solr_url: "http://localhost:8983/solr"is hardcoded and won't work in containerized or distributed deployments. Consider using an environment variable reference similar to line 19's pattern.📝 Committable suggestion
🤖 Prompt for AI Agents