haystack-ai 3.2.0.dev20260902000321__tar.gz → 3.2.0.dev20260903000331__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {haystack_ai-3.2.0.dev20260902000321 → haystack_ai-3.2.0.dev20260903000331}/PKG-INFO +1 -1
- haystack_ai-3.2.0.dev20260903000331/VERSION.txt +1 -0
- {haystack_ai-3.2.0.dev20260902000321 → haystack_ai-3.2.0.dev20260903000331}/haystack/components/converters/output_adapter.py +5 -1
- {haystack_ai-3.2.0.dev20260902000321 → haystack_ai-3.2.0.dev20260903000331}/haystack/components/generators/chat/openai_responses.py +8 -4
- {haystack_ai-3.2.0.dev20260902000321 → haystack_ai-3.2.0.dev20260903000331}/haystack/components/preprocessors/recursive_splitter.py +7 -7
- {haystack_ai-3.2.0.dev20260902000321 → haystack_ai-3.2.0.dev20260903000331}/haystack/components/routers/conditional_router.py +5 -1
- {haystack_ai-3.2.0.dev20260902000321 → haystack_ai-3.2.0.dev20260903000331}/haystack/dataclasses/chat_message.py +11 -4
- {haystack_ai-3.2.0.dev20260902000321 → haystack_ai-3.2.0.dev20260903000331}/haystack/hooks/tool_result_offloading/hooks.py +132 -85
- {haystack_ai-3.2.0.dev20260902000321 → haystack_ai-3.2.0.dev20260903000331}/haystack/hooks/tool_result_offloading/stores.py +25 -8
- {haystack_ai-3.2.0.dev20260902000321 → haystack_ai-3.2.0.dev20260903000331}/haystack/hooks/tool_result_offloading/types/protocol.py +28 -9
- {haystack_ai-3.2.0.dev20260902000321 → haystack_ai-3.2.0.dev20260903000331}/haystack/utils/hf.py +6 -3
- haystack_ai-3.2.0.dev20260902000321/VERSION.txt +0 -1
- {haystack_ai-3.2.0.dev20260902000321 → haystack_ai-3.2.0.dev20260903000331}/.gitignore +0 -0
- {haystack_ai-3.2.0.dev20260902000321 → haystack_ai-3.2.0.dev20260903000331}/LICENSE +0 -0
- {haystack_ai-3.2.0.dev20260902000321 → haystack_ai-3.2.0.dev20260903000331}/README.md +0 -0
- {haystack_ai-3.2.0.dev20260902000321 → haystack_ai-3.2.0.dev20260903000331}/haystack/__init__.py +0 -0
- {haystack_ai-3.2.0.dev20260902000321 → haystack_ai-3.2.0.dev20260903000331}/haystack/components/__init__.py +0 -0
- {haystack_ai-3.2.0.dev20260902000321 → haystack_ai-3.2.0.dev20260903000331}/haystack/components/agents/__init__.py +0 -0
- {haystack_ai-3.2.0.dev20260902000321 → haystack_ai-3.2.0.dev20260903000331}/haystack/components/agents/agent.py +0 -0
- {haystack_ai-3.2.0.dev20260902000321 → haystack_ai-3.2.0.dev20260903000331}/haystack/components/agents/state/__init__.py +0 -0
- {haystack_ai-3.2.0.dev20260902000321 → haystack_ai-3.2.0.dev20260903000331}/haystack/components/agents/state/state.py +0 -0
- {haystack_ai-3.2.0.dev20260902000321 → haystack_ai-3.2.0.dev20260903000331}/haystack/components/agents/state/state_utils.py +0 -0
- {haystack_ai-3.2.0.dev20260902000321 → haystack_ai-3.2.0.dev20260903000331}/haystack/components/agents/tool_calling.py +0 -0
- {haystack_ai-3.2.0.dev20260902000321 → haystack_ai-3.2.0.dev20260903000331}/haystack/components/agents/utils.py +0 -0
- {haystack_ai-3.2.0.dev20260902000321 → haystack_ai-3.2.0.dev20260903000331}/haystack/components/builders/__init__.py +0 -0
- {haystack_ai-3.2.0.dev20260902000321 → haystack_ai-3.2.0.dev20260903000331}/haystack/components/builders/answer_builder.py +0 -0
- {haystack_ai-3.2.0.dev20260902000321 → haystack_ai-3.2.0.dev20260903000331}/haystack/components/builders/chat_prompt_builder.py +0 -0
- {haystack_ai-3.2.0.dev20260902000321 → haystack_ai-3.2.0.dev20260903000331}/haystack/components/builders/prompt_builder.py +0 -0
- {haystack_ai-3.2.0.dev20260902000321 → haystack_ai-3.2.0.dev20260903000331}/haystack/components/caching/__init__.py +0 -0
- {haystack_ai-3.2.0.dev20260902000321 → haystack_ai-3.2.0.dev20260903000331}/haystack/components/caching/cache_checker.py +0 -0
- {haystack_ai-3.2.0.dev20260902000321 → haystack_ai-3.2.0.dev20260903000331}/haystack/components/converters/__init__.py +0 -0
- {haystack_ai-3.2.0.dev20260902000321 → haystack_ai-3.2.0.dev20260903000331}/haystack/components/converters/csv.py +0 -0
- {haystack_ai-3.2.0.dev20260902000321 → haystack_ai-3.2.0.dev20260903000331}/haystack/components/converters/docx.py +0 -0
- {haystack_ai-3.2.0.dev20260902000321 → haystack_ai-3.2.0.dev20260903000331}/haystack/components/converters/file_to_file_content.py +0 -0
- {haystack_ai-3.2.0.dev20260902000321 → haystack_ai-3.2.0.dev20260903000331}/haystack/components/converters/html.py +0 -0
- {haystack_ai-3.2.0.dev20260902000321 → haystack_ai-3.2.0.dev20260903000331}/haystack/components/converters/image/__init__.py +0 -0
- {haystack_ai-3.2.0.dev20260902000321 → haystack_ai-3.2.0.dev20260903000331}/haystack/components/converters/image/document_to_image.py +0 -0
- {haystack_ai-3.2.0.dev20260902000321 → haystack_ai-3.2.0.dev20260903000331}/haystack/components/converters/image/file_to_document.py +0 -0
- {haystack_ai-3.2.0.dev20260902000321 → haystack_ai-3.2.0.dev20260903000331}/haystack/components/converters/image/file_to_image.py +0 -0
- {haystack_ai-3.2.0.dev20260902000321 → haystack_ai-3.2.0.dev20260903000331}/haystack/components/converters/image/image_utils.py +0 -0
- {haystack_ai-3.2.0.dev20260902000321 → haystack_ai-3.2.0.dev20260903000331}/haystack/components/converters/image/pdf_to_image.py +0 -0
- {haystack_ai-3.2.0.dev20260902000321 → haystack_ai-3.2.0.dev20260903000331}/haystack/components/converters/json.py +0 -0
- {haystack_ai-3.2.0.dev20260902000321 → haystack_ai-3.2.0.dev20260903000331}/haystack/components/converters/markdown.py +0 -0
- {haystack_ai-3.2.0.dev20260902000321 → haystack_ai-3.2.0.dev20260903000331}/haystack/components/converters/msg.py +0 -0
- {haystack_ai-3.2.0.dev20260902000321 → haystack_ai-3.2.0.dev20260903000331}/haystack/components/converters/multi_file_converter.py +0 -0
- {haystack_ai-3.2.0.dev20260902000321 → haystack_ai-3.2.0.dev20260903000331}/haystack/components/converters/pdfminer.py +0 -0
- {haystack_ai-3.2.0.dev20260902000321 → haystack_ai-3.2.0.dev20260903000331}/haystack/components/converters/pptx.py +0 -0
- {haystack_ai-3.2.0.dev20260902000321 → haystack_ai-3.2.0.dev20260903000331}/haystack/components/converters/pypdf.py +0 -0
- {haystack_ai-3.2.0.dev20260902000321 → haystack_ai-3.2.0.dev20260903000331}/haystack/components/converters/txt.py +0 -0
- {haystack_ai-3.2.0.dev20260902000321 → haystack_ai-3.2.0.dev20260903000331}/haystack/components/converters/utils.py +0 -0
- {haystack_ai-3.2.0.dev20260902000321 → haystack_ai-3.2.0.dev20260903000331}/haystack/components/converters/xlsx.py +0 -0
- {haystack_ai-3.2.0.dev20260902000321 → haystack_ai-3.2.0.dev20260903000331}/haystack/components/embedders/__init__.py +0 -0
- {haystack_ai-3.2.0.dev20260902000321 → haystack_ai-3.2.0.dev20260903000331}/haystack/components/embedders/azure_document_embedder.py +0 -0
- {haystack_ai-3.2.0.dev20260902000321 → haystack_ai-3.2.0.dev20260903000331}/haystack/components/embedders/azure_text_embedder.py +0 -0
- {haystack_ai-3.2.0.dev20260902000321 → haystack_ai-3.2.0.dev20260903000331}/haystack/components/embedders/mock_document_embedder.py +0 -0
- {haystack_ai-3.2.0.dev20260902000321 → haystack_ai-3.2.0.dev20260903000331}/haystack/components/embedders/mock_text_embedder.py +0 -0
- {haystack_ai-3.2.0.dev20260902000321 → haystack_ai-3.2.0.dev20260903000331}/haystack/components/embedders/mock_utils.py +0 -0
- {haystack_ai-3.2.0.dev20260902000321 → haystack_ai-3.2.0.dev20260903000331}/haystack/components/embedders/openai_document_embedder.py +0 -0
- {haystack_ai-3.2.0.dev20260902000321 → haystack_ai-3.2.0.dev20260903000331}/haystack/components/embedders/openai_text_embedder.py +0 -0
- {haystack_ai-3.2.0.dev20260902000321 → haystack_ai-3.2.0.dev20260903000331}/haystack/components/embedders/types/__init__.py +0 -0
- {haystack_ai-3.2.0.dev20260902000321 → haystack_ai-3.2.0.dev20260903000331}/haystack/components/embedders/types/protocol.py +0 -0
- {haystack_ai-3.2.0.dev20260902000321 → haystack_ai-3.2.0.dev20260903000331}/haystack/components/evaluators/__init__.py +0 -0
- {haystack_ai-3.2.0.dev20260902000321 → haystack_ai-3.2.0.dev20260903000331}/haystack/components/evaluators/answer_exact_match.py +0 -0
- {haystack_ai-3.2.0.dev20260902000321 → haystack_ai-3.2.0.dev20260903000331}/haystack/components/evaluators/context_relevance.py +0 -0
- {haystack_ai-3.2.0.dev20260902000321 → haystack_ai-3.2.0.dev20260903000331}/haystack/components/evaluators/document_map.py +0 -0
- {haystack_ai-3.2.0.dev20260902000321 → haystack_ai-3.2.0.dev20260903000331}/haystack/components/evaluators/document_mrr.py +0 -0
- {haystack_ai-3.2.0.dev20260902000321 → haystack_ai-3.2.0.dev20260903000331}/haystack/components/evaluators/document_ndcg.py +0 -0
- {haystack_ai-3.2.0.dev20260902000321 → haystack_ai-3.2.0.dev20260903000331}/haystack/components/evaluators/document_recall.py +0 -0
- {haystack_ai-3.2.0.dev20260902000321 → haystack_ai-3.2.0.dev20260903000331}/haystack/components/evaluators/faithfulness.py +0 -0
- {haystack_ai-3.2.0.dev20260902000321 → haystack_ai-3.2.0.dev20260903000331}/haystack/components/evaluators/llm_evaluator.py +0 -0
- {haystack_ai-3.2.0.dev20260902000321 → haystack_ai-3.2.0.dev20260903000331}/haystack/components/evaluators/sas_evaluator.py +0 -0
- {haystack_ai-3.2.0.dev20260902000321 → haystack_ai-3.2.0.dev20260903000331}/haystack/components/extractors/__init__.py +0 -0
- {haystack_ai-3.2.0.dev20260902000321 → haystack_ai-3.2.0.dev20260903000331}/haystack/components/extractors/image/__init__.py +0 -0
- {haystack_ai-3.2.0.dev20260902000321 → haystack_ai-3.2.0.dev20260903000331}/haystack/components/extractors/image/llm_document_content_extractor.py +0 -0
- {haystack_ai-3.2.0.dev20260902000321 → haystack_ai-3.2.0.dev20260903000331}/haystack/components/extractors/llm_metadata_extractor.py +0 -0
- {haystack_ai-3.2.0.dev20260902000321 → haystack_ai-3.2.0.dev20260903000331}/haystack/components/extractors/regex_text_extractor.py +0 -0
- {haystack_ai-3.2.0.dev20260902000321 → haystack_ai-3.2.0.dev20260903000331}/haystack/components/fetchers/__init__.py +0 -0
- {haystack_ai-3.2.0.dev20260902000321 → haystack_ai-3.2.0.dev20260903000331}/haystack/components/fetchers/link_content.py +0 -0
- {haystack_ai-3.2.0.dev20260902000321 → haystack_ai-3.2.0.dev20260903000331}/haystack/components/generators/__init__.py +0 -0
- {haystack_ai-3.2.0.dev20260902000321 → haystack_ai-3.2.0.dev20260903000331}/haystack/components/generators/chat/__init__.py +0 -0
- {haystack_ai-3.2.0.dev20260902000321 → haystack_ai-3.2.0.dev20260903000331}/haystack/components/generators/chat/azure.py +0 -0
- {haystack_ai-3.2.0.dev20260902000321 → haystack_ai-3.2.0.dev20260903000331}/haystack/components/generators/chat/azure_responses.py +0 -0
- {haystack_ai-3.2.0.dev20260902000321 → haystack_ai-3.2.0.dev20260903000331}/haystack/components/generators/chat/fallback.py +0 -0
- {haystack_ai-3.2.0.dev20260902000321 → haystack_ai-3.2.0.dev20260903000331}/haystack/components/generators/chat/llm.py +0 -0
- {haystack_ai-3.2.0.dev20260902000321 → haystack_ai-3.2.0.dev20260903000331}/haystack/components/generators/chat/mock.py +0 -0
- {haystack_ai-3.2.0.dev20260902000321 → haystack_ai-3.2.0.dev20260903000331}/haystack/components/generators/chat/openai.py +0 -0
- {haystack_ai-3.2.0.dev20260902000321 → haystack_ai-3.2.0.dev20260903000331}/haystack/components/generators/chat/types/__init__.py +0 -0
- {haystack_ai-3.2.0.dev20260902000321 → haystack_ai-3.2.0.dev20260903000331}/haystack/components/generators/chat/types/protocol.py +0 -0
- {haystack_ai-3.2.0.dev20260902000321 → haystack_ai-3.2.0.dev20260903000331}/haystack/components/generators/openai_image_generator.py +0 -0
- {haystack_ai-3.2.0.dev20260902000321 → haystack_ai-3.2.0.dev20260903000331}/haystack/components/generators/utils.py +0 -0
- {haystack_ai-3.2.0.dev20260902000321 → haystack_ai-3.2.0.dev20260903000331}/haystack/components/joiners/__init__.py +0 -0
- {haystack_ai-3.2.0.dev20260902000321 → haystack_ai-3.2.0.dev20260903000331}/haystack/components/joiners/answer_joiner.py +0 -0
- {haystack_ai-3.2.0.dev20260902000321 → haystack_ai-3.2.0.dev20260903000331}/haystack/components/joiners/branch.py +0 -0
- {haystack_ai-3.2.0.dev20260902000321 → haystack_ai-3.2.0.dev20260903000331}/haystack/components/joiners/document_joiner.py +0 -0
- {haystack_ai-3.2.0.dev20260902000321 → haystack_ai-3.2.0.dev20260903000331}/haystack/components/joiners/list_joiner.py +0 -0
- {haystack_ai-3.2.0.dev20260902000321 → haystack_ai-3.2.0.dev20260903000331}/haystack/components/joiners/string_joiner.py +0 -0
- {haystack_ai-3.2.0.dev20260902000321 → haystack_ai-3.2.0.dev20260903000331}/haystack/components/preprocessors/__init__.py +0 -0
- {haystack_ai-3.2.0.dev20260902000321 → haystack_ai-3.2.0.dev20260903000331}/haystack/components/preprocessors/csv_document_cleaner.py +0 -0
- {haystack_ai-3.2.0.dev20260902000321 → haystack_ai-3.2.0.dev20260903000331}/haystack/components/preprocessors/csv_document_splitter.py +0 -0
- {haystack_ai-3.2.0.dev20260902000321 → haystack_ai-3.2.0.dev20260903000331}/haystack/components/preprocessors/document_cleaner.py +0 -0
- {haystack_ai-3.2.0.dev20260902000321 → haystack_ai-3.2.0.dev20260903000331}/haystack/components/preprocessors/document_preprocessor.py +0 -0
- {haystack_ai-3.2.0.dev20260902000321 → haystack_ai-3.2.0.dev20260903000331}/haystack/components/preprocessors/document_splitter.py +0 -0
- {haystack_ai-3.2.0.dev20260902000321 → haystack_ai-3.2.0.dev20260903000331}/haystack/components/preprocessors/embedding_based_document_splitter.py +0 -0
- {haystack_ai-3.2.0.dev20260902000321 → haystack_ai-3.2.0.dev20260903000331}/haystack/components/preprocessors/hierarchical_document_splitter.py +0 -0
- {haystack_ai-3.2.0.dev20260902000321 → haystack_ai-3.2.0.dev20260903000331}/haystack/components/preprocessors/markdown_header_splitter.py +0 -0
- {haystack_ai-3.2.0.dev20260902000321 → haystack_ai-3.2.0.dev20260903000331}/haystack/components/preprocessors/python_code_splitter.py +0 -0
- {haystack_ai-3.2.0.dev20260902000321 → haystack_ai-3.2.0.dev20260903000331}/haystack/components/preprocessors/sentence_tokenizer.py +0 -0
- {haystack_ai-3.2.0.dev20260902000321 → haystack_ai-3.2.0.dev20260903000331}/haystack/components/preprocessors/text_cleaner.py +0 -0
- {haystack_ai-3.2.0.dev20260902000321 → haystack_ai-3.2.0.dev20260903000331}/haystack/components/query/__init__.py +0 -0
- {haystack_ai-3.2.0.dev20260902000321 → haystack_ai-3.2.0.dev20260903000331}/haystack/components/query/query_expander.py +0 -0
- {haystack_ai-3.2.0.dev20260902000321 → haystack_ai-3.2.0.dev20260903000331}/haystack/components/rankers/__init__.py +0 -0
- {haystack_ai-3.2.0.dev20260902000321 → haystack_ai-3.2.0.dev20260903000331}/haystack/components/rankers/llm_ranker.py +0 -0
- {haystack_ai-3.2.0.dev20260902000321 → haystack_ai-3.2.0.dev20260903000331}/haystack/components/rankers/lost_in_the_middle.py +0 -0
- {haystack_ai-3.2.0.dev20260902000321 → haystack_ai-3.2.0.dev20260903000331}/haystack/components/rankers/meta_field.py +0 -0
- {haystack_ai-3.2.0.dev20260902000321 → haystack_ai-3.2.0.dev20260903000331}/haystack/components/rankers/meta_field_grouping_ranker.py +0 -0
- {haystack_ai-3.2.0.dev20260902000321 → haystack_ai-3.2.0.dev20260903000331}/haystack/components/retrievers/__init__.py +0 -0
- {haystack_ai-3.2.0.dev20260902000321 → haystack_ai-3.2.0.dev20260903000331}/haystack/components/retrievers/auto_merging_retriever.py +0 -0
- {haystack_ai-3.2.0.dev20260902000321 → haystack_ai-3.2.0.dev20260903000331}/haystack/components/retrievers/filter_retriever.py +0 -0
- {haystack_ai-3.2.0.dev20260902000321 → haystack_ai-3.2.0.dev20260903000331}/haystack/components/retrievers/in_memory/__init__.py +0 -0
- {haystack_ai-3.2.0.dev20260902000321 → haystack_ai-3.2.0.dev20260903000331}/haystack/components/retrievers/in_memory/bm25_retriever.py +0 -0
- {haystack_ai-3.2.0.dev20260902000321 → haystack_ai-3.2.0.dev20260903000331}/haystack/components/retrievers/in_memory/embedding_retriever.py +0 -0
- {haystack_ai-3.2.0.dev20260902000321 → haystack_ai-3.2.0.dev20260903000331}/haystack/components/retrievers/multi_query_embedding_retriever.py +0 -0
- {haystack_ai-3.2.0.dev20260902000321 → haystack_ai-3.2.0.dev20260903000331}/haystack/components/retrievers/multi_query_text_retriever.py +0 -0
- {haystack_ai-3.2.0.dev20260902000321 → haystack_ai-3.2.0.dev20260903000331}/haystack/components/retrievers/multi_retriever.py +0 -0
- {haystack_ai-3.2.0.dev20260902000321 → haystack_ai-3.2.0.dev20260903000331}/haystack/components/retrievers/sentence_window_retriever.py +0 -0
- {haystack_ai-3.2.0.dev20260902000321 → haystack_ai-3.2.0.dev20260903000331}/haystack/components/retrievers/text_embedding_retriever.py +0 -0
- {haystack_ai-3.2.0.dev20260902000321 → haystack_ai-3.2.0.dev20260903000331}/haystack/components/retrievers/types/__init__.py +0 -0
- {haystack_ai-3.2.0.dev20260902000321 → haystack_ai-3.2.0.dev20260903000331}/haystack/components/retrievers/types/protocol.py +0 -0
- {haystack_ai-3.2.0.dev20260902000321 → haystack_ai-3.2.0.dev20260903000331}/haystack/components/routers/__init__.py +0 -0
- {haystack_ai-3.2.0.dev20260902000321 → haystack_ai-3.2.0.dev20260903000331}/haystack/components/routers/document_length_router.py +0 -0
- {haystack_ai-3.2.0.dev20260902000321 → haystack_ai-3.2.0.dev20260903000331}/haystack/components/routers/document_type_router.py +0 -0
- {haystack_ai-3.2.0.dev20260902000321 → haystack_ai-3.2.0.dev20260903000331}/haystack/components/routers/file_type_router.py +0 -0
- {haystack_ai-3.2.0.dev20260902000321 → haystack_ai-3.2.0.dev20260903000331}/haystack/components/routers/llm_messages_router.py +0 -0
- {haystack_ai-3.2.0.dev20260902000321 → haystack_ai-3.2.0.dev20260903000331}/haystack/components/routers/metadata_router.py +0 -0
- {haystack_ai-3.2.0.dev20260902000321 → haystack_ai-3.2.0.dev20260903000331}/haystack/components/samplers/__init__.py +0 -0
- {haystack_ai-3.2.0.dev20260902000321 → haystack_ai-3.2.0.dev20260903000331}/haystack/components/samplers/top_p.py +0 -0
- {haystack_ai-3.2.0.dev20260902000321 → haystack_ai-3.2.0.dev20260903000331}/haystack/components/validators/__init__.py +0 -0
- {haystack_ai-3.2.0.dev20260902000321 → haystack_ai-3.2.0.dev20260903000331}/haystack/components/validators/json_schema.py +0 -0
- {haystack_ai-3.2.0.dev20260902000321 → haystack_ai-3.2.0.dev20260903000331}/haystack/components/writers/__init__.py +0 -0
- {haystack_ai-3.2.0.dev20260902000321 → haystack_ai-3.2.0.dev20260903000331}/haystack/components/writers/document_writer.py +0 -0
- {haystack_ai-3.2.0.dev20260902000321 → haystack_ai-3.2.0.dev20260903000331}/haystack/core/__init__.py +0 -0
- {haystack_ai-3.2.0.dev20260902000321 → haystack_ai-3.2.0.dev20260903000331}/haystack/core/component/__init__.py +0 -0
- {haystack_ai-3.2.0.dev20260902000321 → haystack_ai-3.2.0.dev20260903000331}/haystack/core/component/component.py +0 -0
- {haystack_ai-3.2.0.dev20260902000321 → haystack_ai-3.2.0.dev20260903000331}/haystack/core/component/sockets.py +0 -0
- {haystack_ai-3.2.0.dev20260902000321 → haystack_ai-3.2.0.dev20260903000331}/haystack/core/component/types.py +0 -0
- {haystack_ai-3.2.0.dev20260902000321 → haystack_ai-3.2.0.dev20260903000331}/haystack/core/errors.py +0 -0
- {haystack_ai-3.2.0.dev20260902000321 → haystack_ai-3.2.0.dev20260903000331}/haystack/core/pipeline/__init__.py +0 -0
- {haystack_ai-3.2.0.dev20260902000321 → haystack_ai-3.2.0.dev20260903000331}/haystack/core/pipeline/base.py +0 -0
- {haystack_ai-3.2.0.dev20260902000321 → haystack_ai-3.2.0.dev20260903000331}/haystack/core/pipeline/breakpoint.py +0 -0
- {haystack_ai-3.2.0.dev20260902000321 → haystack_ai-3.2.0.dev20260903000331}/haystack/core/pipeline/component_checks.py +0 -0
- {haystack_ai-3.2.0.dev20260902000321 → haystack_ai-3.2.0.dev20260903000331}/haystack/core/pipeline/descriptions.py +0 -0
- {haystack_ai-3.2.0.dev20260902000321 → haystack_ai-3.2.0.dev20260903000331}/haystack/core/pipeline/draw.py +0 -0
- {haystack_ai-3.2.0.dev20260902000321 → haystack_ai-3.2.0.dev20260903000331}/haystack/core/pipeline/pipeline.py +0 -0
- {haystack_ai-3.2.0.dev20260902000321 → haystack_ai-3.2.0.dev20260903000331}/haystack/core/pipeline/utils.py +0 -0
- {haystack_ai-3.2.0.dev20260902000321 → haystack_ai-3.2.0.dev20260903000331}/haystack/core/serialization.py +0 -0
- {haystack_ai-3.2.0.dev20260902000321 → haystack_ai-3.2.0.dev20260903000331}/haystack/core/serialization_security.py +0 -0
- {haystack_ai-3.2.0.dev20260902000321 → haystack_ai-3.2.0.dev20260903000331}/haystack/core/super_component/__init__.py +0 -0
- {haystack_ai-3.2.0.dev20260902000321 → haystack_ai-3.2.0.dev20260903000331}/haystack/core/super_component/super_component.py +0 -0
- {haystack_ai-3.2.0.dev20260902000321 → haystack_ai-3.2.0.dev20260903000331}/haystack/core/super_component/utils.py +0 -0
- {haystack_ai-3.2.0.dev20260902000321 → haystack_ai-3.2.0.dev20260903000331}/haystack/core/type_utils.py +0 -0
- {haystack_ai-3.2.0.dev20260902000321 → haystack_ai-3.2.0.dev20260903000331}/haystack/data/abbreviations/de.txt +0 -0
- {haystack_ai-3.2.0.dev20260902000321 → haystack_ai-3.2.0.dev20260903000331}/haystack/data/abbreviations/en.txt +0 -0
- {haystack_ai-3.2.0.dev20260902000321 → haystack_ai-3.2.0.dev20260903000331}/haystack/dataclasses/__init__.py +0 -0
- {haystack_ai-3.2.0.dev20260902000321 → haystack_ai-3.2.0.dev20260903000331}/haystack/dataclasses/answer.py +0 -0
- {haystack_ai-3.2.0.dev20260902000321 → haystack_ai-3.2.0.dev20260903000331}/haystack/dataclasses/breakpoints.py +0 -0
- {haystack_ai-3.2.0.dev20260902000321 → haystack_ai-3.2.0.dev20260903000331}/haystack/dataclasses/byte_stream.py +0 -0
- {haystack_ai-3.2.0.dev20260902000321 → haystack_ai-3.2.0.dev20260903000331}/haystack/dataclasses/document.py +0 -0
- {haystack_ai-3.2.0.dev20260902000321 → haystack_ai-3.2.0.dev20260903000331}/haystack/dataclasses/file_content.py +0 -0
- {haystack_ai-3.2.0.dev20260902000321 → haystack_ai-3.2.0.dev20260903000331}/haystack/dataclasses/image_content.py +0 -0
- {haystack_ai-3.2.0.dev20260902000321 → haystack_ai-3.2.0.dev20260903000331}/haystack/dataclasses/skill_info.py +0 -0
- {haystack_ai-3.2.0.dev20260902000321 → haystack_ai-3.2.0.dev20260903000331}/haystack/dataclasses/sparse_embedding.py +0 -0
- {haystack_ai-3.2.0.dev20260902000321 → haystack_ai-3.2.0.dev20260903000331}/haystack/dataclasses/streaming_chunk.py +0 -0
- {haystack_ai-3.2.0.dev20260902000321 → haystack_ai-3.2.0.dev20260903000331}/haystack/document_stores/__init__.py +0 -0
- {haystack_ai-3.2.0.dev20260902000321 → haystack_ai-3.2.0.dev20260903000331}/haystack/document_stores/errors/__init__.py +0 -0
- {haystack_ai-3.2.0.dev20260902000321 → haystack_ai-3.2.0.dev20260903000331}/haystack/document_stores/errors/errors.py +0 -0
- {haystack_ai-3.2.0.dev20260902000321 → haystack_ai-3.2.0.dev20260903000331}/haystack/document_stores/in_memory/__init__.py +0 -0
- {haystack_ai-3.2.0.dev20260902000321 → haystack_ai-3.2.0.dev20260903000331}/haystack/document_stores/in_memory/document_store.py +0 -0
- {haystack_ai-3.2.0.dev20260902000321 → haystack_ai-3.2.0.dev20260903000331}/haystack/document_stores/types/__init__.py +0 -0
- {haystack_ai-3.2.0.dev20260902000321 → haystack_ai-3.2.0.dev20260903000331}/haystack/document_stores/types/filter_policy.py +0 -0
- {haystack_ai-3.2.0.dev20260902000321 → haystack_ai-3.2.0.dev20260903000331}/haystack/document_stores/types/policy.py +0 -0
- {haystack_ai-3.2.0.dev20260902000321 → haystack_ai-3.2.0.dev20260903000331}/haystack/document_stores/types/protocol.py +0 -0
- {haystack_ai-3.2.0.dev20260902000321 → haystack_ai-3.2.0.dev20260903000331}/haystack/errors.py +0 -0
- {haystack_ai-3.2.0.dev20260902000321 → haystack_ai-3.2.0.dev20260903000331}/haystack/evaluation/__init__.py +0 -0
- {haystack_ai-3.2.0.dev20260902000321 → haystack_ai-3.2.0.dev20260903000331}/haystack/evaluation/eval_run_result.py +0 -0
- {haystack_ai-3.2.0.dev20260902000321 → haystack_ai-3.2.0.dev20260903000331}/haystack/hooks/__init__.py +0 -0
- {haystack_ai-3.2.0.dev20260902000321 → haystack_ai-3.2.0.dev20260903000331}/haystack/hooks/budget/__init__.py +0 -0
- {haystack_ai-3.2.0.dev20260902000321 → haystack_ai-3.2.0.dev20260903000331}/haystack/hooks/budget/hooks.py +0 -0
- {haystack_ai-3.2.0.dev20260902000321 → haystack_ai-3.2.0.dev20260903000331}/haystack/hooks/compaction/__init__.py +0 -0
- {haystack_ai-3.2.0.dev20260902000321 → haystack_ai-3.2.0.dev20260903000331}/haystack/hooks/compaction/hooks.py +0 -0
- {haystack_ai-3.2.0.dev20260902000321 → haystack_ai-3.2.0.dev20260903000331}/haystack/hooks/compaction/sliding_window.py +0 -0
- {haystack_ai-3.2.0.dev20260902000321 → haystack_ai-3.2.0.dev20260903000331}/haystack/hooks/compaction/summarization.py +0 -0
- {haystack_ai-3.2.0.dev20260902000321 → haystack_ai-3.2.0.dev20260903000331}/haystack/hooks/compaction/tool_result_pruning.py +0 -0
- {haystack_ai-3.2.0.dev20260902000321 → haystack_ai-3.2.0.dev20260903000331}/haystack/hooks/compaction/types/__init__.py +0 -0
- {haystack_ai-3.2.0.dev20260902000321 → haystack_ai-3.2.0.dev20260903000331}/haystack/hooks/compaction/types/protocol.py +0 -0
- {haystack_ai-3.2.0.dev20260902000321 → haystack_ai-3.2.0.dev20260903000331}/haystack/hooks/compaction/utils.py +0 -0
- {haystack_ai-3.2.0.dev20260902000321 → haystack_ai-3.2.0.dev20260903000331}/haystack/hooks/from_function.py +0 -0
- {haystack_ai-3.2.0.dev20260902000321 → haystack_ai-3.2.0.dev20260903000331}/haystack/hooks/human_in_the_loop/__init__.py +0 -0
- {haystack_ai-3.2.0.dev20260902000321 → haystack_ai-3.2.0.dev20260903000331}/haystack/hooks/human_in_the_loop/dataclasses.py +0 -0
- {haystack_ai-3.2.0.dev20260902000321 → haystack_ai-3.2.0.dev20260903000331}/haystack/hooks/human_in_the_loop/hooks.py +0 -0
- {haystack_ai-3.2.0.dev20260902000321 → haystack_ai-3.2.0.dev20260903000331}/haystack/hooks/human_in_the_loop/policies.py +0 -0
- {haystack_ai-3.2.0.dev20260902000321 → haystack_ai-3.2.0.dev20260903000331}/haystack/hooks/human_in_the_loop/strategies.py +0 -0
- {haystack_ai-3.2.0.dev20260902000321 → haystack_ai-3.2.0.dev20260903000331}/haystack/hooks/human_in_the_loop/types/__init__.py +0 -0
- {haystack_ai-3.2.0.dev20260902000321 → haystack_ai-3.2.0.dev20260903000331}/haystack/hooks/human_in_the_loop/types/protocol.py +0 -0
- {haystack_ai-3.2.0.dev20260902000321 → haystack_ai-3.2.0.dev20260903000331}/haystack/hooks/human_in_the_loop/user_interfaces.py +0 -0
- {haystack_ai-3.2.0.dev20260902000321 → haystack_ai-3.2.0.dev20260903000331}/haystack/hooks/invocation.py +0 -0
- {haystack_ai-3.2.0.dev20260902000321 → haystack_ai-3.2.0.dev20260903000331}/haystack/hooks/protocol.py +0 -0
- {haystack_ai-3.2.0.dev20260902000321 → haystack_ai-3.2.0.dev20260903000331}/haystack/hooks/tool_result_offloading/__init__.py +0 -0
- {haystack_ai-3.2.0.dev20260902000321 → haystack_ai-3.2.0.dev20260903000331}/haystack/hooks/tool_result_offloading/policies.py +0 -0
- {haystack_ai-3.2.0.dev20260902000321 → haystack_ai-3.2.0.dev20260903000331}/haystack/hooks/tool_result_offloading/types/__init__.py +0 -0
- {haystack_ai-3.2.0.dev20260902000321 → haystack_ai-3.2.0.dev20260903000331}/haystack/hooks/utils.py +0 -0
- {haystack_ai-3.2.0.dev20260902000321 → haystack_ai-3.2.0.dev20260903000331}/haystack/lazy_imports.py +0 -0
- {haystack_ai-3.2.0.dev20260902000321 → haystack_ai-3.2.0.dev20260903000331}/haystack/logging.py +0 -0
- {haystack_ai-3.2.0.dev20260902000321 → haystack_ai-3.2.0.dev20260903000331}/haystack/marshal/__init__.py +0 -0
- {haystack_ai-3.2.0.dev20260902000321 → haystack_ai-3.2.0.dev20260903000331}/haystack/marshal/protocol.py +0 -0
- {haystack_ai-3.2.0.dev20260902000321 → haystack_ai-3.2.0.dev20260903000331}/haystack/marshal/yaml.py +0 -0
- {haystack_ai-3.2.0.dev20260902000321 → haystack_ai-3.2.0.dev20260903000331}/haystack/py.typed +0 -0
- {haystack_ai-3.2.0.dev20260902000321 → haystack_ai-3.2.0.dev20260903000331}/haystack/skill_stores/__init__.py +0 -0
- {haystack_ai-3.2.0.dev20260902000321 → haystack_ai-3.2.0.dev20260903000331}/haystack/skill_stores/file_system/__init__.py +0 -0
- {haystack_ai-3.2.0.dev20260902000321 → haystack_ai-3.2.0.dev20260903000331}/haystack/skill_stores/file_system/skill_store.py +0 -0
- {haystack_ai-3.2.0.dev20260902000321 → haystack_ai-3.2.0.dev20260903000331}/haystack/skill_stores/types/__init__.py +0 -0
- {haystack_ai-3.2.0.dev20260902000321 → haystack_ai-3.2.0.dev20260903000331}/haystack/skill_stores/types/protocol.py +0 -0
- {haystack_ai-3.2.0.dev20260902000321 → haystack_ai-3.2.0.dev20260903000331}/haystack/telemetry/__init__.py +0 -0
- {haystack_ai-3.2.0.dev20260902000321 → haystack_ai-3.2.0.dev20260903000331}/haystack/telemetry/_environment.py +0 -0
- {haystack_ai-3.2.0.dev20260902000321 → haystack_ai-3.2.0.dev20260903000331}/haystack/telemetry/_telemetry.py +0 -0
- {haystack_ai-3.2.0.dev20260902000321 → haystack_ai-3.2.0.dev20260903000331}/haystack/testing/__init__.py +0 -0
- {haystack_ai-3.2.0.dev20260902000321 → haystack_ai-3.2.0.dev20260903000331}/haystack/testing/callable_serialization/random_callable.py +0 -0
- {haystack_ai-3.2.0.dev20260902000321 → haystack_ai-3.2.0.dev20260903000331}/haystack/testing/document_store.py +0 -0
- {haystack_ai-3.2.0.dev20260902000321 → haystack_ai-3.2.0.dev20260903000331}/haystack/testing/document_store_async.py +0 -0
- {haystack_ai-3.2.0.dev20260902000321 → haystack_ai-3.2.0.dev20260903000331}/haystack/testing/factory.py +0 -0
- {haystack_ai-3.2.0.dev20260902000321 → haystack_ai-3.2.0.dev20260903000331}/haystack/testing/sample_components/__init__.py +0 -0
- {haystack_ai-3.2.0.dev20260902000321 → haystack_ai-3.2.0.dev20260903000331}/haystack/testing/sample_components/accumulate.py +0 -0
- {haystack_ai-3.2.0.dev20260902000321 → haystack_ai-3.2.0.dev20260903000331}/haystack/testing/sample_components/add_value.py +0 -0
- {haystack_ai-3.2.0.dev20260902000321 → haystack_ai-3.2.0.dev20260903000331}/haystack/testing/sample_components/concatenate.py +0 -0
- {haystack_ai-3.2.0.dev20260902000321 → haystack_ai-3.2.0.dev20260903000331}/haystack/testing/sample_components/double.py +0 -0
- {haystack_ai-3.2.0.dev20260902000321 → haystack_ai-3.2.0.dev20260903000331}/haystack/testing/sample_components/fstring.py +0 -0
- {haystack_ai-3.2.0.dev20260902000321 → haystack_ai-3.2.0.dev20260903000331}/haystack/testing/sample_components/future_annotations.py +0 -0
- {haystack_ai-3.2.0.dev20260902000321 → haystack_ai-3.2.0.dev20260903000331}/haystack/testing/sample_components/greet.py +0 -0
- {haystack_ai-3.2.0.dev20260902000321 → haystack_ai-3.2.0.dev20260903000331}/haystack/testing/sample_components/hello.py +0 -0
- {haystack_ai-3.2.0.dev20260902000321 → haystack_ai-3.2.0.dev20260903000331}/haystack/testing/sample_components/joiner.py +0 -0
- {haystack_ai-3.2.0.dev20260902000321 → haystack_ai-3.2.0.dev20260903000331}/haystack/testing/sample_components/parity.py +0 -0
- {haystack_ai-3.2.0.dev20260902000321 → haystack_ai-3.2.0.dev20260903000331}/haystack/testing/sample_components/remainder.py +0 -0
- {haystack_ai-3.2.0.dev20260902000321 → haystack_ai-3.2.0.dev20260903000331}/haystack/testing/sample_components/repeat.py +0 -0
- {haystack_ai-3.2.0.dev20260902000321 → haystack_ai-3.2.0.dev20260903000331}/haystack/testing/sample_components/subtract.py +0 -0
- {haystack_ai-3.2.0.dev20260902000321 → haystack_ai-3.2.0.dev20260903000331}/haystack/testing/sample_components/sum.py +0 -0
- {haystack_ai-3.2.0.dev20260902000321 → haystack_ai-3.2.0.dev20260903000331}/haystack/testing/sample_components/text_splitter.py +0 -0
- {haystack_ai-3.2.0.dev20260902000321 → haystack_ai-3.2.0.dev20260903000331}/haystack/testing/sample_components/threshold.py +0 -0
- {haystack_ai-3.2.0.dev20260902000321 → haystack_ai-3.2.0.dev20260903000331}/haystack/testing/test_utils.py +0 -0
- {haystack_ai-3.2.0.dev20260902000321 → haystack_ai-3.2.0.dev20260903000331}/haystack/token_counters/__init__.py +0 -0
- {haystack_ai-3.2.0.dev20260902000321 → haystack_ai-3.2.0.dev20260903000331}/haystack/token_counters/approximate_counter.py +0 -0
- {haystack_ai-3.2.0.dev20260902000321 → haystack_ai-3.2.0.dev20260903000331}/haystack/token_counters/openai_counter.py +0 -0
- {haystack_ai-3.2.0.dev20260902000321 → haystack_ai-3.2.0.dev20260903000331}/haystack/token_counters/tiktoken_counter.py +0 -0
- {haystack_ai-3.2.0.dev20260902000321 → haystack_ai-3.2.0.dev20260903000331}/haystack/token_counters/types/__init__.py +0 -0
- {haystack_ai-3.2.0.dev20260902000321 → haystack_ai-3.2.0.dev20260903000331}/haystack/token_counters/types/protocol.py +0 -0
- {haystack_ai-3.2.0.dev20260902000321 → haystack_ai-3.2.0.dev20260903000331}/haystack/token_counters/utils.py +0 -0
- {haystack_ai-3.2.0.dev20260902000321 → haystack_ai-3.2.0.dev20260903000331}/haystack/tools/__init__.py +0 -0
- {haystack_ai-3.2.0.dev20260902000321 → haystack_ai-3.2.0.dev20260903000331}/haystack/tools/agent_tool.py +0 -0
- {haystack_ai-3.2.0.dev20260902000321 → haystack_ai-3.2.0.dev20260903000331}/haystack/tools/component_tool.py +0 -0
- {haystack_ai-3.2.0.dev20260902000321 → haystack_ai-3.2.0.dev20260903000331}/haystack/tools/errors.py +0 -0
- {haystack_ai-3.2.0.dev20260902000321 → haystack_ai-3.2.0.dev20260903000331}/haystack/tools/from_function.py +0 -0
- {haystack_ai-3.2.0.dev20260902000321 → haystack_ai-3.2.0.dev20260903000331}/haystack/tools/parameters_schema_utils.py +0 -0
- {haystack_ai-3.2.0.dev20260902000321 → haystack_ai-3.2.0.dev20260903000331}/haystack/tools/pipeline_tool.py +0 -0
- {haystack_ai-3.2.0.dev20260902000321 → haystack_ai-3.2.0.dev20260903000331}/haystack/tools/searchable_toolset.py +0 -0
- {haystack_ai-3.2.0.dev20260902000321 → haystack_ai-3.2.0.dev20260903000331}/haystack/tools/serde_utils.py +0 -0
- {haystack_ai-3.2.0.dev20260902000321 → haystack_ai-3.2.0.dev20260903000331}/haystack/tools/skills/__init__.py +0 -0
- {haystack_ai-3.2.0.dev20260902000321 → haystack_ai-3.2.0.dev20260903000331}/haystack/tools/skills/skill_toolset.py +0 -0
- {haystack_ai-3.2.0.dev20260902000321 → haystack_ai-3.2.0.dev20260903000331}/haystack/tools/tool.py +0 -0
- {haystack_ai-3.2.0.dev20260902000321 → haystack_ai-3.2.0.dev20260903000331}/haystack/tools/tool_types.py +0 -0
- {haystack_ai-3.2.0.dev20260902000321 → haystack_ai-3.2.0.dev20260903000331}/haystack/tools/toolset.py +0 -0
- {haystack_ai-3.2.0.dev20260902000321 → haystack_ai-3.2.0.dev20260903000331}/haystack/tools/utils.py +0 -0
- {haystack_ai-3.2.0.dev20260902000321 → haystack_ai-3.2.0.dev20260903000331}/haystack/tracing/__init__.py +0 -0
- {haystack_ai-3.2.0.dev20260902000321 → haystack_ai-3.2.0.dev20260903000331}/haystack/tracing/logging_tracer.py +0 -0
- {haystack_ai-3.2.0.dev20260902000321 → haystack_ai-3.2.0.dev20260903000331}/haystack/tracing/tracer.py +0 -0
- {haystack_ai-3.2.0.dev20260902000321 → haystack_ai-3.2.0.dev20260903000331}/haystack/tracing/utils.py +0 -0
- {haystack_ai-3.2.0.dev20260902000321 → haystack_ai-3.2.0.dev20260903000331}/haystack/utils/__init__.py +0 -0
- {haystack_ai-3.2.0.dev20260902000321 → haystack_ai-3.2.0.dev20260903000331}/haystack/utils/async_utils.py +0 -0
- {haystack_ai-3.2.0.dev20260902000321 → haystack_ai-3.2.0.dev20260903000331}/haystack/utils/auth.py +0 -0
- {haystack_ai-3.2.0.dev20260902000321 → haystack_ai-3.2.0.dev20260903000331}/haystack/utils/azure.py +0 -0
- {haystack_ai-3.2.0.dev20260902000321 → haystack_ai-3.2.0.dev20260903000331}/haystack/utils/base_serialization.py +0 -0
- {haystack_ai-3.2.0.dev20260902000321 → haystack_ai-3.2.0.dev20260903000331}/haystack/utils/callable_serialization.py +0 -0
- {haystack_ai-3.2.0.dev20260902000321 → haystack_ai-3.2.0.dev20260903000331}/haystack/utils/dataclasses.py +0 -0
- {haystack_ai-3.2.0.dev20260902000321 → haystack_ai-3.2.0.dev20260903000331}/haystack/utils/deserialization.py +0 -0
- {haystack_ai-3.2.0.dev20260902000321 → haystack_ai-3.2.0.dev20260903000331}/haystack/utils/device.py +0 -0
- {haystack_ai-3.2.0.dev20260902000321 → haystack_ai-3.2.0.dev20260903000331}/haystack/utils/experimental.py +0 -0
- {haystack_ai-3.2.0.dev20260902000321 → haystack_ai-3.2.0.dev20260903000331}/haystack/utils/filters.py +0 -0
- {haystack_ai-3.2.0.dev20260902000321 → haystack_ai-3.2.0.dev20260903000331}/haystack/utils/http_client.py +0 -0
- {haystack_ai-3.2.0.dev20260902000321 → haystack_ai-3.2.0.dev20260903000331}/haystack/utils/jinja2_chat_extension.py +0 -0
- {haystack_ai-3.2.0.dev20260902000321 → haystack_ai-3.2.0.dev20260903000331}/haystack/utils/jinja2_extensions.py +0 -0
- {haystack_ai-3.2.0.dev20260902000321 → haystack_ai-3.2.0.dev20260903000331}/haystack/utils/jinja2_sandbox.py +0 -0
- {haystack_ai-3.2.0.dev20260902000321 → haystack_ai-3.2.0.dev20260903000331}/haystack/utils/jupyter.py +0 -0
- {haystack_ai-3.2.0.dev20260902000321 → haystack_ai-3.2.0.dev20260903000331}/haystack/utils/misc.py +0 -0
- {haystack_ai-3.2.0.dev20260902000321 → haystack_ai-3.2.0.dev20260903000331}/haystack/utils/requests_utils.py +0 -0
- {haystack_ai-3.2.0.dev20260902000321 → haystack_ai-3.2.0.dev20260903000331}/haystack/utils/type_serialization.py +0 -0
- {haystack_ai-3.2.0.dev20260902000321 → haystack_ai-3.2.0.dev20260903000331}/haystack/utils/url_validation.py +0 -0
- {haystack_ai-3.2.0.dev20260902000321 → haystack_ai-3.2.0.dev20260903000331}/haystack/version.py +0 -0
- {haystack_ai-3.2.0.dev20260902000321 → haystack_ai-3.2.0.dev20260903000331}/pyproject.toml +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: haystack-ai
|
|
3
|
-
Version: 3.2.0.
|
|
3
|
+
Version: 3.2.0.dev20260903000331
|
|
4
4
|
Summary: LLM framework to build customizable, production-ready LLM applications. Connect components (models, vector DBs, file converters) to pipelines or agents that can interact with your data.
|
|
5
5
|
Project-URL: CI: GitHub, https://github.com/deepset-ai/haystack/actions
|
|
6
6
|
Project-URL: Docs: RTD, https://haystack.deepset.ai/overview/intro
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
3.2.0.dev20260903000331
|
|
@@ -136,8 +136,12 @@ class OutputAdapter:
|
|
|
136
136
|
# we try to evaluate it and would fail.
|
|
137
137
|
# This must be done cause the output could be different literal structures.
|
|
138
138
|
# This doesn't support any user types.
|
|
139
|
+
# When the declared output_type is str we skip literal evaluation so that a
|
|
140
|
+
# rendered string that happens to be a valid Python literal (e.g. "1,000" -> (1, 0),
|
|
141
|
+
# "42" -> 42, "None" -> None) is returned unchanged instead of being coerced to
|
|
142
|
+
# another type, which would violate the declared output_type.
|
|
139
143
|
with contextlib.suppress(Exception):
|
|
140
|
-
if not self._unsafe:
|
|
144
|
+
if not self._unsafe and self.output_type is not str:
|
|
141
145
|
output_result = ast.literal_eval(output_result)
|
|
142
146
|
|
|
143
147
|
adapted_outputs["output"] = output_result
|
|
@@ -16,6 +16,7 @@ from haystack import component, default_from_dict, default_to_dict, logging
|
|
|
16
16
|
from haystack.components.generators.utils import _normalize_messages, _serialize_object
|
|
17
17
|
from haystack.dataclasses import (
|
|
18
18
|
ChatMessage,
|
|
19
|
+
ChatRole,
|
|
19
20
|
ComponentInfo,
|
|
20
21
|
FileContent,
|
|
21
22
|
ImageContent,
|
|
@@ -992,10 +993,13 @@ def _convert_chat_message_to_responses_api_format(message: ChatMessage) -> list[
|
|
|
992
993
|
reasonings = message.reasonings
|
|
993
994
|
files = message.files
|
|
994
995
|
|
|
995
|
-
|
|
996
|
+
has_content = any([text_contents, tool_calls, tool_call_results, images, reasonings, files])
|
|
997
|
+
# We convert an assistant message with no content part into a message with empty content, which the API accepts
|
|
998
|
+
if not has_content and not message.is_from(ChatRole.ASSISTANT):
|
|
996
999
|
raise ValueError(
|
|
997
|
-
"
|
|
998
|
-
|
|
1000
|
+
f"A `ChatMessage` from `{message._role.value}` must contain at least one `TextContent`, `ToolCall`, "
|
|
1001
|
+
"`ToolCallResult`, `ImageContent`, `FileContent`, or `ReasoningContent`. Only assistant messages can "
|
|
1002
|
+
"be empty."
|
|
999
1003
|
)
|
|
1000
1004
|
if len(tool_call_results) > 0 and len(message._content) > 1:
|
|
1001
1005
|
raise ValueError(
|
|
@@ -1068,7 +1072,7 @@ def _convert_chat_message_to_responses_api_format(message: ChatMessage) -> list[
|
|
|
1068
1072
|
formatted_messages.extend(formatted_tool_calls)
|
|
1069
1073
|
|
|
1070
1074
|
# system and assistant messages
|
|
1071
|
-
if text_contents:
|
|
1075
|
+
if text_contents or not formatted_messages:
|
|
1072
1076
|
openai_msg["content"] = " ".join(text_contents)
|
|
1073
1077
|
formatted_messages.append(openai_msg)
|
|
1074
1078
|
|
|
@@ -350,17 +350,11 @@ class RecursiveDocumentSplitter:
|
|
|
350
350
|
if current_chunk:
|
|
351
351
|
chunks.append("".join(current_chunk))
|
|
352
352
|
|
|
353
|
-
if self.split_overlap > 0:
|
|
354
|
-
chunks = self._apply_overlap(chunks)
|
|
355
|
-
|
|
356
353
|
if chunks:
|
|
357
354
|
return chunks
|
|
358
355
|
|
|
359
356
|
# if no separator worked, fall back to word- or character-level chunking
|
|
360
|
-
|
|
361
|
-
if self.split_overlap > 0:
|
|
362
|
-
chunks = self._apply_overlap(chunks)
|
|
363
|
-
return chunks
|
|
357
|
+
return self._fall_back_to_fixed_chunking(text, self.split_units)
|
|
364
358
|
|
|
365
359
|
def _fall_back_to_fixed_chunking(self, text: str, split_units: Literal["word", "char", "token"]) -> list[str]:
|
|
366
360
|
"""
|
|
@@ -429,6 +423,12 @@ class RecursiveDocumentSplitter:
|
|
|
429
423
|
def _run_one(self, doc: Document) -> list[Document]:
|
|
430
424
|
chunks = self._chunk_text(doc.content) # type: ignore # the caller already check for a non-empty doc.content
|
|
431
425
|
chunks = chunks[:-1] if len(chunks[-1]) == 0 else chunks # remove last empty chunk if it exists
|
|
426
|
+
|
|
427
|
+
# apply the overlap once, on the fully chunked list, so that chunks produced
|
|
428
|
+
# at inner recursion levels don't get the overlap applied a second time
|
|
429
|
+
if self.split_overlap > 0:
|
|
430
|
+
chunks = self._apply_overlap(chunks)
|
|
431
|
+
|
|
432
432
|
current_position = 0
|
|
433
433
|
current_page = 1
|
|
434
434
|
|
|
@@ -458,8 +458,12 @@ class ConditionalRouter:
|
|
|
458
458
|
# we try to evaluate it and would fail.
|
|
459
459
|
# This must be done cause the output could be different literal structures.
|
|
460
460
|
# This doesn't support any user types.
|
|
461
|
+
# When the declared output_type is str we skip literal evaluation so that a
|
|
462
|
+
# rendered string that happens to be a valid Python literal (e.g. "1,000" -> (1, 0),
|
|
463
|
+
# "42" -> 42, "None" -> None) is returned unchanged instead of being coerced to
|
|
464
|
+
# another type, which would violate the declared output_type.
|
|
461
465
|
with contextlib.suppress(Exception):
|
|
462
|
-
if not self._unsafe:
|
|
466
|
+
if not self._unsafe and output_type is not str:
|
|
463
467
|
output_value = ast.literal_eval(output_value)
|
|
464
468
|
|
|
465
469
|
# Validate output type if needed
|
|
@@ -646,10 +646,12 @@ class ChatMessage:
|
|
|
646
646
|
If the message format is invalid, or if `require_tool_call_ids` is True and any Tool Call is missing an
|
|
647
647
|
`id` attribute.
|
|
648
648
|
"""
|
|
649
|
-
|
|
649
|
+
has_content = bool(self.texts or self.tool_calls or self.tool_call_results or self.images or self.files)
|
|
650
|
+
# We convert an assistant message with no content part into a message with empty content, which the API accepts
|
|
651
|
+
if not has_content and not self.is_from(ChatRole.ASSISTANT):
|
|
650
652
|
raise ValueError(
|
|
651
|
-
"A `ChatMessage` must contain at least one `TextContent`, `ToolCall`, "
|
|
652
|
-
"`ToolCallResult`, `ImageContent`, or `FileContent`."
|
|
653
|
+
f"A `ChatMessage` from `{self._role.value}` must contain at least one `TextContent`, `ToolCall`, "
|
|
654
|
+
"`ToolCallResult`, `ImageContent`, or `FileContent`. Only assistant messages can be empty."
|
|
653
655
|
)
|
|
654
656
|
if len(self.tool_call_results) > 0 and len(self._content) > 1:
|
|
655
657
|
raise ValueError(
|
|
@@ -745,6 +747,9 @@ class ChatMessage:
|
|
|
745
747
|
raise ValueError("`ToolCall` must have a non-null `id` attribute to be used with OpenAI.")
|
|
746
748
|
openai_tool_calls.append(openai_tool_call)
|
|
747
749
|
openai_msg["tool_calls"] = openai_tool_calls
|
|
750
|
+
# The API rejects a message carrying neither content nor tool calls, so send empty content for it.
|
|
751
|
+
if "content" not in openai_msg and "tool_calls" not in openai_msg:
|
|
752
|
+
openai_msg["content"] = ""
|
|
748
753
|
return openai_msg
|
|
749
754
|
|
|
750
755
|
@staticmethod
|
|
@@ -766,7 +771,9 @@ class ChatMessage:
|
|
|
766
771
|
raise ValueError(f"Unsupported role: {role}")
|
|
767
772
|
|
|
768
773
|
if role == "assistant":
|
|
769
|
-
|
|
774
|
+
# An empty string is valid content for an assistant message: that is how a reply with nothing to send
|
|
775
|
+
# is serialized. Other falsy content requires tool calls.
|
|
776
|
+
if not content and content != "" and not tool_calls:
|
|
770
777
|
raise ValueError("For assistant messages, either `content` or `tool_calls` must be present.")
|
|
771
778
|
if tool_calls:
|
|
772
779
|
for tc in tool_calls:
|
|
@@ -2,23 +2,28 @@
|
|
|
2
2
|
#
|
|
3
3
|
# SPDX-License-Identifier: Apache-2.0
|
|
4
4
|
|
|
5
|
+
import base64
|
|
5
6
|
import json
|
|
7
|
+
import mimetypes
|
|
8
|
+
from pathlib import Path
|
|
6
9
|
from typing import Any
|
|
7
10
|
|
|
8
11
|
from haystack import logging
|
|
9
12
|
from haystack.components.agents.state.state import State
|
|
10
13
|
from haystack.components.agents.state.state_utils import replace_values
|
|
11
14
|
from haystack.core.serialization import default_from_dict, default_to_dict
|
|
12
|
-
from haystack.dataclasses import ChatMessage, TextContent
|
|
13
|
-
from haystack.dataclasses.chat_message import ToolCallResultContentT
|
|
15
|
+
from haystack.dataclasses import ChatMessage, FileContent, ImageContent, TextContent
|
|
14
16
|
from haystack.hooks.tool_result_offloading.types import OffloadPolicy, ToolResultStore
|
|
15
17
|
from haystack.utils.deserialization import deserialize_component_inplace
|
|
16
18
|
|
|
19
|
+
# Extension used for a binary block whose MIME type is unknown or maps to no known extension.
|
|
20
|
+
_FALLBACK_EXTENSION = ".bin"
|
|
21
|
+
|
|
17
22
|
logger = logging.getLogger(__name__)
|
|
18
23
|
|
|
19
|
-
# Meta key marking an already-offloaded tool-result message
|
|
20
|
-
#
|
|
21
|
-
#
|
|
24
|
+
# Meta key marking an already-offloaded tool-result message; its value is the list of store references written.
|
|
25
|
+
# Stops a second `after_tool` offload hook from offloading the pointer text again, since the pointer is itself a
|
|
26
|
+
# tool result.
|
|
22
27
|
_OFFLOADED_META_KEY = "tool_result_offloaded"
|
|
23
28
|
|
|
24
29
|
# Key under which a per-run store override may be supplied via the Agent's `hook_context` (e.g. a request-scoped
|
|
@@ -26,24 +31,6 @@ _OFFLOADED_META_KEY = "tool_result_offloaded"
|
|
|
26
31
|
RESULT_STORE_CONTEXT_KEY = "tool_result_store"
|
|
27
32
|
|
|
28
33
|
|
|
29
|
-
def _result_store_key(tool_name: str, tool_call_id: str | None, step: int, index: int) -> str:
|
|
30
|
-
"""
|
|
31
|
-
Build a per-result store key that is stable and unique within a run.
|
|
32
|
-
|
|
33
|
-
Combining the step, tool name, and tool call id keeps results from different tools and different steps from
|
|
34
|
-
colliding. When the tool call carries no id (it is optional and not every generator sets it), the result's
|
|
35
|
-
position in the step's batch is used instead, so two id-less calls to the same tool in the same step do not
|
|
36
|
-
collide.
|
|
37
|
-
|
|
38
|
-
:param tool_name: The name of the tool that produced the result.
|
|
39
|
-
:param tool_call_id: The id of the originating tool call, or None when the call carried no id.
|
|
40
|
-
:param step: The Agent's current step count.
|
|
41
|
-
:param index: The result's position within this step's batch of tool results, used when `tool_call_id` is None.
|
|
42
|
-
:returns: A file-name-like key for the store, e.g. `2_web_search_call-123.txt`.
|
|
43
|
-
"""
|
|
44
|
-
return f"{step}_{tool_name}_{tool_call_id or f'call{index}'}.txt"
|
|
45
|
-
|
|
46
|
-
|
|
47
34
|
def _fresh_tool_results_start(messages: list[ChatMessage]) -> int:
|
|
48
35
|
"""
|
|
49
36
|
Return the index at which the trailing run of tool-result messages begins.
|
|
@@ -62,23 +49,18 @@ def _fresh_tool_results_start(messages: list[ChatMessage]) -> int:
|
|
|
62
49
|
return index
|
|
63
50
|
|
|
64
51
|
|
|
65
|
-
def
|
|
52
|
+
def _content_block_payload(content_block: TextContent | ImageContent | FileContent) -> str:
|
|
66
53
|
"""
|
|
67
|
-
Return the
|
|
54
|
+
Return the string a content block contributes to the conversation.
|
|
68
55
|
|
|
69
|
-
|
|
70
|
-
into a single string. Anything else (e.g. a result containing image or file content) returns None and is left in
|
|
71
|
-
context.
|
|
56
|
+
For an image or a file this is the base64 payload, which is what actually occupies the context window.
|
|
72
57
|
|
|
73
|
-
:param
|
|
74
|
-
:returns: The
|
|
58
|
+
:param content_block: The content block to inspect.
|
|
59
|
+
:returns: The content block's text or base64 payload.
|
|
75
60
|
"""
|
|
76
|
-
if isinstance(
|
|
77
|
-
return
|
|
78
|
-
|
|
79
|
-
if texts and len(texts) == len(content):
|
|
80
|
-
return "".join(texts)
|
|
81
|
-
return None
|
|
61
|
+
if isinstance(content_block, TextContent):
|
|
62
|
+
return content_block.text
|
|
63
|
+
return content_block.base64_image if isinstance(content_block, ImageContent) else content_block.base64_data
|
|
82
64
|
|
|
83
65
|
|
|
84
66
|
def _serialize_offload_strategies(strategies: dict[str | tuple[str, ...], OffloadPolicy]) -> dict[str, Any]:
|
|
@@ -158,10 +140,10 @@ class ToolResultOffloadHook:
|
|
|
158
140
|
any tool without a more specific entry. More specific keys win. A tool with no matching key (and no `"*"`) is not
|
|
159
141
|
offloaded.
|
|
160
142
|
|
|
161
|
-
Only successful
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
143
|
+
Only successful tool output is offloaded; error results are always left in context. Each part of a result is
|
|
144
|
+
written to its own store entry and the pointer says where each one went. Image and file content is only offloaded
|
|
145
|
+
to a store that sets `supports_binary_content`; with a text-only store the result stays in context and a warning
|
|
146
|
+
is logged. Each result is offloaded at most once, even though the hook runs on every tool step.
|
|
165
147
|
|
|
166
148
|
The hook keeps no mutable state, so a single instance can be shared across concurrent runs. The constructor
|
|
167
149
|
`store`, however, is shared by every run that does not override it — fine for single-user or local use, but in a
|
|
@@ -187,8 +169,9 @@ class ToolResultOffloadHook:
|
|
|
187
169
|
:param store: Where offloaded results are written. Can be overridden per run via `hook_context`.
|
|
188
170
|
:param offload_strategies: Mapping of tool name (or a tuple of tool names, or the wildcard `"*"`) to the
|
|
189
171
|
`OffloadPolicy` that decides whether that tool's results are offloaded.
|
|
190
|
-
:param preview_chars: Number of leading characters of
|
|
191
|
-
the conversation, so the model knows roughly what was offloaded.
|
|
172
|
+
:param preview_chars: Number of leading characters of each offloaded text to include in the pointer left in
|
|
173
|
+
the conversation, so the model knows roughly what was offloaded. Image and file blocks are described by
|
|
174
|
+
their MIME type and size instead.
|
|
192
175
|
"""
|
|
193
176
|
self.store = store
|
|
194
177
|
self.offload_strategies = offload_strategies
|
|
@@ -217,29 +200,25 @@ class ToolResultOffloadHook:
|
|
|
217
200
|
:returns: None. The hook mutates `state` in place.
|
|
218
201
|
"""
|
|
219
202
|
messages = state.data.get("messages") or []
|
|
220
|
-
start = _fresh_tool_results_start(messages)
|
|
203
|
+
start = _fresh_tool_results_start(messages=messages)
|
|
221
204
|
if start == len(messages):
|
|
222
205
|
return
|
|
223
|
-
|
|
206
|
+
|
|
207
|
+
# The hook instance is shared across concurrent runs, so a run isolates itself by carrying its own store in
|
|
208
|
+
# `hook_context`.
|
|
209
|
+
hook_context = state.data.get("hook_context") or {}
|
|
210
|
+
store = hook_context.get(RESULT_STORE_CONTEXT_KEY, self.store)
|
|
211
|
+
|
|
224
212
|
rewritten: list[ChatMessage] = list(messages[:start])
|
|
225
213
|
changed = False
|
|
226
214
|
for index, message in enumerate(messages[start:]):
|
|
227
|
-
new_message = self._maybe_offload(message, store, state, index)
|
|
215
|
+
new_message = self._maybe_offload(message=message, store=store, state=state, index=index)
|
|
228
216
|
rewritten.append(new_message)
|
|
229
217
|
changed = changed or new_message is not message
|
|
230
|
-
if changed:
|
|
231
|
-
state.set("messages", rewritten, handler_override=replace_values)
|
|
232
|
-
|
|
233
|
-
def _resolve_store(self, state: State) -> ToolResultStore:
|
|
234
|
-
"""
|
|
235
|
-
Return the store to write to for this run.
|
|
236
218
|
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
"""
|
|
241
|
-
context = state.data.get("hook_context") or {}
|
|
242
|
-
return context.get(RESULT_STORE_CONTEXT_KEY, self.store)
|
|
219
|
+
# Only write back to state when at least one message changed
|
|
220
|
+
if changed:
|
|
221
|
+
state.set(key="messages", value=rewritten, handler_override=replace_values)
|
|
243
222
|
|
|
244
223
|
def _policy_for(self, tool_name: str) -> OffloadPolicy | None:
|
|
245
224
|
"""
|
|
@@ -264,11 +243,12 @@ class ToolResultOffloadHook:
|
|
|
264
243
|
|
|
265
244
|
A message is left as-is when it is not a tool result, when the result is an error (including `before_tool`
|
|
266
245
|
human-in-the-loop rejections), when it was already offloaded (e.g. another offload hook under `after_tool`
|
|
267
|
-
handled it), when no policy applies, when the result is
|
|
268
|
-
policy declines to offload.
|
|
246
|
+
handled it), when no policy applies, when the result is empty, when the result carries image or file content
|
|
247
|
+
that `store` cannot store, or when the policy declines to offload.
|
|
269
248
|
|
|
270
|
-
Otherwise the result
|
|
271
|
-
result, preserving its origin and error flag and marking it offloaded.
|
|
249
|
+
Otherwise the result is written to `store` and the message is rebuilt with a pointer in place of the full
|
|
250
|
+
result, preserving its origin and error flag and marking it offloaded. Each part of the result goes to its own
|
|
251
|
+
store entry.
|
|
272
252
|
|
|
273
253
|
:param message: The message to consider offloading.
|
|
274
254
|
:param store: The store to write the result to.
|
|
@@ -283,48 +263,115 @@ class ToolResultOffloadHook:
|
|
|
283
263
|
return message
|
|
284
264
|
|
|
285
265
|
tool_name = result.origin.tool_name
|
|
286
|
-
policy = self._policy_for(tool_name)
|
|
266
|
+
policy = self._policy_for(tool_name=tool_name)
|
|
287
267
|
|
|
288
268
|
# If no policy applies, leave the result in context
|
|
289
269
|
if policy is None:
|
|
290
270
|
return message
|
|
291
271
|
|
|
292
|
-
# A
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
if
|
|
272
|
+
# A plain string result is handled as a single text block, so everything below has one shape to work with.
|
|
273
|
+
content_blocks: list[TextContent | ImageContent | FileContent] = (
|
|
274
|
+
[TextContent(text=result.result)] if isinstance(result.result, str) else list(result.result)
|
|
275
|
+
)
|
|
276
|
+
if not content_blocks:
|
|
277
|
+
return message
|
|
278
|
+
|
|
279
|
+
# Check whether the store can store binary content before offloading an image or file result. A text-only store
|
|
280
|
+
# leaves the result in context and logs a warning.
|
|
281
|
+
if not getattr(store, "supports_binary_content", False) and not all(
|
|
282
|
+
isinstance(content_block, TextContent) for content_block in content_blocks
|
|
283
|
+
):
|
|
297
284
|
logger.warning(
|
|
298
|
-
"Tool '{tool}' produced a
|
|
299
|
-
"
|
|
285
|
+
"Tool '{tool}' produced a result with image or file content, but {store} does not support binary "
|
|
286
|
+
"content; leaving the result in context.",
|
|
300
287
|
tool=tool_name,
|
|
288
|
+
store=type(store).__name__,
|
|
301
289
|
)
|
|
302
290
|
return message
|
|
303
291
|
|
|
304
|
-
#
|
|
305
|
-
|
|
292
|
+
# The policy sizes up the result by the string it occupies in the conversation, which for an image or a file
|
|
293
|
+
# block is its base64 payload.
|
|
294
|
+
payload = "".join(_content_block_payload(content_block=content_block) for content_block in content_blocks)
|
|
295
|
+
if not policy.should_offload(tool_name=tool_name, result=payload, state=state):
|
|
306
296
|
return message
|
|
307
297
|
|
|
308
|
-
|
|
309
|
-
|
|
298
|
+
# Step, tool name, and call id keep results from different tools and steps from colliding. A tool call id is
|
|
299
|
+
# optional, so an id-less call falls back to its position in this step's batch.
|
|
300
|
+
step = state.data.get("step_count", 0)
|
|
301
|
+
prefix = f"{step}_{tool_name}_{result.origin.id or f'call{index}'}"
|
|
302
|
+
references, pointer = self._offload_content_blocks(content_blocks=content_blocks, store=store, prefix=prefix)
|
|
303
|
+
|
|
310
304
|
return ChatMessage.from_tool(
|
|
311
|
-
tool_result=
|
|
305
|
+
tool_result=pointer,
|
|
312
306
|
origin=result.origin,
|
|
313
307
|
error=result.error,
|
|
314
|
-
meta={**message.meta, _OFFLOADED_META_KEY:
|
|
308
|
+
meta={**message.meta, _OFFLOADED_META_KEY: references},
|
|
315
309
|
)
|
|
316
310
|
|
|
317
|
-
def
|
|
311
|
+
def _offload_content_blocks(
|
|
312
|
+
self, content_blocks: list[TextContent | ImageContent | FileContent], store: ToolResultStore, prefix: str
|
|
313
|
+
) -> tuple[list[str], str]:
|
|
314
|
+
"""
|
|
315
|
+
Write a result's content blocks to the store and build the pointer that replaces them in the conversation.
|
|
316
|
+
|
|
317
|
+
Every content block goes to its own store entry. A single block keeps `prefix` as its key and gets a one-line
|
|
318
|
+
pointer; several blocks get position-suffixed keys and one numbered pointer line each.
|
|
319
|
+
|
|
320
|
+
:param content_blocks: The result's content blocks, in order.
|
|
321
|
+
:param store: The store to write to.
|
|
322
|
+
:param prefix: The result's store key prefix, as described above.
|
|
323
|
+
:returns: The store references written, and the pointer text for the conversation.
|
|
324
|
+
"""
|
|
325
|
+
references: list[str] = []
|
|
326
|
+
descriptions: list[str] = []
|
|
327
|
+
single = len(content_blocks) == 1
|
|
328
|
+
for position, content_block in enumerate(content_blocks):
|
|
329
|
+
reference, description = self._offload_content_block(
|
|
330
|
+
content_block=content_block, store=store, key_prefix=prefix if single else f"{prefix}_{position}"
|
|
331
|
+
)
|
|
332
|
+
references.append(reference)
|
|
333
|
+
descriptions.append(description)
|
|
334
|
+
|
|
335
|
+
if len(descriptions) == 1:
|
|
336
|
+
return references, f"Tool result offloaded to {descriptions[0]}"
|
|
337
|
+
|
|
338
|
+
numbered = [f"{position}. {description}" for position, description in enumerate(descriptions, start=1)]
|
|
339
|
+
return references, "\n".join([f"Tool result offloaded to {len(descriptions)} files:", *numbered])
|
|
340
|
+
|
|
341
|
+
def _offload_content_block(
|
|
342
|
+
self, content_block: TextContent | ImageContent | FileContent, store: ToolResultStore, key_prefix: str
|
|
343
|
+
) -> tuple[str, str]:
|
|
318
344
|
"""
|
|
319
|
-
|
|
345
|
+
Write a single content block to the store and describe where it went.
|
|
320
346
|
|
|
321
|
-
:param
|
|
322
|
-
:param
|
|
323
|
-
:
|
|
347
|
+
:param content_block: The content block to offload.
|
|
348
|
+
:param store: The store to write to.
|
|
349
|
+
:param key_prefix: The content block's store key without its extension.
|
|
350
|
+
:returns: The store reference the content block was written to, and a one-line description for the pointer.
|
|
324
351
|
"""
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
352
|
+
if isinstance(content_block, TextContent):
|
|
353
|
+
text = content_block.text
|
|
354
|
+
reference = store.write(key=f"{key_prefix}.txt", content=text)
|
|
355
|
+
# An ellipsis marks a preview that was cut short, so the model can tell it is not the whole text.
|
|
356
|
+
preview = f"{text[: self.preview_chars]}{'...' if len(text) > self.preview_chars else ''}"
|
|
357
|
+
return reference, f"text ({len(text)} characters) at '{reference}'. Preview: {preview}"
|
|
358
|
+
|
|
359
|
+
if isinstance(content_block, ImageContent):
|
|
360
|
+
data = base64.b64decode(content_block.base64_image)
|
|
361
|
+
label = content_block.mime_type or "image"
|
|
362
|
+
filename = None
|
|
363
|
+
else:
|
|
364
|
+
data = base64.b64decode(content_block.base64_data)
|
|
365
|
+
label = content_block.mime_type or "file"
|
|
366
|
+
filename = content_block.filename
|
|
367
|
+
|
|
368
|
+
# What the tool called the file wins over its MIME type. Only the suffix is taken.
|
|
369
|
+
mime_extension = mimetypes.guess_extension(content_block.mime_type) if content_block.mime_type else None
|
|
370
|
+
extension = (Path(filename).suffix if filename else "") or mime_extension or _FALLBACK_EXTENSION
|
|
371
|
+
reference = store.write(key=f"{key_prefix}{extension}", content=data)
|
|
372
|
+
|
|
373
|
+
named = f" named '{filename}'" if filename else ""
|
|
374
|
+
return reference, f"{label}{named} ({len(data)} bytes) at '{reference}'"
|
|
328
375
|
|
|
329
376
|
def to_dict(self) -> dict[str, Any]:
|
|
330
377
|
"""
|
|
@@ -335,7 +382,7 @@ class ToolResultOffloadHook:
|
|
|
335
382
|
return default_to_dict(
|
|
336
383
|
self,
|
|
337
384
|
store=self.store.to_dict(),
|
|
338
|
-
offload_strategies=_serialize_offload_strategies(self.offload_strategies),
|
|
385
|
+
offload_strategies=_serialize_offload_strategies(strategies=self.offload_strategies),
|
|
339
386
|
preview_chars=self.preview_chars,
|
|
340
387
|
)
|
|
341
388
|
|
|
@@ -351,5 +398,5 @@ class ToolResultOffloadHook:
|
|
|
351
398
|
if init_params.get("store") is not None:
|
|
352
399
|
deserialize_component_inplace(init_params, key="store")
|
|
353
400
|
if init_params.get("offload_strategies") is not None:
|
|
354
|
-
init_params["offload_strategies"] = _deserialize_offload_strategies(init_params["offload_strategies"])
|
|
401
|
+
init_params["offload_strategies"] = _deserialize_offload_strategies(data=init_params["offload_strategies"])
|
|
355
402
|
return default_from_dict(cls, data)
|
|
@@ -20,8 +20,13 @@ class FileSystemToolResultStore(ToolResultStore):
|
|
|
20
20
|
reference = store.write(key="search_1.txt", content="...")
|
|
21
21
|
store.read(reference)
|
|
22
22
|
```
|
|
23
|
+
|
|
24
|
+
Binary content is supported too: `write` takes bytes (an offloaded image or file) and `read` returns them
|
|
25
|
+
unchanged.
|
|
23
26
|
"""
|
|
24
27
|
|
|
28
|
+
supports_binary_content = True
|
|
29
|
+
|
|
25
30
|
def __init__(self, root: str | Path) -> None:
|
|
26
31
|
"""
|
|
27
32
|
Initialize the store with the root directory results are written under.
|
|
@@ -49,35 +54,47 @@ class FileSystemToolResultStore(ToolResultStore):
|
|
|
49
54
|
raise ValueError(f"{subject} '{path_like}' resolves outside the store root '{root}'.")
|
|
50
55
|
return resolved
|
|
51
56
|
|
|
52
|
-
def write(self, *, key: str, content: str) -> str:
|
|
57
|
+
def write(self, *, key: str, content: str | bytes) -> str:
|
|
53
58
|
"""
|
|
54
59
|
Write `content` to `<root>/<key>`, creating parent directories, and return the file path.
|
|
55
60
|
|
|
61
|
+
Text is written UTF-8 encoded; bytes (an offloaded image or file) are written verbatim.
|
|
62
|
+
|
|
56
63
|
The resolved target must stay within the root directory: a `key` that escapes it (e.g. containing `../` or an
|
|
57
64
|
absolute path) is rejected, so a tool-provided key cannot write outside the store.
|
|
58
65
|
|
|
59
66
|
:param key: Relative file name for the result within the store root.
|
|
60
|
-
:param content: The tool result to persist.
|
|
67
|
+
:param content: The tool result to persist, as text or as raw bytes.
|
|
61
68
|
:returns: The absolute path the content was written to, as a string, for use with `read`.
|
|
62
69
|
:raises ValueError: If `key` resolves to a location outside the store root.
|
|
63
70
|
"""
|
|
64
71
|
path = self._resolve_in_root(key, subject="Result key")
|
|
65
72
|
path.parent.mkdir(parents=True, exist_ok=True)
|
|
66
|
-
|
|
73
|
+
if isinstance(content, bytes):
|
|
74
|
+
path.write_bytes(content)
|
|
75
|
+
else:
|
|
76
|
+
path.write_text(content, encoding="utf-8")
|
|
67
77
|
return str(path)
|
|
68
78
|
|
|
69
|
-
def read(self, reference: str) -> str:
|
|
79
|
+
def read(self, reference: str) -> str | bytes:
|
|
70
80
|
"""
|
|
71
81
|
Read back the content previously written to `reference`.
|
|
72
82
|
|
|
73
|
-
|
|
74
|
-
|
|
83
|
+
A file whose bytes are valid UTF-8 is returned as a string, so text results round trip unchanged; anything
|
|
84
|
+
else (an offloaded image or file) is returned as raw bytes.
|
|
85
|
+
|
|
86
|
+
The resolved reference must stay within the store root: it is a store-scoped reference returned by `write`,
|
|
87
|
+
to be passed back unchanged, not an arbitrary filesystem path callers can build themselves.
|
|
75
88
|
|
|
76
89
|
:param reference: A store reference returned by `write`.
|
|
77
|
-
:returns: The stored content.
|
|
90
|
+
:returns: The stored content, as text when it decodes as UTF-8 and as bytes otherwise.
|
|
78
91
|
:raises ValueError: If `reference` resolves to a location outside the store root.
|
|
79
92
|
"""
|
|
80
|
-
|
|
93
|
+
data = self._resolve_in_root(reference, subject="Result reference").read_bytes()
|
|
94
|
+
try:
|
|
95
|
+
return data.decode("utf-8")
|
|
96
|
+
except UnicodeDecodeError:
|
|
97
|
+
return data
|
|
81
98
|
|
|
82
99
|
def to_dict(self) -> dict[str, Any]:
|
|
83
100
|
"""
|
|
@@ -13,25 +13,42 @@ class ToolResultStore(Protocol):
|
|
|
13
13
|
A place a `ToolResultOffloadHook` writes offloaded tool results to, and reads them back from.
|
|
14
14
|
|
|
15
15
|
Implementations decide where and how the content lives (local disk, an isolated sandbox filesystem, object
|
|
16
|
-
storage, ...). `write` returns
|
|
17
|
-
|
|
16
|
+
storage, ...). `write` returns a reference string that the Agent puts in the conversation in place of the full
|
|
17
|
+
result; `read` resolves that reference back to the original content. Only the store interprets a reference -
|
|
18
|
+
callers pass it back to `read` unchanged.
|
|
19
|
+
|
|
20
|
+
A store that sets `supports_binary_content` takes bytes in `write` and gives them back from `read`. One that
|
|
21
|
+
leaves it False is only ever given text, and image and file results stay in the conversation instead.
|
|
18
22
|
|
|
19
23
|
Implement both `to_dict` and `from_dict` to make a custom store serializable; the default implementations below
|
|
20
24
|
cover stores whose constructor takes no arguments.
|
|
21
25
|
"""
|
|
22
26
|
|
|
23
|
-
|
|
27
|
+
# Whether the store handles binary content: `write` accepting bytes and `read` returning them. When False, the
|
|
28
|
+
# store is only ever given text, and the image and file content of a tool result stays in the conversation.
|
|
29
|
+
supports_binary_content: bool = False
|
|
30
|
+
|
|
31
|
+
def write(self, *, key: str, content: str | bytes) -> str:
|
|
24
32
|
"""
|
|
25
|
-
Persist `content` under `key` and return
|
|
33
|
+
Persist `content` under `key` and return a reference to it.
|
|
26
34
|
|
|
27
|
-
:param key: A stable, per-result identifier the hook derives from the tool call (e.g. a file name).
|
|
28
|
-
|
|
35
|
+
:param key: A stable, per-result identifier the hook derives from the tool call (e.g. a file name). It carries
|
|
36
|
+
an extension matching the content, so a store that maps keys to files can use it as-is.
|
|
37
|
+
:param content: The tool result to persist. Text arrives as a string. Image and file content arrives as the
|
|
38
|
+
decoded bytes of its base64 payload, and only when the store sets `supports_binary_content` to True - a
|
|
39
|
+
text-only store may narrow this parameter to `str`.
|
|
29
40
|
:returns: A reference string (e.g. a path or URI) that `read` can later resolve.
|
|
30
41
|
"""
|
|
31
42
|
...
|
|
32
43
|
|
|
33
|
-
def read(self, reference: str) -> str:
|
|
34
|
-
"""
|
|
44
|
+
def read(self, reference: str) -> str | bytes:
|
|
45
|
+
"""
|
|
46
|
+
Return the content previously stored under `reference`.
|
|
47
|
+
|
|
48
|
+
:param reference: A reference string returned by `write`.
|
|
49
|
+
:returns: The stored content: a string for content written as text, bytes for binary content such as an
|
|
50
|
+
offloaded image or file. A store that does not support binary content only ever returns a string.
|
|
51
|
+
"""
|
|
35
52
|
...
|
|
36
53
|
|
|
37
54
|
def to_dict(self) -> dict[str, Any]:
|
|
@@ -60,7 +77,9 @@ class OffloadPolicy(Protocol):
|
|
|
60
77
|
Return whether the given tool result should be offloaded.
|
|
61
78
|
|
|
62
79
|
:param tool_name: The name of the tool that produced the result.
|
|
63
|
-
:param result: The tool result as a string (the content that would otherwise stay in the conversation).
|
|
80
|
+
:param result: The tool result as a string (the content that would otherwise stay in the conversation). For a
|
|
81
|
+
result carrying image or file blocks, this is the text and base64 payloads of all its blocks joined
|
|
82
|
+
together, so its length reflects the context the result actually occupies.
|
|
64
83
|
:param state: The Agent's live `State`, for policies that decide based on run context.
|
|
65
84
|
:returns: True to offload the result to the store, False to leave it in context.
|
|
66
85
|
"""
|
{haystack_ai-3.2.0.dev20260902000321 → haystack_ai-3.2.0.dev20260903000331}/haystack/utils/hf.py
RENAMED
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
from typing import Any
|
|
6
6
|
|
|
7
7
|
from haystack import logging
|
|
8
|
-
from haystack.dataclasses import ChatMessage, ImageContent, ReasoningContent, TextContent
|
|
8
|
+
from haystack.dataclasses import ChatMessage, ChatRole, ImageContent, ReasoningContent, TextContent
|
|
9
9
|
from haystack.lazy_imports import LazyImport
|
|
10
10
|
|
|
11
11
|
with LazyImport(message="Run 'pip install \"transformers[torch]\"'") as torch_import:
|
|
@@ -69,9 +69,12 @@ def convert_message_to_hf_format(message: ChatMessage) -> dict[str, Any]:
|
|
|
69
69
|
# ReasoningContent is for human transparency only, not sent to the API
|
|
70
70
|
non_reasoning_content = [c for c in message._content if not isinstance(c, ReasoningContent)]
|
|
71
71
|
|
|
72
|
-
|
|
72
|
+
has_content = bool(text_contents or tool_calls or tool_call_results or images)
|
|
73
|
+
# We convert an assistant message with no content part into a message with empty content, which the API accepts
|
|
74
|
+
if not has_content and not message.is_from(ChatRole.ASSISTANT):
|
|
73
75
|
raise ValueError(
|
|
74
|
-
"A `ChatMessage` must contain at least one `TextContent`, `ToolCall`,
|
|
76
|
+
f"A `ChatMessage` from `{message._role.value}` must contain at least one `TextContent`, `ToolCall`, "
|
|
77
|
+
"`ToolCallResult`, or `ImageContent`. Only assistant messages can be empty."
|
|
75
78
|
)
|
|
76
79
|
if len(tool_call_results) > 0 and len(non_reasoning_content) > 1:
|
|
77
80
|
raise ValueError(
|