haystack-ai 3.2.0rc1__tar.gz → 3.3.0__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.0rc1 → haystack_ai-3.3.0}/PKG-INFO +2 -11
- {haystack_ai-3.2.0rc1 → haystack_ai-3.3.0}/README.md +0 -10
- haystack_ai-3.3.0/VERSION.txt +1 -0
- {haystack_ai-3.2.0rc1 → haystack_ai-3.3.0}/haystack/components/agents/agent.py +8 -6
- {haystack_ai-3.2.0rc1 → haystack_ai-3.3.0}/haystack/components/agents/tool_calling.py +4 -4
- {haystack_ai-3.2.0rc1 → haystack_ai-3.3.0}/haystack/components/builders/chat_prompt_builder.py +15 -11
- {haystack_ai-3.2.0rc1 → haystack_ai-3.3.0}/haystack/components/converters/docx.py +39 -5
- {haystack_ai-3.2.0rc1 → haystack_ai-3.3.0}/haystack/components/converters/image/document_to_image.py +2 -2
- {haystack_ai-3.2.0rc1 → haystack_ai-3.3.0}/haystack/components/converters/image/image_utils.py +12 -5
- {haystack_ai-3.2.0rc1 → haystack_ai-3.3.0}/haystack/components/converters/json.py +3 -0
- {haystack_ai-3.2.0rc1 → haystack_ai-3.3.0}/haystack/components/converters/xlsx.py +9 -0
- {haystack_ai-3.2.0rc1 → haystack_ai-3.3.0}/haystack/components/embedders/mock_document_embedder.py +3 -3
- {haystack_ai-3.2.0rc1 → haystack_ai-3.3.0}/haystack/components/embedders/mock_text_embedder.py +3 -3
- {haystack_ai-3.2.0rc1 → haystack_ai-3.3.0}/haystack/components/extractors/llm_metadata_extractor.py +64 -52
- {haystack_ai-3.2.0rc1 → haystack_ai-3.3.0}/haystack/components/fetchers/link_content.py +1 -1
- {haystack_ai-3.2.0rc1 → haystack_ai-3.3.0}/haystack/components/joiners/branch.py +4 -2
- {haystack_ai-3.2.0rc1 → haystack_ai-3.3.0}/haystack/components/preprocessors/sentence_tokenizer.py +21 -2
- {haystack_ai-3.2.0rc1 → haystack_ai-3.3.0}/haystack/components/preprocessors/text_cleaner.py +4 -0
- {haystack_ai-3.2.0rc1 → haystack_ai-3.3.0}/haystack/components/rankers/meta_field.py +8 -6
- {haystack_ai-3.2.0rc1 → haystack_ai-3.3.0}/haystack/components/rankers/meta_field_grouping_ranker.py +4 -2
- {haystack_ai-3.2.0rc1 → haystack_ai-3.3.0}/haystack/components/retrievers/in_memory/bm25_retriever.py +13 -7
- {haystack_ai-3.2.0rc1 → haystack_ai-3.3.0}/haystack/components/retrievers/in_memory/embedding_retriever.py +13 -7
- {haystack_ai-3.2.0rc1 → haystack_ai-3.3.0}/haystack/components/retrievers/multi_retriever.py +41 -14
- {haystack_ai-3.2.0rc1 → haystack_ai-3.3.0}/haystack/components/retrievers/sentence_window_retriever.py +84 -53
- {haystack_ai-3.2.0rc1 → haystack_ai-3.3.0}/haystack/components/routers/conditional_router.py +18 -15
- {haystack_ai-3.2.0rc1 → haystack_ai-3.3.0}/haystack/core/pipeline/base.py +9 -0
- {haystack_ai-3.2.0rc1 → haystack_ai-3.3.0}/haystack/core/pipeline/pipeline.py +12 -0
- {haystack_ai-3.2.0rc1 → haystack_ai-3.3.0}/haystack/core/type_utils.py +6 -1
- {haystack_ai-3.2.0rc1 → haystack_ai-3.3.0}/haystack/dataclasses/chat_message.py +16 -1
- {haystack_ai-3.2.0rc1 → haystack_ai-3.3.0}/haystack/document_stores/in_memory/document_store.py +46 -11
- {haystack_ai-3.2.0rc1 → haystack_ai-3.3.0}/haystack/hooks/compaction/hooks.py +12 -12
- {haystack_ai-3.2.0rc1 → haystack_ai-3.3.0}/haystack/hooks/human_in_the_loop/strategies.py +4 -17
- {haystack_ai-3.2.0rc1 → haystack_ai-3.3.0}/pyproject.toml +2 -2
- haystack_ai-3.2.0rc1/VERSION.txt +0 -1
- {haystack_ai-3.2.0rc1 → haystack_ai-3.3.0}/.gitignore +0 -0
- {haystack_ai-3.2.0rc1 → haystack_ai-3.3.0}/LICENSE +0 -0
- {haystack_ai-3.2.0rc1 → haystack_ai-3.3.0}/haystack/__init__.py +0 -0
- {haystack_ai-3.2.0rc1 → haystack_ai-3.3.0}/haystack/components/__init__.py +0 -0
- {haystack_ai-3.2.0rc1 → haystack_ai-3.3.0}/haystack/components/agents/__init__.py +0 -0
- {haystack_ai-3.2.0rc1 → haystack_ai-3.3.0}/haystack/components/agents/state/__init__.py +0 -0
- {haystack_ai-3.2.0rc1 → haystack_ai-3.3.0}/haystack/components/agents/state/state.py +0 -0
- {haystack_ai-3.2.0rc1 → haystack_ai-3.3.0}/haystack/components/agents/state/state_utils.py +0 -0
- {haystack_ai-3.2.0rc1 → haystack_ai-3.3.0}/haystack/components/agents/utils.py +0 -0
- {haystack_ai-3.2.0rc1 → haystack_ai-3.3.0}/haystack/components/builders/__init__.py +0 -0
- {haystack_ai-3.2.0rc1 → haystack_ai-3.3.0}/haystack/components/builders/answer_builder.py +0 -0
- {haystack_ai-3.2.0rc1 → haystack_ai-3.3.0}/haystack/components/builders/prompt_builder.py +0 -0
- {haystack_ai-3.2.0rc1 → haystack_ai-3.3.0}/haystack/components/caching/__init__.py +0 -0
- {haystack_ai-3.2.0rc1 → haystack_ai-3.3.0}/haystack/components/caching/cache_checker.py +0 -0
- {haystack_ai-3.2.0rc1 → haystack_ai-3.3.0}/haystack/components/converters/__init__.py +0 -0
- {haystack_ai-3.2.0rc1 → haystack_ai-3.3.0}/haystack/components/converters/csv.py +0 -0
- {haystack_ai-3.2.0rc1 → haystack_ai-3.3.0}/haystack/components/converters/file_to_file_content.py +0 -0
- {haystack_ai-3.2.0rc1 → haystack_ai-3.3.0}/haystack/components/converters/html.py +0 -0
- {haystack_ai-3.2.0rc1 → haystack_ai-3.3.0}/haystack/components/converters/image/__init__.py +0 -0
- {haystack_ai-3.2.0rc1 → haystack_ai-3.3.0}/haystack/components/converters/image/file_to_document.py +0 -0
- {haystack_ai-3.2.0rc1 → haystack_ai-3.3.0}/haystack/components/converters/image/file_to_image.py +0 -0
- {haystack_ai-3.2.0rc1 → haystack_ai-3.3.0}/haystack/components/converters/image/pdf_to_image.py +0 -0
- {haystack_ai-3.2.0rc1 → haystack_ai-3.3.0}/haystack/components/converters/markdown.py +0 -0
- {haystack_ai-3.2.0rc1 → haystack_ai-3.3.0}/haystack/components/converters/msg.py +0 -0
- {haystack_ai-3.2.0rc1 → haystack_ai-3.3.0}/haystack/components/converters/multi_file_converter.py +0 -0
- {haystack_ai-3.2.0rc1 → haystack_ai-3.3.0}/haystack/components/converters/output_adapter.py +0 -0
- {haystack_ai-3.2.0rc1 → haystack_ai-3.3.0}/haystack/components/converters/pdfminer.py +0 -0
- {haystack_ai-3.2.0rc1 → haystack_ai-3.3.0}/haystack/components/converters/pptx.py +0 -0
- {haystack_ai-3.2.0rc1 → haystack_ai-3.3.0}/haystack/components/converters/pypdf.py +0 -0
- {haystack_ai-3.2.0rc1 → haystack_ai-3.3.0}/haystack/components/converters/txt.py +0 -0
- {haystack_ai-3.2.0rc1 → haystack_ai-3.3.0}/haystack/components/converters/utils.py +0 -0
- {haystack_ai-3.2.0rc1 → haystack_ai-3.3.0}/haystack/components/embedders/__init__.py +0 -0
- {haystack_ai-3.2.0rc1 → haystack_ai-3.3.0}/haystack/components/embedders/azure_document_embedder.py +0 -0
- {haystack_ai-3.2.0rc1 → haystack_ai-3.3.0}/haystack/components/embedders/azure_text_embedder.py +0 -0
- {haystack_ai-3.2.0rc1 → haystack_ai-3.3.0}/haystack/components/embedders/mock_utils.py +0 -0
- {haystack_ai-3.2.0rc1 → haystack_ai-3.3.0}/haystack/components/embedders/openai_document_embedder.py +0 -0
- {haystack_ai-3.2.0rc1 → haystack_ai-3.3.0}/haystack/components/embedders/openai_text_embedder.py +0 -0
- {haystack_ai-3.2.0rc1 → haystack_ai-3.3.0}/haystack/components/embedders/types/__init__.py +0 -0
- {haystack_ai-3.2.0rc1 → haystack_ai-3.3.0}/haystack/components/embedders/types/protocol.py +0 -0
- {haystack_ai-3.2.0rc1 → haystack_ai-3.3.0}/haystack/components/evaluators/__init__.py +0 -0
- {haystack_ai-3.2.0rc1 → haystack_ai-3.3.0}/haystack/components/evaluators/answer_exact_match.py +0 -0
- {haystack_ai-3.2.0rc1 → haystack_ai-3.3.0}/haystack/components/evaluators/context_relevance.py +0 -0
- {haystack_ai-3.2.0rc1 → haystack_ai-3.3.0}/haystack/components/evaluators/document_map.py +0 -0
- {haystack_ai-3.2.0rc1 → haystack_ai-3.3.0}/haystack/components/evaluators/document_mrr.py +0 -0
- {haystack_ai-3.2.0rc1 → haystack_ai-3.3.0}/haystack/components/evaluators/document_ndcg.py +0 -0
- {haystack_ai-3.2.0rc1 → haystack_ai-3.3.0}/haystack/components/evaluators/document_recall.py +0 -0
- {haystack_ai-3.2.0rc1 → haystack_ai-3.3.0}/haystack/components/evaluators/faithfulness.py +0 -0
- {haystack_ai-3.2.0rc1 → haystack_ai-3.3.0}/haystack/components/evaluators/llm_evaluator.py +0 -0
- {haystack_ai-3.2.0rc1 → haystack_ai-3.3.0}/haystack/components/evaluators/sas_evaluator.py +0 -0
- {haystack_ai-3.2.0rc1 → haystack_ai-3.3.0}/haystack/components/extractors/__init__.py +0 -0
- {haystack_ai-3.2.0rc1 → haystack_ai-3.3.0}/haystack/components/extractors/image/__init__.py +0 -0
- {haystack_ai-3.2.0rc1 → haystack_ai-3.3.0}/haystack/components/extractors/image/llm_document_content_extractor.py +0 -0
- {haystack_ai-3.2.0rc1 → haystack_ai-3.3.0}/haystack/components/extractors/regex_text_extractor.py +0 -0
- {haystack_ai-3.2.0rc1 → haystack_ai-3.3.0}/haystack/components/fetchers/__init__.py +0 -0
- {haystack_ai-3.2.0rc1 → haystack_ai-3.3.0}/haystack/components/generators/__init__.py +0 -0
- {haystack_ai-3.2.0rc1 → haystack_ai-3.3.0}/haystack/components/generators/chat/__init__.py +0 -0
- {haystack_ai-3.2.0rc1 → haystack_ai-3.3.0}/haystack/components/generators/chat/azure.py +0 -0
- {haystack_ai-3.2.0rc1 → haystack_ai-3.3.0}/haystack/components/generators/chat/azure_responses.py +0 -0
- {haystack_ai-3.2.0rc1 → haystack_ai-3.3.0}/haystack/components/generators/chat/fallback.py +0 -0
- {haystack_ai-3.2.0rc1 → haystack_ai-3.3.0}/haystack/components/generators/chat/llm.py +0 -0
- {haystack_ai-3.2.0rc1 → haystack_ai-3.3.0}/haystack/components/generators/chat/mock.py +0 -0
- {haystack_ai-3.2.0rc1 → haystack_ai-3.3.0}/haystack/components/generators/chat/openai.py +0 -0
- {haystack_ai-3.2.0rc1 → haystack_ai-3.3.0}/haystack/components/generators/chat/openai_responses.py +0 -0
- {haystack_ai-3.2.0rc1 → haystack_ai-3.3.0}/haystack/components/generators/chat/types/__init__.py +0 -0
- {haystack_ai-3.2.0rc1 → haystack_ai-3.3.0}/haystack/components/generators/chat/types/protocol.py +0 -0
- {haystack_ai-3.2.0rc1 → haystack_ai-3.3.0}/haystack/components/generators/openai_image_generator.py +0 -0
- {haystack_ai-3.2.0rc1 → haystack_ai-3.3.0}/haystack/components/generators/utils.py +0 -0
- {haystack_ai-3.2.0rc1 → haystack_ai-3.3.0}/haystack/components/joiners/__init__.py +0 -0
- {haystack_ai-3.2.0rc1 → haystack_ai-3.3.0}/haystack/components/joiners/answer_joiner.py +0 -0
- {haystack_ai-3.2.0rc1 → haystack_ai-3.3.0}/haystack/components/joiners/document_joiner.py +0 -0
- {haystack_ai-3.2.0rc1 → haystack_ai-3.3.0}/haystack/components/joiners/list_joiner.py +0 -0
- {haystack_ai-3.2.0rc1 → haystack_ai-3.3.0}/haystack/components/joiners/string_joiner.py +0 -0
- {haystack_ai-3.2.0rc1 → haystack_ai-3.3.0}/haystack/components/preprocessors/__init__.py +0 -0
- {haystack_ai-3.2.0rc1 → haystack_ai-3.3.0}/haystack/components/preprocessors/csv_document_cleaner.py +0 -0
- {haystack_ai-3.2.0rc1 → haystack_ai-3.3.0}/haystack/components/preprocessors/csv_document_splitter.py +0 -0
- {haystack_ai-3.2.0rc1 → haystack_ai-3.3.0}/haystack/components/preprocessors/document_cleaner.py +0 -0
- {haystack_ai-3.2.0rc1 → haystack_ai-3.3.0}/haystack/components/preprocessors/document_preprocessor.py +0 -0
- {haystack_ai-3.2.0rc1 → haystack_ai-3.3.0}/haystack/components/preprocessors/document_splitter.py +0 -0
- {haystack_ai-3.2.0rc1 → haystack_ai-3.3.0}/haystack/components/preprocessors/embedding_based_document_splitter.py +0 -0
- {haystack_ai-3.2.0rc1 → haystack_ai-3.3.0}/haystack/components/preprocessors/hierarchical_document_splitter.py +0 -0
- {haystack_ai-3.2.0rc1 → haystack_ai-3.3.0}/haystack/components/preprocessors/markdown_header_splitter.py +0 -0
- {haystack_ai-3.2.0rc1 → haystack_ai-3.3.0}/haystack/components/preprocessors/python_code_splitter.py +0 -0
- {haystack_ai-3.2.0rc1 → haystack_ai-3.3.0}/haystack/components/preprocessors/recursive_splitter.py +0 -0
- {haystack_ai-3.2.0rc1 → haystack_ai-3.3.0}/haystack/components/query/__init__.py +0 -0
- {haystack_ai-3.2.0rc1 → haystack_ai-3.3.0}/haystack/components/query/query_expander.py +0 -0
- {haystack_ai-3.2.0rc1 → haystack_ai-3.3.0}/haystack/components/rankers/__init__.py +0 -0
- {haystack_ai-3.2.0rc1 → haystack_ai-3.3.0}/haystack/components/rankers/llm_ranker.py +0 -0
- {haystack_ai-3.2.0rc1 → haystack_ai-3.3.0}/haystack/components/rankers/lost_in_the_middle.py +0 -0
- {haystack_ai-3.2.0rc1 → haystack_ai-3.3.0}/haystack/components/retrievers/__init__.py +0 -0
- {haystack_ai-3.2.0rc1 → haystack_ai-3.3.0}/haystack/components/retrievers/auto_merging_retriever.py +0 -0
- {haystack_ai-3.2.0rc1 → haystack_ai-3.3.0}/haystack/components/retrievers/filter_retriever.py +0 -0
- {haystack_ai-3.2.0rc1 → haystack_ai-3.3.0}/haystack/components/retrievers/in_memory/__init__.py +0 -0
- {haystack_ai-3.2.0rc1 → haystack_ai-3.3.0}/haystack/components/retrievers/multi_query_embedding_retriever.py +0 -0
- {haystack_ai-3.2.0rc1 → haystack_ai-3.3.0}/haystack/components/retrievers/multi_query_text_retriever.py +0 -0
- {haystack_ai-3.2.0rc1 → haystack_ai-3.3.0}/haystack/components/retrievers/text_embedding_retriever.py +0 -0
- {haystack_ai-3.2.0rc1 → haystack_ai-3.3.0}/haystack/components/retrievers/types/__init__.py +0 -0
- {haystack_ai-3.2.0rc1 → haystack_ai-3.3.0}/haystack/components/retrievers/types/protocol.py +0 -0
- {haystack_ai-3.2.0rc1 → haystack_ai-3.3.0}/haystack/components/routers/__init__.py +0 -0
- {haystack_ai-3.2.0rc1 → haystack_ai-3.3.0}/haystack/components/routers/document_length_router.py +0 -0
- {haystack_ai-3.2.0rc1 → haystack_ai-3.3.0}/haystack/components/routers/document_type_router.py +0 -0
- {haystack_ai-3.2.0rc1 → haystack_ai-3.3.0}/haystack/components/routers/file_type_router.py +0 -0
- {haystack_ai-3.2.0rc1 → haystack_ai-3.3.0}/haystack/components/routers/llm_messages_router.py +0 -0
- {haystack_ai-3.2.0rc1 → haystack_ai-3.3.0}/haystack/components/routers/metadata_router.py +0 -0
- {haystack_ai-3.2.0rc1 → haystack_ai-3.3.0}/haystack/components/samplers/__init__.py +0 -0
- {haystack_ai-3.2.0rc1 → haystack_ai-3.3.0}/haystack/components/samplers/top_p.py +0 -0
- {haystack_ai-3.2.0rc1 → haystack_ai-3.3.0}/haystack/components/validators/__init__.py +0 -0
- {haystack_ai-3.2.0rc1 → haystack_ai-3.3.0}/haystack/components/validators/json_schema.py +0 -0
- {haystack_ai-3.2.0rc1 → haystack_ai-3.3.0}/haystack/components/writers/__init__.py +0 -0
- {haystack_ai-3.2.0rc1 → haystack_ai-3.3.0}/haystack/components/writers/document_writer.py +0 -0
- {haystack_ai-3.2.0rc1 → haystack_ai-3.3.0}/haystack/core/__init__.py +0 -0
- {haystack_ai-3.2.0rc1 → haystack_ai-3.3.0}/haystack/core/component/__init__.py +0 -0
- {haystack_ai-3.2.0rc1 → haystack_ai-3.3.0}/haystack/core/component/component.py +0 -0
- {haystack_ai-3.2.0rc1 → haystack_ai-3.3.0}/haystack/core/component/sockets.py +0 -0
- {haystack_ai-3.2.0rc1 → haystack_ai-3.3.0}/haystack/core/component/types.py +0 -0
- {haystack_ai-3.2.0rc1 → haystack_ai-3.3.0}/haystack/core/errors.py +0 -0
- {haystack_ai-3.2.0rc1 → haystack_ai-3.3.0}/haystack/core/pipeline/__init__.py +0 -0
- {haystack_ai-3.2.0rc1 → haystack_ai-3.3.0}/haystack/core/pipeline/breakpoint.py +0 -0
- {haystack_ai-3.2.0rc1 → haystack_ai-3.3.0}/haystack/core/pipeline/component_checks.py +0 -0
- {haystack_ai-3.2.0rc1 → haystack_ai-3.3.0}/haystack/core/pipeline/descriptions.py +0 -0
- {haystack_ai-3.2.0rc1 → haystack_ai-3.3.0}/haystack/core/pipeline/draw.py +0 -0
- {haystack_ai-3.2.0rc1 → haystack_ai-3.3.0}/haystack/core/pipeline/utils.py +0 -0
- {haystack_ai-3.2.0rc1 → haystack_ai-3.3.0}/haystack/core/serialization.py +0 -0
- {haystack_ai-3.2.0rc1 → haystack_ai-3.3.0}/haystack/core/serialization_security.py +0 -0
- {haystack_ai-3.2.0rc1 → haystack_ai-3.3.0}/haystack/core/super_component/__init__.py +0 -0
- {haystack_ai-3.2.0rc1 → haystack_ai-3.3.0}/haystack/core/super_component/super_component.py +0 -0
- {haystack_ai-3.2.0rc1 → haystack_ai-3.3.0}/haystack/core/super_component/utils.py +0 -0
- {haystack_ai-3.2.0rc1 → haystack_ai-3.3.0}/haystack/data/abbreviations/de.txt +0 -0
- {haystack_ai-3.2.0rc1 → haystack_ai-3.3.0}/haystack/data/abbreviations/en.txt +0 -0
- {haystack_ai-3.2.0rc1 → haystack_ai-3.3.0}/haystack/dataclasses/__init__.py +0 -0
- {haystack_ai-3.2.0rc1 → haystack_ai-3.3.0}/haystack/dataclasses/answer.py +0 -0
- {haystack_ai-3.2.0rc1 → haystack_ai-3.3.0}/haystack/dataclasses/breakpoints.py +0 -0
- {haystack_ai-3.2.0rc1 → haystack_ai-3.3.0}/haystack/dataclasses/byte_stream.py +0 -0
- {haystack_ai-3.2.0rc1 → haystack_ai-3.3.0}/haystack/dataclasses/document.py +0 -0
- {haystack_ai-3.2.0rc1 → haystack_ai-3.3.0}/haystack/dataclasses/file_content.py +0 -0
- {haystack_ai-3.2.0rc1 → haystack_ai-3.3.0}/haystack/dataclasses/image_content.py +0 -0
- {haystack_ai-3.2.0rc1 → haystack_ai-3.3.0}/haystack/dataclasses/skill_info.py +0 -0
- {haystack_ai-3.2.0rc1 → haystack_ai-3.3.0}/haystack/dataclasses/sparse_embedding.py +0 -0
- {haystack_ai-3.2.0rc1 → haystack_ai-3.3.0}/haystack/dataclasses/streaming_chunk.py +0 -0
- {haystack_ai-3.2.0rc1 → haystack_ai-3.3.0}/haystack/document_stores/__init__.py +0 -0
- {haystack_ai-3.2.0rc1 → haystack_ai-3.3.0}/haystack/document_stores/errors/__init__.py +0 -0
- {haystack_ai-3.2.0rc1 → haystack_ai-3.3.0}/haystack/document_stores/errors/errors.py +0 -0
- {haystack_ai-3.2.0rc1 → haystack_ai-3.3.0}/haystack/document_stores/in_memory/__init__.py +0 -0
- {haystack_ai-3.2.0rc1 → haystack_ai-3.3.0}/haystack/document_stores/types/__init__.py +0 -0
- {haystack_ai-3.2.0rc1 → haystack_ai-3.3.0}/haystack/document_stores/types/filter_policy.py +0 -0
- {haystack_ai-3.2.0rc1 → haystack_ai-3.3.0}/haystack/document_stores/types/policy.py +0 -0
- {haystack_ai-3.2.0rc1 → haystack_ai-3.3.0}/haystack/document_stores/types/protocol.py +0 -0
- {haystack_ai-3.2.0rc1 → haystack_ai-3.3.0}/haystack/errors.py +0 -0
- {haystack_ai-3.2.0rc1 → haystack_ai-3.3.0}/haystack/evaluation/__init__.py +0 -0
- {haystack_ai-3.2.0rc1 → haystack_ai-3.3.0}/haystack/evaluation/eval_run_result.py +0 -0
- {haystack_ai-3.2.0rc1 → haystack_ai-3.3.0}/haystack/hooks/__init__.py +0 -0
- {haystack_ai-3.2.0rc1 → haystack_ai-3.3.0}/haystack/hooks/budget/__init__.py +0 -0
- {haystack_ai-3.2.0rc1 → haystack_ai-3.3.0}/haystack/hooks/budget/hooks.py +0 -0
- {haystack_ai-3.2.0rc1 → haystack_ai-3.3.0}/haystack/hooks/compaction/AGENTS.md +0 -0
- {haystack_ai-3.2.0rc1 → haystack_ai-3.3.0}/haystack/hooks/compaction/CLAUDE.md +0 -0
- {haystack_ai-3.2.0rc1 → haystack_ai-3.3.0}/haystack/hooks/compaction/__init__.py +0 -0
- {haystack_ai-3.2.0rc1 → haystack_ai-3.3.0}/haystack/hooks/compaction/sliding_window.py +0 -0
- {haystack_ai-3.2.0rc1 → haystack_ai-3.3.0}/haystack/hooks/compaction/summarization.py +0 -0
- {haystack_ai-3.2.0rc1 → haystack_ai-3.3.0}/haystack/hooks/compaction/tool_result_pruning.py +0 -0
- {haystack_ai-3.2.0rc1 → haystack_ai-3.3.0}/haystack/hooks/compaction/types/__init__.py +0 -0
- {haystack_ai-3.2.0rc1 → haystack_ai-3.3.0}/haystack/hooks/compaction/types/protocol.py +0 -0
- {haystack_ai-3.2.0rc1 → haystack_ai-3.3.0}/haystack/hooks/compaction/utils.py +0 -0
- {haystack_ai-3.2.0rc1 → haystack_ai-3.3.0}/haystack/hooks/from_function.py +0 -0
- {haystack_ai-3.2.0rc1 → haystack_ai-3.3.0}/haystack/hooks/human_in_the_loop/__init__.py +0 -0
- {haystack_ai-3.2.0rc1 → haystack_ai-3.3.0}/haystack/hooks/human_in_the_loop/dataclasses.py +0 -0
- {haystack_ai-3.2.0rc1 → haystack_ai-3.3.0}/haystack/hooks/human_in_the_loop/hooks.py +0 -0
- {haystack_ai-3.2.0rc1 → haystack_ai-3.3.0}/haystack/hooks/human_in_the_loop/policies.py +0 -0
- {haystack_ai-3.2.0rc1 → haystack_ai-3.3.0}/haystack/hooks/human_in_the_loop/types/__init__.py +0 -0
- {haystack_ai-3.2.0rc1 → haystack_ai-3.3.0}/haystack/hooks/human_in_the_loop/types/protocol.py +0 -0
- {haystack_ai-3.2.0rc1 → haystack_ai-3.3.0}/haystack/hooks/human_in_the_loop/user_interfaces.py +0 -0
- {haystack_ai-3.2.0rc1 → haystack_ai-3.3.0}/haystack/hooks/invocation.py +0 -0
- {haystack_ai-3.2.0rc1 → haystack_ai-3.3.0}/haystack/hooks/protocol.py +0 -0
- {haystack_ai-3.2.0rc1 → haystack_ai-3.3.0}/haystack/hooks/tool_result_offloading/__init__.py +0 -0
- {haystack_ai-3.2.0rc1 → haystack_ai-3.3.0}/haystack/hooks/tool_result_offloading/hooks.py +0 -0
- {haystack_ai-3.2.0rc1 → haystack_ai-3.3.0}/haystack/hooks/tool_result_offloading/policies.py +0 -0
- {haystack_ai-3.2.0rc1 → haystack_ai-3.3.0}/haystack/hooks/tool_result_offloading/stores.py +0 -0
- {haystack_ai-3.2.0rc1 → haystack_ai-3.3.0}/haystack/hooks/tool_result_offloading/types/__init__.py +0 -0
- {haystack_ai-3.2.0rc1 → haystack_ai-3.3.0}/haystack/hooks/tool_result_offloading/types/protocol.py +0 -0
- {haystack_ai-3.2.0rc1 → haystack_ai-3.3.0}/haystack/hooks/utils.py +0 -0
- {haystack_ai-3.2.0rc1 → haystack_ai-3.3.0}/haystack/lazy_imports.py +0 -0
- {haystack_ai-3.2.0rc1 → haystack_ai-3.3.0}/haystack/logging.py +0 -0
- {haystack_ai-3.2.0rc1 → haystack_ai-3.3.0}/haystack/marshal/__init__.py +0 -0
- {haystack_ai-3.2.0rc1 → haystack_ai-3.3.0}/haystack/marshal/protocol.py +0 -0
- {haystack_ai-3.2.0rc1 → haystack_ai-3.3.0}/haystack/marshal/yaml.py +0 -0
- {haystack_ai-3.2.0rc1 → haystack_ai-3.3.0}/haystack/py.typed +0 -0
- {haystack_ai-3.2.0rc1 → haystack_ai-3.3.0}/haystack/skill_stores/__init__.py +0 -0
- {haystack_ai-3.2.0rc1 → haystack_ai-3.3.0}/haystack/skill_stores/file_system/__init__.py +0 -0
- {haystack_ai-3.2.0rc1 → haystack_ai-3.3.0}/haystack/skill_stores/file_system/skill_store.py +0 -0
- {haystack_ai-3.2.0rc1 → haystack_ai-3.3.0}/haystack/skill_stores/types/__init__.py +0 -0
- {haystack_ai-3.2.0rc1 → haystack_ai-3.3.0}/haystack/skill_stores/types/protocol.py +0 -0
- {haystack_ai-3.2.0rc1 → haystack_ai-3.3.0}/haystack/telemetry/__init__.py +0 -0
- {haystack_ai-3.2.0rc1 → haystack_ai-3.3.0}/haystack/telemetry/_environment.py +0 -0
- {haystack_ai-3.2.0rc1 → haystack_ai-3.3.0}/haystack/telemetry/_telemetry.py +0 -0
- {haystack_ai-3.2.0rc1 → haystack_ai-3.3.0}/haystack/testing/__init__.py +0 -0
- {haystack_ai-3.2.0rc1 → haystack_ai-3.3.0}/haystack/testing/callable_serialization/random_callable.py +0 -0
- {haystack_ai-3.2.0rc1 → haystack_ai-3.3.0}/haystack/testing/document_store.py +0 -0
- {haystack_ai-3.2.0rc1 → haystack_ai-3.3.0}/haystack/testing/document_store_async.py +0 -0
- {haystack_ai-3.2.0rc1 → haystack_ai-3.3.0}/haystack/testing/factory.py +0 -0
- {haystack_ai-3.2.0rc1 → haystack_ai-3.3.0}/haystack/testing/sample_components/__init__.py +0 -0
- {haystack_ai-3.2.0rc1 → haystack_ai-3.3.0}/haystack/testing/sample_components/accumulate.py +0 -0
- {haystack_ai-3.2.0rc1 → haystack_ai-3.3.0}/haystack/testing/sample_components/add_value.py +0 -0
- {haystack_ai-3.2.0rc1 → haystack_ai-3.3.0}/haystack/testing/sample_components/concatenate.py +0 -0
- {haystack_ai-3.2.0rc1 → haystack_ai-3.3.0}/haystack/testing/sample_components/double.py +0 -0
- {haystack_ai-3.2.0rc1 → haystack_ai-3.3.0}/haystack/testing/sample_components/fstring.py +0 -0
- {haystack_ai-3.2.0rc1 → haystack_ai-3.3.0}/haystack/testing/sample_components/future_annotations.py +0 -0
- {haystack_ai-3.2.0rc1 → haystack_ai-3.3.0}/haystack/testing/sample_components/greet.py +0 -0
- {haystack_ai-3.2.0rc1 → haystack_ai-3.3.0}/haystack/testing/sample_components/hello.py +0 -0
- {haystack_ai-3.2.0rc1 → haystack_ai-3.3.0}/haystack/testing/sample_components/joiner.py +0 -0
- {haystack_ai-3.2.0rc1 → haystack_ai-3.3.0}/haystack/testing/sample_components/parity.py +0 -0
- {haystack_ai-3.2.0rc1 → haystack_ai-3.3.0}/haystack/testing/sample_components/remainder.py +0 -0
- {haystack_ai-3.2.0rc1 → haystack_ai-3.3.0}/haystack/testing/sample_components/repeat.py +0 -0
- {haystack_ai-3.2.0rc1 → haystack_ai-3.3.0}/haystack/testing/sample_components/subtract.py +0 -0
- {haystack_ai-3.2.0rc1 → haystack_ai-3.3.0}/haystack/testing/sample_components/sum.py +0 -0
- {haystack_ai-3.2.0rc1 → haystack_ai-3.3.0}/haystack/testing/sample_components/text_splitter.py +0 -0
- {haystack_ai-3.2.0rc1 → haystack_ai-3.3.0}/haystack/testing/sample_components/threshold.py +0 -0
- {haystack_ai-3.2.0rc1 → haystack_ai-3.3.0}/haystack/testing/telemetry.py +0 -0
- {haystack_ai-3.2.0rc1 → haystack_ai-3.3.0}/haystack/testing/test_utils.py +0 -0
- {haystack_ai-3.2.0rc1 → haystack_ai-3.3.0}/haystack/token_counters/__init__.py +0 -0
- {haystack_ai-3.2.0rc1 → haystack_ai-3.3.0}/haystack/token_counters/approximate_counter.py +0 -0
- {haystack_ai-3.2.0rc1 → haystack_ai-3.3.0}/haystack/token_counters/openai_counter.py +0 -0
- {haystack_ai-3.2.0rc1 → haystack_ai-3.3.0}/haystack/token_counters/tiktoken_counter.py +0 -0
- {haystack_ai-3.2.0rc1 → haystack_ai-3.3.0}/haystack/token_counters/types/__init__.py +0 -0
- {haystack_ai-3.2.0rc1 → haystack_ai-3.3.0}/haystack/token_counters/types/protocol.py +0 -0
- {haystack_ai-3.2.0rc1 → haystack_ai-3.3.0}/haystack/token_counters/utils.py +0 -0
- {haystack_ai-3.2.0rc1 → haystack_ai-3.3.0}/haystack/tools/__init__.py +0 -0
- {haystack_ai-3.2.0rc1 → haystack_ai-3.3.0}/haystack/tools/agent_tool.py +0 -0
- {haystack_ai-3.2.0rc1 → haystack_ai-3.3.0}/haystack/tools/component_tool.py +0 -0
- {haystack_ai-3.2.0rc1 → haystack_ai-3.3.0}/haystack/tools/errors.py +0 -0
- {haystack_ai-3.2.0rc1 → haystack_ai-3.3.0}/haystack/tools/from_function.py +0 -0
- {haystack_ai-3.2.0rc1 → haystack_ai-3.3.0}/haystack/tools/parameters_schema_utils.py +0 -0
- {haystack_ai-3.2.0rc1 → haystack_ai-3.3.0}/haystack/tools/pipeline_tool.py +0 -0
- {haystack_ai-3.2.0rc1 → haystack_ai-3.3.0}/haystack/tools/searchable_toolset.py +0 -0
- {haystack_ai-3.2.0rc1 → haystack_ai-3.3.0}/haystack/tools/serde_utils.py +0 -0
- {haystack_ai-3.2.0rc1 → haystack_ai-3.3.0}/haystack/tools/skills/__init__.py +0 -0
- {haystack_ai-3.2.0rc1 → haystack_ai-3.3.0}/haystack/tools/skills/skill_toolset.py +0 -0
- {haystack_ai-3.2.0rc1 → haystack_ai-3.3.0}/haystack/tools/tool.py +0 -0
- {haystack_ai-3.2.0rc1 → haystack_ai-3.3.0}/haystack/tools/tool_types.py +0 -0
- {haystack_ai-3.2.0rc1 → haystack_ai-3.3.0}/haystack/tools/toolset.py +0 -0
- {haystack_ai-3.2.0rc1 → haystack_ai-3.3.0}/haystack/tools/utils.py +0 -0
- {haystack_ai-3.2.0rc1 → haystack_ai-3.3.0}/haystack/tracing/__init__.py +0 -0
- {haystack_ai-3.2.0rc1 → haystack_ai-3.3.0}/haystack/tracing/logging_tracer.py +0 -0
- {haystack_ai-3.2.0rc1 → haystack_ai-3.3.0}/haystack/tracing/tracer.py +0 -0
- {haystack_ai-3.2.0rc1 → haystack_ai-3.3.0}/haystack/tracing/utils.py +0 -0
- {haystack_ai-3.2.0rc1 → haystack_ai-3.3.0}/haystack/utils/__init__.py +0 -0
- {haystack_ai-3.2.0rc1 → haystack_ai-3.3.0}/haystack/utils/async_utils.py +0 -0
- {haystack_ai-3.2.0rc1 → haystack_ai-3.3.0}/haystack/utils/auth.py +0 -0
- {haystack_ai-3.2.0rc1 → haystack_ai-3.3.0}/haystack/utils/azure.py +0 -0
- {haystack_ai-3.2.0rc1 → haystack_ai-3.3.0}/haystack/utils/base_serialization.py +0 -0
- {haystack_ai-3.2.0rc1 → haystack_ai-3.3.0}/haystack/utils/callable_serialization.py +0 -0
- {haystack_ai-3.2.0rc1 → haystack_ai-3.3.0}/haystack/utils/dataclasses.py +0 -0
- {haystack_ai-3.2.0rc1 → haystack_ai-3.3.0}/haystack/utils/deserialization.py +0 -0
- {haystack_ai-3.2.0rc1 → haystack_ai-3.3.0}/haystack/utils/device.py +0 -0
- {haystack_ai-3.2.0rc1 → haystack_ai-3.3.0}/haystack/utils/experimental.py +0 -0
- {haystack_ai-3.2.0rc1 → haystack_ai-3.3.0}/haystack/utils/filters.py +0 -0
- {haystack_ai-3.2.0rc1 → haystack_ai-3.3.0}/haystack/utils/hf.py +0 -0
- {haystack_ai-3.2.0rc1 → haystack_ai-3.3.0}/haystack/utils/http_client.py +0 -0
- {haystack_ai-3.2.0rc1 → haystack_ai-3.3.0}/haystack/utils/jinja2_chat_extension.py +0 -0
- {haystack_ai-3.2.0rc1 → haystack_ai-3.3.0}/haystack/utils/jinja2_extensions.py +0 -0
- {haystack_ai-3.2.0rc1 → haystack_ai-3.3.0}/haystack/utils/jinja2_sandbox.py +0 -0
- {haystack_ai-3.2.0rc1 → haystack_ai-3.3.0}/haystack/utils/jupyter.py +0 -0
- {haystack_ai-3.2.0rc1 → haystack_ai-3.3.0}/haystack/utils/misc.py +0 -0
- {haystack_ai-3.2.0rc1 → haystack_ai-3.3.0}/haystack/utils/requests_utils.py +0 -0
- {haystack_ai-3.2.0rc1 → haystack_ai-3.3.0}/haystack/utils/type_serialization.py +0 -0
- {haystack_ai-3.2.0rc1 → haystack_ai-3.3.0}/haystack/utils/url_validation.py +0 -0
- {haystack_ai-3.2.0rc1 → haystack_ai-3.3.0}/haystack/version.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: haystack-ai
|
|
3
|
-
Version: 3.
|
|
3
|
+
Version: 3.3.0
|
|
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
|
|
@@ -25,6 +25,7 @@ Classifier: Programming Language :: Python :: 3.13
|
|
|
25
25
|
Classifier: Programming Language :: Python :: 3.14
|
|
26
26
|
Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
|
|
27
27
|
Requires-Python: >=3.10
|
|
28
|
+
Requires-Dist: anyio>=4.14.2
|
|
28
29
|
Requires-Dist: docstring-parser
|
|
29
30
|
Requires-Dist: filetype
|
|
30
31
|
Requires-Dist: httpx
|
|
@@ -56,16 +57,6 @@ Description-Content-Type: text/markdown
|
|
|
56
57
|
| Meta | [](https://discord.com/invite/qZxjM4bAHU) [](https://twitter.com/haystack_ai) |
|
|
57
58
|
</div>
|
|
58
59
|
|
|
59
|
-
<div align="center">
|
|
60
|
-
|
|
61
|
-
# 🎉🎊✨ Haystack 3.0 is out! ✨🎊🎉
|
|
62
|
-
|
|
63
|
-
### Read the announcement [here](https://haystack.deepset.ai/blog/haystack-3-release)!
|
|
64
|
-
|
|
65
|
-
## 🥳 🎈 🎆 🪅 🎇 🍾 🥂 🎁 🌈
|
|
66
|
-
|
|
67
|
-
</div>
|
|
68
|
-
|
|
69
60
|
[Haystack](https://haystack.deepset.ai/) is an open-source AI orchestration framework for building production-ready LLM applications in Python.
|
|
70
61
|
|
|
71
62
|
Design modular pipelines and agent workflows with explicit control over retrieval, routing, memory, and generation. Build scalable RAG systems, multimodal applications, semantic search, question answering, and autonomous agents, all in a transparent architecture that lets you experiment, customize deeply, and deploy with confidence.
|
|
@@ -9,16 +9,6 @@
|
|
|
9
9
|
| Meta | [](https://discord.com/invite/qZxjM4bAHU) [](https://twitter.com/haystack_ai) |
|
|
10
10
|
</div>
|
|
11
11
|
|
|
12
|
-
<div align="center">
|
|
13
|
-
|
|
14
|
-
# 🎉🎊✨ Haystack 3.0 is out! ✨🎊🎉
|
|
15
|
-
|
|
16
|
-
### Read the announcement [here](https://haystack.deepset.ai/blog/haystack-3-release)!
|
|
17
|
-
|
|
18
|
-
## 🥳 🎈 🎆 🪅 🎇 🍾 🥂 🎁 🌈
|
|
19
|
-
|
|
20
|
-
</div>
|
|
21
|
-
|
|
22
12
|
[Haystack](https://haystack.deepset.ai/) is an open-source AI orchestration framework for building production-ready LLM applications in Python.
|
|
23
13
|
|
|
24
14
|
Design modular pipelines and agent workflows with explicit control over retrieval, routing, memory, and generation. Build scalable RAG systems, multimodal applications, semantic search, question answering, and autonomous agents, all in a transparent architecture that lets you experiment, customize deeply, and deploy with confidence.
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
3.3.0
|
|
@@ -866,9 +866,10 @@ class Agent:
|
|
|
866
866
|
- "exit_reason": Why the Agent stopped, useful for routing the output downstream (e.g. with a
|
|
867
867
|
`ConditionalRouter`). One of: `"text"` (the model returned a complete reply with no tool calls),
|
|
868
868
|
`"length"` or `"content_filter"` (the model returned an incomplete reply, which may contain partial
|
|
869
|
-
text), the name of the tool that satisfied a tool exit condition (
|
|
870
|
-
|
|
871
|
-
|
|
869
|
+
text), the name of the tool that satisfied a tool exit condition (its result is the last tool message in
|
|
870
|
+
`messages` whose `tool_call_result.origin.tool_name` matches it, which may not be `last_message` when
|
|
871
|
+
the model called several tools at once), or `"max_agent_steps"` (the Agent hit `max_agent_steps` before
|
|
872
|
+
meeting an exit condition), or a custom reason a hook supplied through the `stop_run` state key.
|
|
872
873
|
- Any additional keys defined in the `state_schema`.
|
|
873
874
|
"""
|
|
874
875
|
agent_inputs = {"messages": messages, "streaming_callback": streaming_callback, **kwargs}
|
|
@@ -952,9 +953,10 @@ class Agent:
|
|
|
952
953
|
- "exit_reason": Why the Agent stopped, useful for routing the output downstream (e.g. with a
|
|
953
954
|
`ConditionalRouter`). One of: `"text"` (the model returned a complete reply with no tool calls),
|
|
954
955
|
`"length"` or `"content_filter"` (the model returned an incomplete reply, which may contain partial
|
|
955
|
-
text), the name of the tool that satisfied a tool exit condition (
|
|
956
|
-
|
|
957
|
-
|
|
956
|
+
text), the name of the tool that satisfied a tool exit condition (its result is the last tool message in
|
|
957
|
+
`messages` whose `tool_call_result.origin.tool_name` matches it, which may not be `last_message` when
|
|
958
|
+
the model called several tools at once), or `"max_agent_steps"` (the Agent hit `max_agent_steps` before
|
|
959
|
+
meeting an exit condition), or a custom reason a hook supplied through the `stop_run` state key.
|
|
958
960
|
- Any additional keys defined in the `state_schema`.
|
|
959
961
|
"""
|
|
960
962
|
agent_inputs = {"messages": messages, "streaming_callback": streaming_callback, **kwargs}
|
|
@@ -449,8 +449,9 @@ def _schedule_tool_calls(tool_calls: list[ToolCall], tools: list[Tool]) -> list[
|
|
|
449
449
|
once — cannot be ordered by the read-after-write rule alone, so they are broken deterministically by call order
|
|
450
450
|
(the lowest-index remaining call runs next, on its own).
|
|
451
451
|
|
|
452
|
-
|
|
453
|
-
|
|
452
|
+
Write-write overlaps alone create no dependency. Outputs are merged into State after each batch, in the
|
|
453
|
+
LLM's requested order within that batch, so subsequent batches see the updated values. This keeps merging
|
|
454
|
+
deterministic, but does not guarantee that writes follow the LLM's requested order across batches.
|
|
454
455
|
|
|
455
456
|
:param tool_calls: The tool calls to schedule, in call order.
|
|
456
457
|
:param tools: The resolved Tool for each entry in `tool_calls` (parallel list).
|
|
@@ -498,8 +499,7 @@ def _finalize_tool_result(
|
|
|
498
499
|
Turn a single tool invocation result into a tool-result ChatMessage, merging outputs into State.
|
|
499
500
|
|
|
500
501
|
On a `ToolInvocationError`, either re-raise (when `raise_on_failure`) or return an error message. Otherwise
|
|
501
|
-
merge the tool's outputs into State
|
|
502
|
-
result message.
|
|
502
|
+
merge the tool's outputs into State and build the result message.
|
|
503
503
|
"""
|
|
504
504
|
if isinstance(result, ToolInvocationError):
|
|
505
505
|
if raise_on_failure:
|
{haystack_ai-3.2.0rc1 → haystack_ai-3.3.0}/haystack/components/builders/chat_prompt_builder.py
RENAMED
|
@@ -176,12 +176,13 @@ class ChatPromptBuilder:
|
|
|
176
176
|
# infer variables from template
|
|
177
177
|
if message.text is None:
|
|
178
178
|
raise ValueError(NO_TEXT_ERROR_MESSAGE.format(role=message.role.value, message=message))
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
179
|
+
for text in message.texts:
|
|
180
|
+
if "templatize_part" in text:
|
|
181
|
+
raise ValueError(FILTER_NOT_ALLOWED_ERROR_MESSAGE)
|
|
182
|
+
assigned_variables, template_variables = _extract_template_variables_and_assignments(
|
|
183
|
+
env=self._env, template=text
|
|
184
|
+
)
|
|
185
|
+
extracted_variables += list(template_variables - assigned_variables)
|
|
185
186
|
elif isinstance(template, str):
|
|
186
187
|
assigned_variables, template_variables = _extract_template_variables_and_assignments(
|
|
187
188
|
env=self._env, template=template
|
|
@@ -263,12 +264,15 @@ class ChatPromptBuilder:
|
|
|
263
264
|
self._validate_variables(set(template_variables_combined.keys()))
|
|
264
265
|
if message.text is None:
|
|
265
266
|
raise ValueError(NO_TEXT_ERROR_MESSAGE.format(role=message.role.value, message=message))
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
267
|
+
rendered_content = list(message._content)
|
|
268
|
+
for index, part in enumerate(rendered_content):
|
|
269
|
+
if isinstance(part, TextContent):
|
|
270
|
+
if "templatize_part" in part.text:
|
|
271
|
+
raise ValueError(FILTER_NOT_ALLOWED_ERROR_MESSAGE)
|
|
272
|
+
rendered_text = self._env.from_string(part.text).render(template_variables_combined)
|
|
273
|
+
rendered_content[index] = TextContent(text=rendered_text)
|
|
270
274
|
# use dataclasses.replace to avoid in-place mutation of the original message
|
|
271
|
-
rendered_message: ChatMessage = replace(message, _content=
|
|
275
|
+
rendered_message: ChatMessage = replace(message, _content=rendered_content)
|
|
272
276
|
processed_messages.append(rendered_message)
|
|
273
277
|
else:
|
|
274
278
|
processed_messages.append(message)
|
|
@@ -5,6 +5,7 @@
|
|
|
5
5
|
import csv
|
|
6
6
|
import io
|
|
7
7
|
import os
|
|
8
|
+
import re
|
|
8
9
|
from dataclasses import asdict, dataclass
|
|
9
10
|
from enum import Enum
|
|
10
11
|
from io import StringIO
|
|
@@ -18,10 +19,15 @@ from haystack.lazy_imports import LazyImport
|
|
|
18
19
|
|
|
19
20
|
logger = logging.getLogger(__name__)
|
|
20
21
|
|
|
22
|
+
# A Markdown table row ends at a line break and its columns are separated by pipes, so
|
|
23
|
+
# neither can survive inside a cell.
|
|
24
|
+
_MARKDOWN_CELL_BREAK_PATTERN = re.compile(r"\s*(?:\r\n|\r|\n)\s*")
|
|
25
|
+
_MARKDOWN_CELL_PIPE_PATTERN = re.compile(r"(?<!\\)(\\*)\|")
|
|
26
|
+
|
|
21
27
|
with LazyImport("Run 'pip install python-docx'") as docx_import:
|
|
22
28
|
import docx
|
|
23
29
|
from docx.document import Document as DocxDocument
|
|
24
|
-
from docx.table import Table
|
|
30
|
+
from docx.table import Table, _Cell
|
|
25
31
|
from docx.text.hyperlink import Hyperlink
|
|
26
32
|
from docx.text.paragraph import Paragraph
|
|
27
33
|
from docx.text.run import Run
|
|
@@ -305,6 +311,31 @@ class DOCXToDocument:
|
|
|
305
311
|
|
|
306
312
|
return text
|
|
307
313
|
|
|
314
|
+
@staticmethod
|
|
315
|
+
def _escape_markdown_cell(text: str) -> str:
|
|
316
|
+
"""
|
|
317
|
+
Makes a cell's text safe to put between the pipes of a Markdown table row.
|
|
318
|
+
|
|
319
|
+
A cell spanning several paragraphs arrives with newlines in it, which would end
|
|
320
|
+
the row in the middle, and a pipe in a cell would be read as a column separator.
|
|
321
|
+
|
|
322
|
+
:param text: The cell text.
|
|
323
|
+
:returns: The text with line breaks collapsed and pipes escaped.
|
|
324
|
+
"""
|
|
325
|
+
text = _MARKDOWN_CELL_BREAK_PATTERN.sub(" ", text)
|
|
326
|
+
# The backslash run in front of the pipe is doubled first, so a backslash the
|
|
327
|
+
# cell already contains cannot consume the escape.
|
|
328
|
+
return _MARKDOWN_CELL_PIPE_PATTERN.sub(lambda match: match.group(1) * 2 + r"\|", text)
|
|
329
|
+
|
|
330
|
+
def _cell_text(self, cell: "_Cell") -> str:
|
|
331
|
+
"""
|
|
332
|
+
Returns a table cell's text with links formatted like links in body paragraphs.
|
|
333
|
+
|
|
334
|
+
:param cell: The DOCX table cell.
|
|
335
|
+
:returns: The cell's paragraphs joined by newlines, as `cell.text` joins them.
|
|
336
|
+
"""
|
|
337
|
+
return "\n".join(self._process_links_in_paragraph(paragraph) for paragraph in cell.paragraphs)
|
|
338
|
+
|
|
308
339
|
def _table_to_markdown(self, table: "Table") -> str:
|
|
309
340
|
"""
|
|
310
341
|
Converts a DOCX table to a Markdown string.
|
|
@@ -315,10 +346,10 @@ class DOCXToDocument:
|
|
|
315
346
|
markdown: list[str] = []
|
|
316
347
|
max_col_widths: list[int] = []
|
|
317
348
|
|
|
318
|
-
# Calculate max width for each column
|
|
349
|
+
# Calculate max width for each column, on the escaped text that is written out
|
|
319
350
|
for row in table.rows:
|
|
320
351
|
for i, cell in enumerate(row.cells):
|
|
321
|
-
cell_text = cell.
|
|
352
|
+
cell_text = self._escape_markdown_cell(self._cell_text(cell).strip())
|
|
322
353
|
if i >= len(max_col_widths):
|
|
323
354
|
max_col_widths.append(len(cell_text))
|
|
324
355
|
else:
|
|
@@ -326,7 +357,10 @@ class DOCXToDocument:
|
|
|
326
357
|
|
|
327
358
|
# Process rows
|
|
328
359
|
for i, row in enumerate(table.rows):
|
|
329
|
-
md_row = [
|
|
360
|
+
md_row = [
|
|
361
|
+
self._escape_markdown_cell(self._cell_text(cell).strip()).ljust(max_col_widths[j])
|
|
362
|
+
for j, cell in enumerate(row.cells)
|
|
363
|
+
]
|
|
330
364
|
markdown.append("| " + " | ".join(md_row) + " |")
|
|
331
365
|
|
|
332
366
|
# Add separator after header row
|
|
@@ -348,7 +382,7 @@ class DOCXToDocument:
|
|
|
348
382
|
|
|
349
383
|
# Process rows
|
|
350
384
|
for row in table.rows:
|
|
351
|
-
csv_row = [cell.
|
|
385
|
+
csv_row = [self._cell_text(cell).strip() for cell in row.cells]
|
|
352
386
|
csv_writer.writerow(csv_row)
|
|
353
387
|
|
|
354
388
|
# Get the CSV as a string and strip any trailing newlines
|
{haystack_ai-3.2.0rc1 → haystack_ai-3.3.0}/haystack/components/converters/image/document_to_image.py
RENAMED
|
@@ -114,8 +114,8 @@ class DocumentToImageContent:
|
|
|
114
114
|
Dictionary containing one key:
|
|
115
115
|
- "image_contents": ImageContents created from the processed documents. These contain base64-encoded image
|
|
116
116
|
data and metadata. The order corresponds to the order of the input documents. A document that is
|
|
117
|
-
missing the required metadata keys, has an invalid file path,
|
|
118
|
-
None in its position and a logged warning with the reason.
|
|
117
|
+
missing the required metadata keys, has an invalid file path, has an unsupported MIME type, or points
|
|
118
|
+
to a PDF page that cannot be converted gets None in its position and a logged warning with the reason.
|
|
119
119
|
"""
|
|
120
120
|
if not documents:
|
|
121
121
|
return {"image_contents": []}
|
{haystack_ai-3.2.0rc1 → haystack_ai-3.3.0}/haystack/components/converters/image/image_utils.py
RENAMED
|
@@ -164,7 +164,11 @@ def _convert_pdf_to_images(
|
|
|
164
164
|
|
|
165
165
|
for page_number in resolved_page_range:
|
|
166
166
|
if page_number < 1 or page_number > num_pages:
|
|
167
|
-
logger.warning(
|
|
167
|
+
logger.warning(
|
|
168
|
+
"Page {page_number} is out of range for the PDF file {file_path}. Skipping it.",
|
|
169
|
+
page_number=page_number,
|
|
170
|
+
file_path=bytestream.meta.get("file_path"),
|
|
171
|
+
)
|
|
168
172
|
continue
|
|
169
173
|
|
|
170
174
|
# Get dimensions of the page
|
|
@@ -312,7 +316,8 @@ def _batch_convert_pdf_pages_to_images(
|
|
|
312
316
|
:param size: Optional tuple of width and height to resize the images to.
|
|
313
317
|
:param return_base64: If True, return base64 encoded images instead of PIL images.
|
|
314
318
|
|
|
315
|
-
:returns: Dictionary mapping document indices to images (PIL.Image or base64 string).
|
|
319
|
+
:returns: Dictionary mapping document indices to images (PIL.Image or base64 string). Documents whose page could
|
|
320
|
+
not be converted are not included.
|
|
316
321
|
"""
|
|
317
322
|
if not pdf_page_infos:
|
|
318
323
|
return {}
|
|
@@ -325,16 +330,18 @@ def _batch_convert_pdf_pages_to_images(
|
|
|
325
330
|
|
|
326
331
|
for pdf_path, page_infos_for_pdf in page_infos_by_pdf_path.items():
|
|
327
332
|
page_numbers_to_convert = [info["page_number"] for info in page_infos_for_pdf]
|
|
328
|
-
bytestream = ByteStream.from_file_path(pdf_path)
|
|
333
|
+
bytestream = ByteStream.from_file_path(pdf_path, meta={"file_path": str(pdf_path)})
|
|
329
334
|
|
|
330
335
|
converted_pages = _convert_pdf_to_images(
|
|
331
336
|
bytestream=bytestream, return_base64=return_base64, page_range=page_numbers_to_convert, size=size
|
|
332
337
|
)
|
|
333
338
|
|
|
334
|
-
# Map results back to document indices
|
|
339
|
+
# Map results back to document indices. Pages that could not be converted (out of range, or the PDF
|
|
340
|
+
# could not be read) are missing from the results, so their documents are left out.
|
|
335
341
|
page_number_to_image = dict(converted_pages)
|
|
336
342
|
for page_info in page_infos_for_pdf:
|
|
337
|
-
|
|
343
|
+
if page_info["page_number"] in page_number_to_image:
|
|
344
|
+
converted_images_by_doc_index[page_info["doc_idx"]] = page_number_to_image[page_info["page_number"]]
|
|
338
345
|
|
|
339
346
|
# mypy is not able to infer that we match the declared return type
|
|
340
347
|
return converted_images_by_doc_index # type: ignore[return-value]
|
|
@@ -238,6 +238,9 @@ class JSONConverter:
|
|
|
238
238
|
logger.warning("Expected a scalar value but got {obj}. Skipping it.", obj=obj)
|
|
239
239
|
continue
|
|
240
240
|
|
|
241
|
+
if text is not None and not isinstance(text, str):
|
|
242
|
+
text = str(text)
|
|
243
|
+
|
|
241
244
|
meta = {}
|
|
242
245
|
if meta_fields == "*":
|
|
243
246
|
meta = {k: v for k, v in obj.items() if k != self._content_key}
|
|
@@ -234,6 +234,15 @@ class XLSXToDocument:
|
|
|
234
234
|
"missingval": "",
|
|
235
235
|
**self.table_format_kwargs,
|
|
236
236
|
}
|
|
237
|
+
if resolved_kwargs["tablefmt"] == "pipe":
|
|
238
|
+
value = value.replace(
|
|
239
|
+
{
|
|
240
|
+
r"\r\n|\r|\n": " ", # keep in-cell line breaks from creating extra Markdown rows
|
|
241
|
+
r"(\\*)\|": r"\1\1\\|", # escape pipes but preserve any preceding literal backslashes
|
|
242
|
+
},
|
|
243
|
+
regex=True,
|
|
244
|
+
)
|
|
245
|
+
|
|
237
246
|
# to_markdown uses tabulate, whose missingval only covers None: a NaN
|
|
238
247
|
# reaches the formatter as a number and is written out as "nan". Replace
|
|
239
248
|
# the empty cells with None so an empty cell reads as empty, the way
|
{haystack_ai-3.2.0rc1 → haystack_ai-3.3.0}/haystack/components/embedders/mock_document_embedder.py
RENAMED
|
@@ -81,13 +81,13 @@ class MockDocumentEmbedder:
|
|
|
81
81
|
:param meta_fields_to_embed: List of metadata fields to embed along with the document text.
|
|
82
82
|
:param embedding_separator: Separator used to concatenate the metadata fields to the document text.
|
|
83
83
|
:param progress_bar: Accepted for interface compatibility with real Document Embedders and ignored.
|
|
84
|
-
:raises ValueError: If both `embedding` and `embedding_fn` are provided, if `
|
|
85
|
-
if `
|
|
84
|
+
:raises ValueError: If both `embedding` and `embedding_fn` are provided, if `embedding` is an empty list,
|
|
85
|
+
or if neither is provided and `dimension` is not positive.
|
|
86
86
|
:raises TypeError: If `embedding` is not a sequence of numbers.
|
|
87
87
|
"""
|
|
88
88
|
if embedding is not None and embedding_fn is not None:
|
|
89
89
|
raise ValueError("Pass either 'embedding' or 'embedding_fn', not both.")
|
|
90
|
-
if dimension <= 0:
|
|
90
|
+
if embedding is None and embedding_fn is None and dimension <= 0:
|
|
91
91
|
raise ValueError("'dimension' must be a positive integer.")
|
|
92
92
|
|
|
93
93
|
self.embedding = _coerce_embedding(embedding, name="'embedding'") if embedding is not None else None
|
{haystack_ai-3.2.0rc1 → haystack_ai-3.3.0}/haystack/components/embedders/mock_text_embedder.py
RENAMED
|
@@ -70,13 +70,13 @@ class MockTextEmbedder:
|
|
|
70
70
|
:param meta: Additional metadata merged into the output `meta`.
|
|
71
71
|
:param prefix: A string to add at the beginning of the text before embedding.
|
|
72
72
|
:param suffix: A string to add at the end of the text before embedding.
|
|
73
|
-
:raises ValueError: If both `embedding` and `embedding_fn` are provided, if `
|
|
74
|
-
if `
|
|
73
|
+
:raises ValueError: If both `embedding` and `embedding_fn` are provided, if `embedding` is an empty list,
|
|
74
|
+
or if neither is provided and `dimension` is not positive.
|
|
75
75
|
:raises TypeError: If `embedding` is not a sequence of numbers.
|
|
76
76
|
"""
|
|
77
77
|
if embedding is not None and embedding_fn is not None:
|
|
78
78
|
raise ValueError("Pass either 'embedding' or 'embedding_fn', not both.")
|
|
79
|
-
if dimension <= 0:
|
|
79
|
+
if embedding is None and embedding_fn is None and dimension <= 0:
|
|
80
80
|
raise ValueError("'dimension' must be a positive integer.")
|
|
81
81
|
|
|
82
82
|
self.embedding = _coerce_embedding(embedding, name="'embedding'") if embedding is not None else None
|
{haystack_ai-3.2.0rc1 → haystack_ai-3.3.0}/haystack/components/extractors/llm_metadata_extractor.py
RENAMED
|
@@ -5,10 +5,8 @@
|
|
|
5
5
|
import copy
|
|
6
6
|
import json
|
|
7
7
|
from asyncio import Semaphore, gather
|
|
8
|
-
from collections.abc import Iterable
|
|
9
8
|
from concurrent.futures import ThreadPoolExecutor
|
|
10
9
|
from dataclasses import replace
|
|
11
|
-
from functools import partial
|
|
12
10
|
from typing import Any
|
|
13
11
|
|
|
14
12
|
from jinja2 import meta
|
|
@@ -297,10 +295,17 @@ class LLMMetadataExtractor:
|
|
|
297
295
|
|
|
298
296
|
return all_prompts
|
|
299
297
|
|
|
300
|
-
|
|
301
|
-
|
|
298
|
+
@staticmethod
|
|
299
|
+
def _fail(document: Document, error: str, response: ChatMessage | None = None) -> tuple[Document, bool]:
|
|
300
|
+
"""Return a copy of ``document`` with the failure metadata set, flagged as failed."""
|
|
301
|
+
new_meta = {**document.meta, "metadata_extraction_error": error, "metadata_extraction_response": response}
|
|
302
|
+
return replace(document, meta=new_meta), False
|
|
303
|
+
|
|
304
|
+
def _run_on_thread(
|
|
305
|
+
self, document: Document, prompt: ChatMessage | None, parent_span: tracing.Span | None = None
|
|
306
|
+
) -> tuple[Document, bool]:
|
|
302
307
|
if prompt is None:
|
|
303
|
-
return
|
|
308
|
+
return self._fail(document, "Document has no content, skipping LLM call.")
|
|
304
309
|
|
|
305
310
|
try:
|
|
306
311
|
with _trace_chat_generator_run(
|
|
@@ -316,13 +321,14 @@ class LLMMetadataExtractor:
|
|
|
316
321
|
class_name=self._chat_generator.__class__.__name__,
|
|
317
322
|
error=e,
|
|
318
323
|
)
|
|
319
|
-
|
|
320
|
-
return result
|
|
324
|
+
return self._fail(document, "LLM failed with exception: " + str(e))
|
|
325
|
+
return self._process_reply(document, result["replies"][0])
|
|
321
326
|
|
|
322
|
-
async def _run_async(
|
|
323
|
-
|
|
327
|
+
async def _run_async(
|
|
328
|
+
self, document: Document, prompt: ChatMessage | None, parent_span: tracing.Span | None = None
|
|
329
|
+
) -> tuple[Document, bool]:
|
|
324
330
|
if prompt is None:
|
|
325
|
-
return
|
|
331
|
+
return self._fail(document, "Document has no content, skipping LLM call.")
|
|
326
332
|
|
|
327
333
|
try:
|
|
328
334
|
with _trace_chat_generator_run(
|
|
@@ -338,45 +344,33 @@ class LLMMetadataExtractor:
|
|
|
338
344
|
class_name=self._chat_generator.__class__.__name__,
|
|
339
345
|
error=e,
|
|
340
346
|
)
|
|
341
|
-
|
|
342
|
-
return result
|
|
347
|
+
return self._fail(document, "LLM failed with exception: " + str(e))
|
|
348
|
+
return self._process_reply(document, result["replies"][0])
|
|
343
349
|
|
|
344
|
-
def
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
successful_documents = []
|
|
348
|
-
failed_documents = []
|
|
349
|
-
for document, result in zip(documents, results, strict=True):
|
|
350
|
-
new_meta = {**document.meta}
|
|
351
|
-
if "error" in result:
|
|
352
|
-
new_meta["metadata_extraction_error"] = result["error"]
|
|
353
|
-
new_meta["metadata_extraction_response"] = None
|
|
354
|
-
failed_documents.append(replace(document, meta=new_meta))
|
|
355
|
-
continue
|
|
350
|
+
def _process_reply(self, document: Document, reply: ChatMessage) -> tuple[Document, bool]:
|
|
351
|
+
"""
|
|
352
|
+
Parse one document's LLM reply into metadata.
|
|
356
353
|
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
failed_documents.append(replace(document, meta=new_meta))
|
|
372
|
-
continue
|
|
354
|
+
Returns (updated_document, True if success else False).
|
|
355
|
+
"""
|
|
356
|
+
try:
|
|
357
|
+
parsed_metadata = _parse_dict_from_json(
|
|
358
|
+
reply.text or "", expected_keys=self.expected_keys, raise_on_failure=True
|
|
359
|
+
)
|
|
360
|
+
except (ValueError, json.JSONDecodeError) as e:
|
|
361
|
+
logger.warning(
|
|
362
|
+
"Response from the LLM is not valid JSON or missing expected keys. Received output: {response}",
|
|
363
|
+
response=reply.text,
|
|
364
|
+
)
|
|
365
|
+
if self.raise_on_failure:
|
|
366
|
+
raise
|
|
367
|
+
return self._fail(document, "Response is not valid JSON or missing keys. Error: " + str(e), response=reply)
|
|
373
368
|
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
return successful_documents, failed_documents
|
|
369
|
+
new_meta = {**document.meta, **parsed_metadata}
|
|
370
|
+
# Remove metadata_extraction_error and metadata_extraction_response if present from previous runs.
|
|
371
|
+
new_meta.pop("metadata_extraction_error", None)
|
|
372
|
+
new_meta.pop("metadata_extraction_response", None)
|
|
373
|
+
return replace(document, meta=new_meta), True
|
|
380
374
|
|
|
381
375
|
@component.output_types(documents=list[Document], failed_documents=list[Document])
|
|
382
376
|
def run(self, documents: list[Document], page_range: list[str | int] | None = None) -> dict[str, Any]:
|
|
@@ -421,9 +415,19 @@ class LLMMetadataExtractor:
|
|
|
421
415
|
|
|
422
416
|
# Run the LLM on each prompt
|
|
423
417
|
with ThreadPoolExecutor(max_workers=self.max_workers) as executor:
|
|
424
|
-
results = executor.map(
|
|
418
|
+
results = executor.map(
|
|
419
|
+
lambda document, prompt: self._run_on_thread(document, prompt, parent_span=parent_span),
|
|
420
|
+
documents,
|
|
421
|
+
all_prompts,
|
|
422
|
+
)
|
|
425
423
|
|
|
426
|
-
successful_documents
|
|
424
|
+
successful_documents = []
|
|
425
|
+
failed_documents = []
|
|
426
|
+
for doc, success in results:
|
|
427
|
+
if success:
|
|
428
|
+
successful_documents.append(doc)
|
|
429
|
+
else:
|
|
430
|
+
failed_documents.append(doc)
|
|
427
431
|
|
|
428
432
|
return {"documents": successful_documents, "failed_documents": failed_documents}
|
|
429
433
|
|
|
@@ -474,12 +478,20 @@ class LLMMetadataExtractor:
|
|
|
474
478
|
# Run the LLM on each prompt, bounding concurrency per task so max_workers is enforced.
|
|
475
479
|
sem = Semaphore(max(1, self.max_workers))
|
|
476
480
|
|
|
477
|
-
async def _bounded_run(prompt: ChatMessage | None) ->
|
|
481
|
+
async def _bounded_run(document: Document, prompt: ChatMessage | None) -> tuple[Document, bool]:
|
|
478
482
|
async with sem:
|
|
479
|
-
return await self._run_async(prompt, parent_span=parent_span)
|
|
483
|
+
return await self._run_async(document, prompt, parent_span=parent_span)
|
|
480
484
|
|
|
481
|
-
results = await gather(
|
|
485
|
+
results = await gather(
|
|
486
|
+
*[_bounded_run(document, prompt) for document, prompt in zip(documents, all_prompts, strict=True)]
|
|
487
|
+
)
|
|
482
488
|
|
|
483
|
-
successful_documents
|
|
489
|
+
successful_documents = []
|
|
490
|
+
failed_documents = []
|
|
491
|
+
for doc, success in results:
|
|
492
|
+
if success:
|
|
493
|
+
successful_documents.append(doc)
|
|
494
|
+
else:
|
|
495
|
+
failed_documents.append(doc)
|
|
484
496
|
|
|
485
497
|
return {"documents": successful_documents, "failed_documents": failed_documents}
|
|
@@ -143,7 +143,7 @@ class LinkContentFetcher:
|
|
|
143
143
|
self.retry_attempts = retry_attempts
|
|
144
144
|
self.timeout = timeout
|
|
145
145
|
self.http2 = http2
|
|
146
|
-
self.client_kwargs = client_kwargs or {}
|
|
146
|
+
self.client_kwargs = dict(client_kwargs or {})
|
|
147
147
|
self.request_headers = request_headers or {}
|
|
148
148
|
|
|
149
149
|
# Configure default client settings
|
|
@@ -113,8 +113,10 @@ class BranchJoiner:
|
|
|
113
113
|
:returns:
|
|
114
114
|
A deserialized `BranchJoiner` instance.
|
|
115
115
|
"""
|
|
116
|
-
data
|
|
117
|
-
|
|
116
|
+
# Copy so the caller's data keeps the serialized type and can be deserialized again
|
|
117
|
+
init_parameters = dict(data["init_parameters"])
|
|
118
|
+
init_parameters["type_"] = deserialize_type(init_parameters["type_"])
|
|
119
|
+
return default_from_dict(cls, {**data, "init_parameters": init_parameters})
|
|
118
120
|
|
|
119
121
|
def run(self, **kwargs: Any) -> dict[str, Any]:
|
|
120
122
|
"""
|
{haystack_ai-3.2.0rc1 → haystack_ai-3.3.0}/haystack/components/preprocessors/sentence_tokenizer.py
RENAMED
|
@@ -42,6 +42,9 @@ ISO639_TO_NLTK = {
|
|
|
42
42
|
|
|
43
43
|
QUOTE_SPANS_RE = re.compile(r'"[^"]*"|\'[^\']*\'')
|
|
44
44
|
|
|
45
|
+
# Closing brackets and quotes that can trail a sentence ending, as in `He said "Hi." Bye.` or `(Hi.) Bye.`
|
|
46
|
+
SENTENCE_CLOSING_CHARS_RE = r"[\)\]}\"'’”»]*"
|
|
47
|
+
|
|
45
48
|
if nltk_imports.is_successful():
|
|
46
49
|
|
|
47
50
|
def load_sentence_tokenizer(
|
|
@@ -105,8 +108,10 @@ if nltk_imports.is_successful():
|
|
|
105
108
|
self._period_context_fmt
|
|
106
109
|
% {
|
|
107
110
|
"NonWord": self._re_non_word_chars,
|
|
108
|
-
# SentEndChars might be followed by closing brackets, so we match them here.
|
|
109
|
-
|
|
111
|
+
# SentEndChars might be followed by closing brackets or quotes, so we match them here.
|
|
112
|
+
# If we don't, the whitespace after e.g. `."` is behind the closing quote, the pattern
|
|
113
|
+
# above can't reach it and it ends up in none of the sentences we return.
|
|
114
|
+
"SentEndChars": self._re_sent_end_chars + SENTENCE_CLOSING_CHARS_RE,
|
|
110
115
|
},
|
|
111
116
|
re.UNICODE | re.VERBOSE,
|
|
112
117
|
)
|
|
@@ -201,6 +206,10 @@ class SentenceSplitter:
|
|
|
201
206
|
start, end = span
|
|
202
207
|
next_start, next_end = next_span
|
|
203
208
|
|
|
209
|
+
# with keep_white_spaces=True a span also covers the whitespace up to the next sentence, the rules below
|
|
210
|
+
# look at where the sentence itself ends
|
|
211
|
+
end = start + len(text[start:end].rstrip())
|
|
212
|
+
|
|
204
213
|
# sentence. sentence"\nsentence -> no split (end << quote_end)
|
|
205
214
|
# sentence.", sentence -> no split (end < quote_end)
|
|
206
215
|
# sentence?", sentence -> no split (end < quote_end)
|
|
@@ -214,6 +223,16 @@ class SentenceSplitter:
|
|
|
214
223
|
# question is cited
|
|
215
224
|
return True
|
|
216
225
|
|
|
226
|
+
# sentence.", sentence -> no split (end == quote_end): widening the closing-char class (see
|
|
227
|
+
# period_context_re) moves the boundary just past the closing quote instead of leaving it inside,
|
|
228
|
+
# so anything other than whitespace directly after the quote continues the sentence.
|
|
229
|
+
if any(
|
|
230
|
+
quote_start < end == quote_end and quote_end < len(text) and not text[quote_end].isspace()
|
|
231
|
+
for quote_start, quote_end in quote_spans
|
|
232
|
+
):
|
|
233
|
+
# e.g. `He said "Hi.", then left.` -> the punctuation continues the sentence
|
|
234
|
+
return True
|
|
235
|
+
|
|
217
236
|
if re.search(r"(^|\n)\s*\d{1,2}\.$", text[start:end]) is not None:
|
|
218
237
|
# sentence ends with a numeration
|
|
219
238
|
return True
|
{haystack_ai-3.2.0rc1 → haystack_ai-3.3.0}/haystack/components/preprocessors/text_cleaner.py
RENAMED
|
@@ -84,7 +84,11 @@ class TextCleaner:
|
|
|
84
84
|
:param texts: List of strings to clean.
|
|
85
85
|
:returns: A dictionary with the following key:
|
|
86
86
|
- `texts`: the cleaned list of strings.
|
|
87
|
+
:raises TypeError:
|
|
88
|
+
If `texts` is not a list or any element is not a string.
|
|
87
89
|
"""
|
|
90
|
+
if not isinstance(texts, list) or not all(isinstance(text, str) for text in texts):
|
|
91
|
+
raise TypeError("`texts` must be a list of strings.")
|
|
88
92
|
|
|
89
93
|
if self._regex:
|
|
90
94
|
texts = [self._regex.sub("", text) for text in texts]
|