Open botschmiede opened 1 month ago
@botschmiede Greetings! I'm here to assist you with any issues you're facing. Let's work together to resolve the bug you've encountered.
To resolve the synthesizer object has no attribute 'metadata'
error when using RetrieverQueryEngine
with a custom hybrid retriever and response_synthesizer
objects like CompactAndRefine
or Refine
, you need to ensure that the RetrieverQueryEngine
and the response_synthesizer
are correctly set up and used.
Here is an example of how to set up and use the RetrieverQueryEngine
and Refine
class:
from collections import OrderedDict
from typing import Any, Dict, Optional, Type, cast
import pytest
from llama_index.legacy.bridge.pydantic import BaseModel
from llama_index.legacy.callbacks import CallbackManager
from llama_index.legacy.response_synthesizers import Refine
from llama_index.legacy.response_synthesizers.refine import (
StructuredRefineResponse,
)
from llama_index.legacy.service_context import ServiceContext
from llama_index.legacy.types import BasePydanticProgram
from llama_index.core.query_engine.retriever_query_engine import RetrieverQueryEngine
class MockRefineProgram(BasePydanticProgram):
def __init__(self, input_to_query_satisfied: Dict[str, bool]):
self._input_to_query_satisfied = input_to_query_satisfied
@property
def output_cls(self) -> Type[BaseModel]:
return StructuredRefineResponse
def __call__(self, *args: Any, context_str: Optional[str] = None, context_msg: Optional[str] = None, **kwargs: Any) -> StructuredRefineResponse:
input_str = context_str or context_msg
input_str = cast(str, input_str)
query_satisfied = self._input_to_query_satisfied[input_str]
return StructuredRefineResponse(answer=input_str, query_satisfied=query_satisfied)
async def acall(self, *args: Any, context_str: Optional[str] = None, context_msg: Optional[str] = None, **kwargs: Any) -> StructuredRefineResponse:
input_str = context_str or context_msg
input_str = cast(str, input_str)
query_satisfied = self._input_to_query_satisfied[input_str]
return StructuredRefineResponse(answer=input_str, query_satisfied=query_satisfied)
@pytest.fixture()
def mock_refine_service_context(patch_llm_predictor: Any) -> ServiceContext:
cb_manager = CallbackManager([])
return ServiceContext.from_defaults(llm_predictor=patch_llm_predictor, callback_manager=cb_manager)
@pytest.fixture()
def refine_instance(mock_refine_service_context: ServiceContext) -> Refine:
return Refine(service_context=mock_refine_service_context, streaming=False, verbose=True, structured_answer_filtering=True)
@pytest.mark.asyncio()
async def test_answer_filtering_one_answer(mock_refine_service_context: ServiceContext) -> None:
input_to_query_satisfied = OrderedDict([("input1", False), ("input2", True), ("input3", False)])
def program_factory(*args: Any, **kwargs: Any) -> MockRefineProgram:
return MockRefineProgram(input_to_query_satisfied)
refine_instance = Refine(service_context=mock_refine_service_context, structured_answer_filtering=True, program_factory=program_factory)
res = await refine_instance.aget_response("question", list(input_to_query_satisfied.keys()))
assert res == "input2"
@pytest.mark.asyncio()
async def test_answer_filtering_no_answers(mock_refine_service_context: ServiceContext) -> None:
input_to_query_satisfied = OrderedDict([("input1", False), ("input2", False), ("input3", False)])
def program_factory(*args: Any, **kwargs: Any) -> MockRefineProgram:
return MockRefineProgram(input_to_query_satisfied)
refine_instance = Refine(service_context=mock_refine_service_context, structured_answer_filtering=True, program_factory=program_factory)
res = await refine_instance.aget_response("question", list(input_to_query_satisfied.keys()))
assert res == "Empty Response"
# Setting up the RetrieverQueryEngine
retriever = ... # Your custom retriever
response_synthesizer = Refine(service_context=mock_refine_service_context, structured_answer_filtering=True, program_factory=program_factory)
query_engine = RetrieverQueryEngine(retriever=retriever, response_synthesizer=response_synthesizer)
# Example usage
query_bundle = ... # Your query bundle
response = query_engine.synthesize(query_bundle, nodes)
Ensure that the RetrieverQueryEngine
is correctly instantiated with the necessary parameters and that the Refine
class is properly defined to handle the responses. This setup should help resolve the synthesizer object has no attribute 'metadata'
error.
Additionally, the RetrieverQueryEngine
class has a method synthesize
that uses the response_synthesizer
to generate the response. Make sure that the response_synthesizer
is correctly initialized and passed to the RetrieverQueryEngine
:
def synthesize(
self,
query_bundle: QueryBundle,
nodes: List[NodeWithScore],
additional_source_nodes: Optional[Sequence[NodeWithScore]] = None,
) -> RESPONSE_TYPE:
return self._response_synthesizer.synthesize(
query=query_bundle,
nodes=nodes,
additional_source_nodes=additional_source_nodes,
)
This method should be used to synthesize the response from the retrieved nodes. Ensure that all necessary attributes and methods are correctly defined in your custom response_synthesizer
to avoid attribute errors [1][2][3].
To continue talking to Dosu, mention @dosu.
Bug Description
RetrieverQueryEngine cant handle response_synthesizer objects when passing custom hybrid retriever following error message:
synthesizer object has no attribute 'metadata'
Where the synthesizer can be CompactAndRefine, Refine etc.
Version
Version: 0.10.22
Steps to Reproduce
The Custom Hybrid Retriever:
Relevant Logs/Tracbacks
No response