haystack-ai 3.3.0rc1__tar.gz → 3.4.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.3.0rc1 → haystack_ai-3.4.0}/PKG-INFO +3 -4
- haystack_ai-3.4.0/VERSION.txt +1 -0
- {haystack_ai-3.3.0rc1 → haystack_ai-3.4.0}/haystack/components/agents/agent.py +82 -17
- {haystack_ai-3.3.0rc1 → haystack_ai-3.4.0}/haystack/components/agents/tool_calling.py +6 -4
- {haystack_ai-3.3.0rc1 → haystack_ai-3.4.0}/haystack/components/agents/utils.py +1 -1
- {haystack_ai-3.3.0rc1 → haystack_ai-3.4.0}/haystack/components/builders/chat_prompt_builder.py +8 -16
- {haystack_ai-3.3.0rc1 → haystack_ai-3.4.0}/haystack/components/builders/prompt_builder.py +2 -2
- {haystack_ai-3.3.0rc1 → haystack_ai-3.4.0}/haystack/components/converters/csv.py +1 -2
- {haystack_ai-3.3.0rc1 → haystack_ai-3.4.0}/haystack/components/converters/docx.py +41 -2
- {haystack_ai-3.3.0rc1 → haystack_ai-3.4.0}/haystack/components/converters/html.py +14 -5
- {haystack_ai-3.3.0rc1 → haystack_ai-3.4.0}/haystack/components/converters/image/image_utils.py +41 -42
- {haystack_ai-3.3.0rc1 → haystack_ai-3.4.0}/haystack/components/converters/multi_file_converter.py +3 -3
- {haystack_ai-3.3.0rc1 → haystack_ai-3.4.0}/haystack/components/embedders/azure_document_embedder.py +2 -2
- {haystack_ai-3.3.0rc1 → haystack_ai-3.4.0}/haystack/components/embedders/azure_text_embedder.py +3 -3
- {haystack_ai-3.3.0rc1 → haystack_ai-3.4.0}/haystack/components/embedders/openai_document_embedder.py +2 -2
- {haystack_ai-3.3.0rc1 → haystack_ai-3.4.0}/haystack/components/embedders/openai_text_embedder.py +3 -3
- {haystack_ai-3.3.0rc1 → haystack_ai-3.4.0}/haystack/components/evaluators/answer_exact_match.py +3 -0
- {haystack_ai-3.3.0rc1 → haystack_ai-3.4.0}/haystack/components/evaluators/context_relevance.py +2 -1
- {haystack_ai-3.3.0rc1 → haystack_ai-3.4.0}/haystack/components/evaluators/document_map.py +4 -0
- {haystack_ai-3.3.0rc1 → haystack_ai-3.4.0}/haystack/components/evaluators/document_mrr.py +4 -0
- {haystack_ai-3.3.0rc1 → haystack_ai-3.4.0}/haystack/components/evaluators/document_recall.py +4 -0
- {haystack_ai-3.3.0rc1 → haystack_ai-3.4.0}/haystack/components/evaluators/faithfulness.py +2 -1
- {haystack_ai-3.3.0rc1 → haystack_ai-3.4.0}/haystack/components/evaluators/llm_evaluator.py +46 -13
- {haystack_ai-3.3.0rc1 → haystack_ai-3.4.0}/haystack/components/extractors/image/llm_document_content_extractor.py +29 -19
- {haystack_ai-3.3.0rc1 → haystack_ai-3.4.0}/haystack/components/extractors/llm_metadata_extractor.py +9 -10
- {haystack_ai-3.3.0rc1 → haystack_ai-3.4.0}/haystack/components/generators/chat/azure.py +5 -14
- {haystack_ai-3.3.0rc1 → haystack_ai-3.4.0}/haystack/components/generators/chat/llm.py +2 -2
- {haystack_ai-3.3.0rc1 → haystack_ai-3.4.0}/haystack/components/generators/chat/openai.py +5 -13
- {haystack_ai-3.3.0rc1 → haystack_ai-3.4.0}/haystack/components/generators/chat/openai_responses.py +18 -39
- {haystack_ai-3.3.0rc1 → haystack_ai-3.4.0}/haystack/components/joiners/answer_joiner.py +3 -3
- {haystack_ai-3.3.0rc1 → haystack_ai-3.4.0}/haystack/components/joiners/branch.py +6 -6
- {haystack_ai-3.3.0rc1 → haystack_ai-3.4.0}/haystack/components/joiners/list_joiner.py +3 -3
- {haystack_ai-3.3.0rc1 → haystack_ai-3.4.0}/haystack/components/joiners/string_joiner.py +0 -3
- haystack_ai-3.4.0/haystack/components/preprocessors/_page_numbers.py +38 -0
- {haystack_ai-3.3.0rc1 → haystack_ai-3.4.0}/haystack/components/preprocessors/document_splitter.py +4 -2
- {haystack_ai-3.3.0rc1 → haystack_ai-3.4.0}/haystack/components/preprocessors/embedding_based_document_splitter.py +8 -6
- {haystack_ai-3.3.0rc1 → haystack_ai-3.4.0}/haystack/components/preprocessors/markdown_header_splitter.py +19 -6
- {haystack_ai-3.3.0rc1 → haystack_ai-3.4.0}/haystack/components/preprocessors/recursive_splitter.py +101 -48
- {haystack_ai-3.3.0rc1 → haystack_ai-3.4.0}/haystack/components/preprocessors/sentence_tokenizer.py +2 -2
- {haystack_ai-3.3.0rc1 → haystack_ai-3.4.0}/haystack/components/query/query_expander.py +77 -27
- {haystack_ai-3.3.0rc1 → haystack_ai-3.4.0}/haystack/components/rankers/llm_ranker.py +61 -33
- {haystack_ai-3.3.0rc1 → haystack_ai-3.4.0}/haystack/components/rankers/lost_in_the_middle.py +4 -4
- {haystack_ai-3.3.0rc1 → haystack_ai-3.4.0}/haystack/components/rankers/meta_field.py +10 -3
- {haystack_ai-3.3.0rc1 → haystack_ai-3.4.0}/haystack/components/retrievers/sentence_window_retriever.py +13 -8
- {haystack_ai-3.3.0rc1 → haystack_ai-3.4.0}/haystack/components/routers/conditional_router.py +1 -2
- {haystack_ai-3.3.0rc1 → haystack_ai-3.4.0}/haystack/components/validators/json_schema.py +9 -4
- {haystack_ai-3.3.0rc1 → haystack_ai-3.4.0}/haystack/core/component/sockets.py +0 -11
- {haystack_ai-3.3.0rc1 → haystack_ai-3.4.0}/haystack/core/pipeline/base.py +4 -6
- {haystack_ai-3.3.0rc1 → haystack_ai-3.4.0}/haystack/core/pipeline/breakpoint.py +4 -2
- {haystack_ai-3.3.0rc1 → haystack_ai-3.4.0}/haystack/core/pipeline/pipeline.py +16 -19
- {haystack_ai-3.3.0rc1 → haystack_ai-3.4.0}/haystack/core/super_component/super_component.py +2 -2
- {haystack_ai-3.3.0rc1 → haystack_ai-3.4.0}/haystack/core/type_utils.py +2 -3
- {haystack_ai-3.3.0rc1 → haystack_ai-3.4.0}/haystack/dataclasses/chat_message.py +191 -13
- {haystack_ai-3.3.0rc1 → haystack_ai-3.4.0}/haystack/document_stores/in_memory/document_store.py +9 -6
- {haystack_ai-3.3.0rc1 → haystack_ai-3.4.0}/haystack/document_stores/types/filter_policy.py +31 -9
- {haystack_ai-3.3.0rc1 → haystack_ai-3.4.0}/haystack/evaluation/eval_run_result.py +1 -1
- {haystack_ai-3.3.0rc1 → haystack_ai-3.4.0}/haystack/hooks/budget/hooks.py +2 -2
- {haystack_ai-3.3.0rc1 → haystack_ai-3.4.0}/haystack/hooks/compaction/__init__.py +2 -0
- haystack_ai-3.4.0/haystack/hooks/compaction/tool_result_offloading.py +210 -0
- {haystack_ai-3.3.0rc1 → haystack_ai-3.4.0}/haystack/hooks/compaction/tool_result_pruning.py +8 -38
- {haystack_ai-3.3.0rc1 → haystack_ai-3.4.0}/haystack/hooks/compaction/utils.py +58 -0
- {haystack_ai-3.3.0rc1 → haystack_ai-3.4.0}/haystack/hooks/from_function.py +6 -2
- {haystack_ai-3.3.0rc1 → haystack_ai-3.4.0}/haystack/hooks/human_in_the_loop/hooks.py +2 -2
- {haystack_ai-3.3.0rc1 → haystack_ai-3.4.0}/haystack/hooks/tool_result_offloading/hooks.py +24 -126
- {haystack_ai-3.3.0rc1 → haystack_ai-3.4.0}/haystack/hooks/tool_result_offloading/types/protocol.py +3 -3
- haystack_ai-3.4.0/haystack/hooks/tool_result_offloading/utils.py +194 -0
- {haystack_ai-3.3.0rc1 → haystack_ai-3.4.0}/haystack/logging.py +85 -11
- {haystack_ai-3.3.0rc1 → haystack_ai-3.4.0}/haystack/telemetry/_telemetry.py +1 -1
- {haystack_ai-3.3.0rc1 → haystack_ai-3.4.0}/haystack/token_counters/openai_counter.py +1 -1
- {haystack_ai-3.3.0rc1 → haystack_ai-3.4.0}/haystack/tools/__init__.py +17 -4
- {haystack_ai-3.3.0rc1 → haystack_ai-3.4.0}/haystack/tools/component_tool.py +27 -13
- {haystack_ai-3.3.0rc1 → haystack_ai-3.4.0}/haystack/tools/from_function.py +2 -3
- {haystack_ai-3.3.0rc1 → haystack_ai-3.4.0}/haystack/tools/parameters_schema_utils.py +14 -1
- {haystack_ai-3.3.0rc1 → haystack_ai-3.4.0}/haystack/tools/pipeline_tool.py +2 -2
- {haystack_ai-3.3.0rc1 → haystack_ai-3.4.0}/haystack/tools/searchable_toolset.py +47 -16
- {haystack_ai-3.3.0rc1 → haystack_ai-3.4.0}/haystack/tools/skills/skill_toolset.py +9 -2
- {haystack_ai-3.3.0rc1 → haystack_ai-3.4.0}/haystack/tools/tool.py +18 -20
- {haystack_ai-3.3.0rc1 → haystack_ai-3.4.0}/haystack/tools/toolset.py +5 -37
- haystack_ai-3.4.0/haystack/tools/utils.py +116 -0
- {haystack_ai-3.3.0rc1 → haystack_ai-3.4.0}/haystack/utils/filters.py +4 -2
- {haystack_ai-3.3.0rc1 → haystack_ai-3.4.0}/haystack/utils/misc.py +5 -2
- {haystack_ai-3.3.0rc1 → haystack_ai-3.4.0}/pyproject.toml +5 -6
- haystack_ai-3.3.0rc1/VERSION.txt +0 -1
- haystack_ai-3.3.0rc1/haystack/tools/utils.py +0 -64
- {haystack_ai-3.3.0rc1 → haystack_ai-3.4.0}/.gitignore +0 -0
- {haystack_ai-3.3.0rc1 → haystack_ai-3.4.0}/LICENSE +0 -0
- {haystack_ai-3.3.0rc1 → haystack_ai-3.4.0}/README.md +0 -0
- {haystack_ai-3.3.0rc1 → haystack_ai-3.4.0}/haystack/__init__.py +0 -0
- {haystack_ai-3.3.0rc1 → haystack_ai-3.4.0}/haystack/components/__init__.py +0 -0
- {haystack_ai-3.3.0rc1 → haystack_ai-3.4.0}/haystack/components/agents/__init__.py +0 -0
- {haystack_ai-3.3.0rc1 → haystack_ai-3.4.0}/haystack/components/agents/state/__init__.py +0 -0
- {haystack_ai-3.3.0rc1 → haystack_ai-3.4.0}/haystack/components/agents/state/state.py +0 -0
- {haystack_ai-3.3.0rc1 → haystack_ai-3.4.0}/haystack/components/agents/state/state_utils.py +0 -0
- {haystack_ai-3.3.0rc1 → haystack_ai-3.4.0}/haystack/components/builders/__init__.py +0 -0
- {haystack_ai-3.3.0rc1 → haystack_ai-3.4.0}/haystack/components/builders/answer_builder.py +0 -0
- {haystack_ai-3.3.0rc1 → haystack_ai-3.4.0}/haystack/components/caching/__init__.py +0 -0
- {haystack_ai-3.3.0rc1 → haystack_ai-3.4.0}/haystack/components/caching/cache_checker.py +0 -0
- {haystack_ai-3.3.0rc1 → haystack_ai-3.4.0}/haystack/components/converters/__init__.py +0 -0
- {haystack_ai-3.3.0rc1 → haystack_ai-3.4.0}/haystack/components/converters/file_to_file_content.py +0 -0
- {haystack_ai-3.3.0rc1 → haystack_ai-3.4.0}/haystack/components/converters/image/__init__.py +0 -0
- {haystack_ai-3.3.0rc1 → haystack_ai-3.4.0}/haystack/components/converters/image/document_to_image.py +0 -0
- {haystack_ai-3.3.0rc1 → haystack_ai-3.4.0}/haystack/components/converters/image/file_to_document.py +0 -0
- {haystack_ai-3.3.0rc1 → haystack_ai-3.4.0}/haystack/components/converters/image/file_to_image.py +0 -0
- {haystack_ai-3.3.0rc1 → haystack_ai-3.4.0}/haystack/components/converters/image/pdf_to_image.py +0 -0
- {haystack_ai-3.3.0rc1 → haystack_ai-3.4.0}/haystack/components/converters/json.py +0 -0
- {haystack_ai-3.3.0rc1 → haystack_ai-3.4.0}/haystack/components/converters/markdown.py +0 -0
- {haystack_ai-3.3.0rc1 → haystack_ai-3.4.0}/haystack/components/converters/msg.py +0 -0
- {haystack_ai-3.3.0rc1 → haystack_ai-3.4.0}/haystack/components/converters/output_adapter.py +0 -0
- {haystack_ai-3.3.0rc1 → haystack_ai-3.4.0}/haystack/components/converters/pdfminer.py +0 -0
- {haystack_ai-3.3.0rc1 → haystack_ai-3.4.0}/haystack/components/converters/pptx.py +0 -0
- {haystack_ai-3.3.0rc1 → haystack_ai-3.4.0}/haystack/components/converters/pypdf.py +0 -0
- {haystack_ai-3.3.0rc1 → haystack_ai-3.4.0}/haystack/components/converters/txt.py +0 -0
- {haystack_ai-3.3.0rc1 → haystack_ai-3.4.0}/haystack/components/converters/utils.py +0 -0
- {haystack_ai-3.3.0rc1 → haystack_ai-3.4.0}/haystack/components/converters/xlsx.py +0 -0
- {haystack_ai-3.3.0rc1 → haystack_ai-3.4.0}/haystack/components/embedders/__init__.py +0 -0
- {haystack_ai-3.3.0rc1 → haystack_ai-3.4.0}/haystack/components/embedders/mock_document_embedder.py +0 -0
- {haystack_ai-3.3.0rc1 → haystack_ai-3.4.0}/haystack/components/embedders/mock_text_embedder.py +0 -0
- {haystack_ai-3.3.0rc1 → haystack_ai-3.4.0}/haystack/components/embedders/mock_utils.py +0 -0
- {haystack_ai-3.3.0rc1 → haystack_ai-3.4.0}/haystack/components/embedders/types/__init__.py +0 -0
- {haystack_ai-3.3.0rc1 → haystack_ai-3.4.0}/haystack/components/embedders/types/protocol.py +0 -0
- {haystack_ai-3.3.0rc1 → haystack_ai-3.4.0}/haystack/components/evaluators/__init__.py +0 -0
- {haystack_ai-3.3.0rc1 → haystack_ai-3.4.0}/haystack/components/evaluators/document_ndcg.py +0 -0
- {haystack_ai-3.3.0rc1 → haystack_ai-3.4.0}/haystack/components/evaluators/sas_evaluator.py +0 -0
- {haystack_ai-3.3.0rc1 → haystack_ai-3.4.0}/haystack/components/extractors/__init__.py +0 -0
- {haystack_ai-3.3.0rc1 → haystack_ai-3.4.0}/haystack/components/extractors/image/__init__.py +0 -0
- {haystack_ai-3.3.0rc1 → haystack_ai-3.4.0}/haystack/components/extractors/regex_text_extractor.py +0 -0
- {haystack_ai-3.3.0rc1 → haystack_ai-3.4.0}/haystack/components/fetchers/__init__.py +0 -0
- {haystack_ai-3.3.0rc1 → haystack_ai-3.4.0}/haystack/components/fetchers/link_content.py +0 -0
- {haystack_ai-3.3.0rc1 → haystack_ai-3.4.0}/haystack/components/generators/__init__.py +0 -0
- {haystack_ai-3.3.0rc1 → haystack_ai-3.4.0}/haystack/components/generators/chat/__init__.py +0 -0
- {haystack_ai-3.3.0rc1 → haystack_ai-3.4.0}/haystack/components/generators/chat/azure_responses.py +0 -0
- {haystack_ai-3.3.0rc1 → haystack_ai-3.4.0}/haystack/components/generators/chat/fallback.py +0 -0
- {haystack_ai-3.3.0rc1 → haystack_ai-3.4.0}/haystack/components/generators/chat/mock.py +0 -0
- {haystack_ai-3.3.0rc1 → haystack_ai-3.4.0}/haystack/components/generators/chat/types/__init__.py +0 -0
- {haystack_ai-3.3.0rc1 → haystack_ai-3.4.0}/haystack/components/generators/chat/types/protocol.py +0 -0
- {haystack_ai-3.3.0rc1 → haystack_ai-3.4.0}/haystack/components/generators/openai_image_generator.py +0 -0
- {haystack_ai-3.3.0rc1 → haystack_ai-3.4.0}/haystack/components/generators/utils.py +0 -0
- {haystack_ai-3.3.0rc1 → haystack_ai-3.4.0}/haystack/components/joiners/__init__.py +0 -0
- {haystack_ai-3.3.0rc1 → haystack_ai-3.4.0}/haystack/components/joiners/document_joiner.py +0 -0
- {haystack_ai-3.3.0rc1 → haystack_ai-3.4.0}/haystack/components/preprocessors/__init__.py +0 -0
- {haystack_ai-3.3.0rc1 → haystack_ai-3.4.0}/haystack/components/preprocessors/csv_document_cleaner.py +0 -0
- {haystack_ai-3.3.0rc1 → haystack_ai-3.4.0}/haystack/components/preprocessors/csv_document_splitter.py +0 -0
- {haystack_ai-3.3.0rc1 → haystack_ai-3.4.0}/haystack/components/preprocessors/document_cleaner.py +0 -0
- {haystack_ai-3.3.0rc1 → haystack_ai-3.4.0}/haystack/components/preprocessors/document_preprocessor.py +0 -0
- {haystack_ai-3.3.0rc1 → haystack_ai-3.4.0}/haystack/components/preprocessors/hierarchical_document_splitter.py +0 -0
- {haystack_ai-3.3.0rc1 → haystack_ai-3.4.0}/haystack/components/preprocessors/python_code_splitter.py +0 -0
- {haystack_ai-3.3.0rc1 → haystack_ai-3.4.0}/haystack/components/preprocessors/text_cleaner.py +0 -0
- {haystack_ai-3.3.0rc1 → haystack_ai-3.4.0}/haystack/components/query/__init__.py +0 -0
- {haystack_ai-3.3.0rc1 → haystack_ai-3.4.0}/haystack/components/rankers/__init__.py +0 -0
- {haystack_ai-3.3.0rc1 → haystack_ai-3.4.0}/haystack/components/rankers/meta_field_grouping_ranker.py +0 -0
- {haystack_ai-3.3.0rc1 → haystack_ai-3.4.0}/haystack/components/retrievers/__init__.py +0 -0
- {haystack_ai-3.3.0rc1 → haystack_ai-3.4.0}/haystack/components/retrievers/auto_merging_retriever.py +0 -0
- {haystack_ai-3.3.0rc1 → haystack_ai-3.4.0}/haystack/components/retrievers/filter_retriever.py +0 -0
- {haystack_ai-3.3.0rc1 → haystack_ai-3.4.0}/haystack/components/retrievers/in_memory/__init__.py +0 -0
- {haystack_ai-3.3.0rc1 → haystack_ai-3.4.0}/haystack/components/retrievers/in_memory/bm25_retriever.py +0 -0
- {haystack_ai-3.3.0rc1 → haystack_ai-3.4.0}/haystack/components/retrievers/in_memory/embedding_retriever.py +0 -0
- {haystack_ai-3.3.0rc1 → haystack_ai-3.4.0}/haystack/components/retrievers/multi_query_embedding_retriever.py +0 -0
- {haystack_ai-3.3.0rc1 → haystack_ai-3.4.0}/haystack/components/retrievers/multi_query_text_retriever.py +0 -0
- {haystack_ai-3.3.0rc1 → haystack_ai-3.4.0}/haystack/components/retrievers/multi_retriever.py +0 -0
- {haystack_ai-3.3.0rc1 → haystack_ai-3.4.0}/haystack/components/retrievers/text_embedding_retriever.py +0 -0
- {haystack_ai-3.3.0rc1 → haystack_ai-3.4.0}/haystack/components/retrievers/types/__init__.py +0 -0
- {haystack_ai-3.3.0rc1 → haystack_ai-3.4.0}/haystack/components/retrievers/types/protocol.py +0 -0
- {haystack_ai-3.3.0rc1 → haystack_ai-3.4.0}/haystack/components/routers/__init__.py +0 -0
- {haystack_ai-3.3.0rc1 → haystack_ai-3.4.0}/haystack/components/routers/document_length_router.py +0 -0
- {haystack_ai-3.3.0rc1 → haystack_ai-3.4.0}/haystack/components/routers/document_type_router.py +0 -0
- {haystack_ai-3.3.0rc1 → haystack_ai-3.4.0}/haystack/components/routers/file_type_router.py +0 -0
- {haystack_ai-3.3.0rc1 → haystack_ai-3.4.0}/haystack/components/routers/llm_messages_router.py +0 -0
- {haystack_ai-3.3.0rc1 → haystack_ai-3.4.0}/haystack/components/routers/metadata_router.py +0 -0
- {haystack_ai-3.3.0rc1 → haystack_ai-3.4.0}/haystack/components/samplers/__init__.py +0 -0
- {haystack_ai-3.3.0rc1 → haystack_ai-3.4.0}/haystack/components/samplers/top_p.py +0 -0
- {haystack_ai-3.3.0rc1 → haystack_ai-3.4.0}/haystack/components/validators/__init__.py +0 -0
- {haystack_ai-3.3.0rc1 → haystack_ai-3.4.0}/haystack/components/writers/__init__.py +0 -0
- {haystack_ai-3.3.0rc1 → haystack_ai-3.4.0}/haystack/components/writers/document_writer.py +0 -0
- {haystack_ai-3.3.0rc1 → haystack_ai-3.4.0}/haystack/core/__init__.py +0 -0
- {haystack_ai-3.3.0rc1 → haystack_ai-3.4.0}/haystack/core/component/__init__.py +0 -0
- {haystack_ai-3.3.0rc1 → haystack_ai-3.4.0}/haystack/core/component/component.py +0 -0
- {haystack_ai-3.3.0rc1 → haystack_ai-3.4.0}/haystack/core/component/types.py +0 -0
- {haystack_ai-3.3.0rc1 → haystack_ai-3.4.0}/haystack/core/errors.py +0 -0
- {haystack_ai-3.3.0rc1 → haystack_ai-3.4.0}/haystack/core/pipeline/__init__.py +0 -0
- {haystack_ai-3.3.0rc1 → haystack_ai-3.4.0}/haystack/core/pipeline/component_checks.py +0 -0
- {haystack_ai-3.3.0rc1 → haystack_ai-3.4.0}/haystack/core/pipeline/descriptions.py +0 -0
- {haystack_ai-3.3.0rc1 → haystack_ai-3.4.0}/haystack/core/pipeline/draw.py +0 -0
- {haystack_ai-3.3.0rc1 → haystack_ai-3.4.0}/haystack/core/pipeline/utils.py +0 -0
- {haystack_ai-3.3.0rc1 → haystack_ai-3.4.0}/haystack/core/serialization.py +0 -0
- {haystack_ai-3.3.0rc1 → haystack_ai-3.4.0}/haystack/core/serialization_security.py +0 -0
- {haystack_ai-3.3.0rc1 → haystack_ai-3.4.0}/haystack/core/super_component/__init__.py +0 -0
- {haystack_ai-3.3.0rc1 → haystack_ai-3.4.0}/haystack/core/super_component/utils.py +0 -0
- {haystack_ai-3.3.0rc1 → haystack_ai-3.4.0}/haystack/data/abbreviations/de.txt +0 -0
- {haystack_ai-3.3.0rc1 → haystack_ai-3.4.0}/haystack/data/abbreviations/en.txt +0 -0
- {haystack_ai-3.3.0rc1 → haystack_ai-3.4.0}/haystack/dataclasses/__init__.py +0 -0
- {haystack_ai-3.3.0rc1 → haystack_ai-3.4.0}/haystack/dataclasses/answer.py +0 -0
- {haystack_ai-3.3.0rc1 → haystack_ai-3.4.0}/haystack/dataclasses/breakpoints.py +0 -0
- {haystack_ai-3.3.0rc1 → haystack_ai-3.4.0}/haystack/dataclasses/byte_stream.py +0 -0
- {haystack_ai-3.3.0rc1 → haystack_ai-3.4.0}/haystack/dataclasses/document.py +0 -0
- {haystack_ai-3.3.0rc1 → haystack_ai-3.4.0}/haystack/dataclasses/file_content.py +0 -0
- {haystack_ai-3.3.0rc1 → haystack_ai-3.4.0}/haystack/dataclasses/image_content.py +0 -0
- {haystack_ai-3.3.0rc1 → haystack_ai-3.4.0}/haystack/dataclasses/skill_info.py +0 -0
- {haystack_ai-3.3.0rc1 → haystack_ai-3.4.0}/haystack/dataclasses/sparse_embedding.py +0 -0
- {haystack_ai-3.3.0rc1 → haystack_ai-3.4.0}/haystack/dataclasses/streaming_chunk.py +0 -0
- {haystack_ai-3.3.0rc1 → haystack_ai-3.4.0}/haystack/document_stores/__init__.py +0 -0
- {haystack_ai-3.3.0rc1 → haystack_ai-3.4.0}/haystack/document_stores/errors/__init__.py +0 -0
- {haystack_ai-3.3.0rc1 → haystack_ai-3.4.0}/haystack/document_stores/errors/errors.py +0 -0
- {haystack_ai-3.3.0rc1 → haystack_ai-3.4.0}/haystack/document_stores/in_memory/__init__.py +0 -0
- {haystack_ai-3.3.0rc1 → haystack_ai-3.4.0}/haystack/document_stores/types/__init__.py +0 -0
- {haystack_ai-3.3.0rc1 → haystack_ai-3.4.0}/haystack/document_stores/types/policy.py +0 -0
- {haystack_ai-3.3.0rc1 → haystack_ai-3.4.0}/haystack/document_stores/types/protocol.py +0 -0
- {haystack_ai-3.3.0rc1 → haystack_ai-3.4.0}/haystack/errors.py +0 -0
- {haystack_ai-3.3.0rc1 → haystack_ai-3.4.0}/haystack/evaluation/__init__.py +0 -0
- {haystack_ai-3.3.0rc1 → haystack_ai-3.4.0}/haystack/hooks/__init__.py +0 -0
- {haystack_ai-3.3.0rc1 → haystack_ai-3.4.0}/haystack/hooks/budget/__init__.py +0 -0
- {haystack_ai-3.3.0rc1 → haystack_ai-3.4.0}/haystack/hooks/compaction/AGENTS.md +0 -0
- {haystack_ai-3.3.0rc1 → haystack_ai-3.4.0}/haystack/hooks/compaction/CLAUDE.md +0 -0
- {haystack_ai-3.3.0rc1 → haystack_ai-3.4.0}/haystack/hooks/compaction/hooks.py +0 -0
- {haystack_ai-3.3.0rc1 → haystack_ai-3.4.0}/haystack/hooks/compaction/sliding_window.py +0 -0
- {haystack_ai-3.3.0rc1 → haystack_ai-3.4.0}/haystack/hooks/compaction/summarization.py +0 -0
- {haystack_ai-3.3.0rc1 → haystack_ai-3.4.0}/haystack/hooks/compaction/types/__init__.py +0 -0
- {haystack_ai-3.3.0rc1 → haystack_ai-3.4.0}/haystack/hooks/compaction/types/protocol.py +0 -0
- {haystack_ai-3.3.0rc1 → haystack_ai-3.4.0}/haystack/hooks/human_in_the_loop/__init__.py +0 -0
- {haystack_ai-3.3.0rc1 → haystack_ai-3.4.0}/haystack/hooks/human_in_the_loop/dataclasses.py +0 -0
- {haystack_ai-3.3.0rc1 → haystack_ai-3.4.0}/haystack/hooks/human_in_the_loop/policies.py +0 -0
- {haystack_ai-3.3.0rc1 → haystack_ai-3.4.0}/haystack/hooks/human_in_the_loop/strategies.py +0 -0
- {haystack_ai-3.3.0rc1 → haystack_ai-3.4.0}/haystack/hooks/human_in_the_loop/types/__init__.py +0 -0
- {haystack_ai-3.3.0rc1 → haystack_ai-3.4.0}/haystack/hooks/human_in_the_loop/types/protocol.py +0 -0
- {haystack_ai-3.3.0rc1 → haystack_ai-3.4.0}/haystack/hooks/human_in_the_loop/user_interfaces.py +0 -0
- {haystack_ai-3.3.0rc1 → haystack_ai-3.4.0}/haystack/hooks/invocation.py +0 -0
- {haystack_ai-3.3.0rc1 → haystack_ai-3.4.0}/haystack/hooks/protocol.py +0 -0
- {haystack_ai-3.3.0rc1 → haystack_ai-3.4.0}/haystack/hooks/tool_result_offloading/__init__.py +0 -0
- {haystack_ai-3.3.0rc1 → haystack_ai-3.4.0}/haystack/hooks/tool_result_offloading/policies.py +0 -0
- {haystack_ai-3.3.0rc1 → haystack_ai-3.4.0}/haystack/hooks/tool_result_offloading/stores.py +0 -0
- {haystack_ai-3.3.0rc1 → haystack_ai-3.4.0}/haystack/hooks/tool_result_offloading/types/__init__.py +0 -0
- {haystack_ai-3.3.0rc1 → haystack_ai-3.4.0}/haystack/hooks/utils.py +0 -0
- {haystack_ai-3.3.0rc1 → haystack_ai-3.4.0}/haystack/lazy_imports.py +0 -0
- {haystack_ai-3.3.0rc1 → haystack_ai-3.4.0}/haystack/marshal/__init__.py +0 -0
- {haystack_ai-3.3.0rc1 → haystack_ai-3.4.0}/haystack/marshal/protocol.py +0 -0
- {haystack_ai-3.3.0rc1 → haystack_ai-3.4.0}/haystack/marshal/yaml.py +0 -0
- {haystack_ai-3.3.0rc1 → haystack_ai-3.4.0}/haystack/py.typed +0 -0
- {haystack_ai-3.3.0rc1 → haystack_ai-3.4.0}/haystack/skill_stores/__init__.py +0 -0
- {haystack_ai-3.3.0rc1 → haystack_ai-3.4.0}/haystack/skill_stores/file_system/__init__.py +0 -0
- {haystack_ai-3.3.0rc1 → haystack_ai-3.4.0}/haystack/skill_stores/file_system/skill_store.py +0 -0
- {haystack_ai-3.3.0rc1 → haystack_ai-3.4.0}/haystack/skill_stores/types/__init__.py +0 -0
- {haystack_ai-3.3.0rc1 → haystack_ai-3.4.0}/haystack/skill_stores/types/protocol.py +0 -0
- {haystack_ai-3.3.0rc1 → haystack_ai-3.4.0}/haystack/telemetry/__init__.py +0 -0
- {haystack_ai-3.3.0rc1 → haystack_ai-3.4.0}/haystack/telemetry/_environment.py +0 -0
- {haystack_ai-3.3.0rc1 → haystack_ai-3.4.0}/haystack/testing/__init__.py +0 -0
- {haystack_ai-3.3.0rc1 → haystack_ai-3.4.0}/haystack/testing/callable_serialization/random_callable.py +0 -0
- {haystack_ai-3.3.0rc1 → haystack_ai-3.4.0}/haystack/testing/document_store.py +0 -0
- {haystack_ai-3.3.0rc1 → haystack_ai-3.4.0}/haystack/testing/document_store_async.py +0 -0
- {haystack_ai-3.3.0rc1 → haystack_ai-3.4.0}/haystack/testing/factory.py +0 -0
- {haystack_ai-3.3.0rc1 → haystack_ai-3.4.0}/haystack/testing/sample_components/__init__.py +0 -0
- {haystack_ai-3.3.0rc1 → haystack_ai-3.4.0}/haystack/testing/sample_components/accumulate.py +0 -0
- {haystack_ai-3.3.0rc1 → haystack_ai-3.4.0}/haystack/testing/sample_components/add_value.py +0 -0
- {haystack_ai-3.3.0rc1 → haystack_ai-3.4.0}/haystack/testing/sample_components/concatenate.py +0 -0
- {haystack_ai-3.3.0rc1 → haystack_ai-3.4.0}/haystack/testing/sample_components/double.py +0 -0
- {haystack_ai-3.3.0rc1 → haystack_ai-3.4.0}/haystack/testing/sample_components/fstring.py +0 -0
- {haystack_ai-3.3.0rc1 → haystack_ai-3.4.0}/haystack/testing/sample_components/future_annotations.py +0 -0
- {haystack_ai-3.3.0rc1 → haystack_ai-3.4.0}/haystack/testing/sample_components/greet.py +0 -0
- {haystack_ai-3.3.0rc1 → haystack_ai-3.4.0}/haystack/testing/sample_components/hello.py +0 -0
- {haystack_ai-3.3.0rc1 → haystack_ai-3.4.0}/haystack/testing/sample_components/joiner.py +0 -0
- {haystack_ai-3.3.0rc1 → haystack_ai-3.4.0}/haystack/testing/sample_components/parity.py +0 -0
- {haystack_ai-3.3.0rc1 → haystack_ai-3.4.0}/haystack/testing/sample_components/remainder.py +0 -0
- {haystack_ai-3.3.0rc1 → haystack_ai-3.4.0}/haystack/testing/sample_components/repeat.py +0 -0
- {haystack_ai-3.3.0rc1 → haystack_ai-3.4.0}/haystack/testing/sample_components/subtract.py +0 -0
- {haystack_ai-3.3.0rc1 → haystack_ai-3.4.0}/haystack/testing/sample_components/sum.py +0 -0
- {haystack_ai-3.3.0rc1 → haystack_ai-3.4.0}/haystack/testing/sample_components/text_splitter.py +0 -0
- {haystack_ai-3.3.0rc1 → haystack_ai-3.4.0}/haystack/testing/sample_components/threshold.py +0 -0
- {haystack_ai-3.3.0rc1 → haystack_ai-3.4.0}/haystack/testing/telemetry.py +0 -0
- {haystack_ai-3.3.0rc1 → haystack_ai-3.4.0}/haystack/testing/test_utils.py +0 -0
- {haystack_ai-3.3.0rc1 → haystack_ai-3.4.0}/haystack/token_counters/__init__.py +0 -0
- {haystack_ai-3.3.0rc1 → haystack_ai-3.4.0}/haystack/token_counters/approximate_counter.py +0 -0
- {haystack_ai-3.3.0rc1 → haystack_ai-3.4.0}/haystack/token_counters/tiktoken_counter.py +0 -0
- {haystack_ai-3.3.0rc1 → haystack_ai-3.4.0}/haystack/token_counters/types/__init__.py +0 -0
- {haystack_ai-3.3.0rc1 → haystack_ai-3.4.0}/haystack/token_counters/types/protocol.py +0 -0
- {haystack_ai-3.3.0rc1 → haystack_ai-3.4.0}/haystack/token_counters/utils.py +0 -0
- {haystack_ai-3.3.0rc1 → haystack_ai-3.4.0}/haystack/tools/agent_tool.py +0 -0
- {haystack_ai-3.3.0rc1 → haystack_ai-3.4.0}/haystack/tools/errors.py +0 -0
- {haystack_ai-3.3.0rc1 → haystack_ai-3.4.0}/haystack/tools/serde_utils.py +0 -0
- {haystack_ai-3.3.0rc1 → haystack_ai-3.4.0}/haystack/tools/skills/__init__.py +0 -0
- {haystack_ai-3.3.0rc1 → haystack_ai-3.4.0}/haystack/tools/tool_types.py +0 -0
- {haystack_ai-3.3.0rc1 → haystack_ai-3.4.0}/haystack/tracing/__init__.py +0 -0
- {haystack_ai-3.3.0rc1 → haystack_ai-3.4.0}/haystack/tracing/logging_tracer.py +0 -0
- {haystack_ai-3.3.0rc1 → haystack_ai-3.4.0}/haystack/tracing/tracer.py +0 -0
- {haystack_ai-3.3.0rc1 → haystack_ai-3.4.0}/haystack/tracing/utils.py +0 -0
- {haystack_ai-3.3.0rc1 → haystack_ai-3.4.0}/haystack/utils/__init__.py +0 -0
- {haystack_ai-3.3.0rc1 → haystack_ai-3.4.0}/haystack/utils/async_utils.py +0 -0
- {haystack_ai-3.3.0rc1 → haystack_ai-3.4.0}/haystack/utils/auth.py +0 -0
- {haystack_ai-3.3.0rc1 → haystack_ai-3.4.0}/haystack/utils/azure.py +0 -0
- {haystack_ai-3.3.0rc1 → haystack_ai-3.4.0}/haystack/utils/base_serialization.py +0 -0
- {haystack_ai-3.3.0rc1 → haystack_ai-3.4.0}/haystack/utils/callable_serialization.py +0 -0
- {haystack_ai-3.3.0rc1 → haystack_ai-3.4.0}/haystack/utils/dataclasses.py +0 -0
- {haystack_ai-3.3.0rc1 → haystack_ai-3.4.0}/haystack/utils/deserialization.py +0 -0
- {haystack_ai-3.3.0rc1 → haystack_ai-3.4.0}/haystack/utils/device.py +0 -0
- {haystack_ai-3.3.0rc1 → haystack_ai-3.4.0}/haystack/utils/experimental.py +0 -0
- {haystack_ai-3.3.0rc1 → haystack_ai-3.4.0}/haystack/utils/hf.py +0 -0
- {haystack_ai-3.3.0rc1 → haystack_ai-3.4.0}/haystack/utils/http_client.py +0 -0
- {haystack_ai-3.3.0rc1 → haystack_ai-3.4.0}/haystack/utils/jinja2_chat_extension.py +0 -0
- {haystack_ai-3.3.0rc1 → haystack_ai-3.4.0}/haystack/utils/jinja2_extensions.py +0 -0
- {haystack_ai-3.3.0rc1 → haystack_ai-3.4.0}/haystack/utils/jinja2_sandbox.py +0 -0
- {haystack_ai-3.3.0rc1 → haystack_ai-3.4.0}/haystack/utils/jupyter.py +0 -0
- {haystack_ai-3.3.0rc1 → haystack_ai-3.4.0}/haystack/utils/requests_utils.py +0 -0
- {haystack_ai-3.3.0rc1 → haystack_ai-3.4.0}/haystack/utils/type_serialization.py +0 -0
- {haystack_ai-3.3.0rc1 → haystack_ai-3.4.0}/haystack/utils/url_validation.py +0 -0
- {haystack_ai-3.3.0rc1 → haystack_ai-3.4.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.4.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
|
|
@@ -18,13 +18,13 @@ Classifier: License :: OSI Approved :: Apache Software License
|
|
|
18
18
|
Classifier: Operating System :: OS Independent
|
|
19
19
|
Classifier: Programming Language :: Python
|
|
20
20
|
Classifier: Programming Language :: Python :: 3
|
|
21
|
-
Classifier: Programming Language :: Python :: 3.10
|
|
22
21
|
Classifier: Programming Language :: Python :: 3.11
|
|
23
22
|
Classifier: Programming Language :: Python :: 3.12
|
|
24
23
|
Classifier: Programming Language :: Python :: 3.13
|
|
25
24
|
Classifier: Programming Language :: Python :: 3.14
|
|
25
|
+
Classifier: Programming Language :: Python :: 3.15
|
|
26
26
|
Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
|
|
27
|
-
Requires-Python: >=3.
|
|
27
|
+
Requires-Python: >=3.11
|
|
28
28
|
Requires-Dist: anyio>=4.14.2
|
|
29
29
|
Requires-Dist: docstring-parser
|
|
30
30
|
Requires-Dist: filetype
|
|
@@ -43,7 +43,6 @@ Requires-Dist: python-dateutil
|
|
|
43
43
|
Requires-Dist: pyyaml
|
|
44
44
|
Requires-Dist: tenacity!=8.4.0
|
|
45
45
|
Requires-Dist: tqdm
|
|
46
|
-
Requires-Dist: typing-extensions>=4.7
|
|
47
46
|
Description-Content-Type: text/markdown
|
|
48
47
|
|
|
49
48
|
<div align="center">
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
3.4.0
|
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
# SPDX-License-Identifier: Apache-2.0
|
|
4
4
|
|
|
5
5
|
import inspect
|
|
6
|
+
from collections import Counter
|
|
6
7
|
from dataclasses import dataclass
|
|
7
8
|
from typing import Any, Literal, cast
|
|
8
9
|
|
|
@@ -28,7 +29,12 @@ from haystack.components.agents.utils import (
|
|
|
28
29
|
)
|
|
29
30
|
from haystack.components.builders import ChatPromptBuilder
|
|
30
31
|
from haystack.components.generators.chat.types import ChatGenerator
|
|
31
|
-
from haystack.core.serialization import
|
|
32
|
+
from haystack.core.serialization import (
|
|
33
|
+
component_to_dict,
|
|
34
|
+
default_from_dict,
|
|
35
|
+
default_to_dict,
|
|
36
|
+
generate_qualified_class_name,
|
|
37
|
+
)
|
|
32
38
|
from haystack.dataclasses import ChatMessage, ChatRole, StreamingCallbackT, select_streaming_callback
|
|
33
39
|
from haystack.hooks.invocation import _run_hooks, _run_hooks_async
|
|
34
40
|
from haystack.hooks.protocol import (
|
|
@@ -51,13 +57,18 @@ from haystack.hooks.utils import (
|
|
|
51
57
|
warm_up_hooks_async,
|
|
52
58
|
)
|
|
53
59
|
from haystack.tools import (
|
|
60
|
+
ComponentTool,
|
|
61
|
+
Tool,
|
|
54
62
|
Toolset,
|
|
55
63
|
ToolsType,
|
|
56
64
|
_check_duplicate_tool_names,
|
|
65
|
+
close_tools,
|
|
66
|
+
close_tools_async,
|
|
57
67
|
deserialize_tools_or_toolset_inplace,
|
|
58
68
|
flatten_tools_or_toolsets,
|
|
59
69
|
serialize_tools_or_toolset,
|
|
60
70
|
warm_up_tools,
|
|
71
|
+
warm_up_tools_async,
|
|
61
72
|
)
|
|
62
73
|
from haystack.utils.async_utils import _execute_component_async
|
|
63
74
|
from haystack.utils.callable_serialization import deserialize_callable, serialize_callable
|
|
@@ -199,6 +210,34 @@ def _pending_tool_call_messages_from_state(state: State) -> list[ChatMessage]:
|
|
|
199
210
|
return [last_message] if last_message.tool_calls else []
|
|
200
211
|
|
|
201
212
|
|
|
213
|
+
def _get_tool_telemetry_data(tool: Tool) -> dict[str, Any]:
|
|
214
|
+
"""
|
|
215
|
+
Describe a single tool for telemetry.
|
|
216
|
+
|
|
217
|
+
Descriptions are deliberately left out: they are free text that can hold personal or confidential data.
|
|
218
|
+
"""
|
|
219
|
+
data = {"type": generate_qualified_class_name(type(tool)), "name": tool.name}
|
|
220
|
+
if isinstance(tool, ComponentTool):
|
|
221
|
+
data["component"] = generate_qualified_class_name(type(tool._component))
|
|
222
|
+
return data
|
|
223
|
+
|
|
224
|
+
|
|
225
|
+
def _get_tools_telemetry_data(tools: ToolsType) -> dict[str, Any]:
|
|
226
|
+
"""
|
|
227
|
+
Summarize the tools for telemetry.
|
|
228
|
+
|
|
229
|
+
Call it only after the tools are warmed up: flattening iterates the Toolsets, and some of them (e.g.
|
|
230
|
+
SearchableToolset) warm up on iteration, while others (e.g. MCPToolset) only load their tools in `warm_up()`.
|
|
231
|
+
"""
|
|
232
|
+
toolsets = [tools] if isinstance(tools, Toolset) else [entry for entry in tools if isinstance(entry, Toolset)]
|
|
233
|
+
flat_tools = flatten_tools_or_toolsets(tools=tools)
|
|
234
|
+
return {
|
|
235
|
+
"count": len(flat_tools),
|
|
236
|
+
"tools": [_get_tool_telemetry_data(tool) for tool in flat_tools],
|
|
237
|
+
"toolset_types": dict(Counter(generate_qualified_class_name(type(toolset)) for toolset in toolsets)),
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
|
|
202
241
|
@dataclass(kw_only=True)
|
|
203
242
|
class _ExecutionContext:
|
|
204
243
|
"""
|
|
@@ -241,7 +280,7 @@ class Agent:
|
|
|
241
280
|
|
|
242
281
|
```python
|
|
243
282
|
from haystack.components.agents import Agent
|
|
244
|
-
from haystack.components.generators.chat import
|
|
283
|
+
from haystack.components.generators.chat import OpenAIResponsesChatGenerator
|
|
245
284
|
from haystack.components.generators.utils import print_streaming_chunk
|
|
246
285
|
from haystack.dataclasses import ChatMessage
|
|
247
286
|
from haystack.tools import tool
|
|
@@ -272,7 +311,7 @@ class Agent:
|
|
|
272
311
|
"You are a helpful assistant. Use the 'search' tool to find information "
|
|
273
312
|
"about a user's question and the 'calculator' tool to perform math."
|
|
274
313
|
),
|
|
275
|
-
chat_generator=
|
|
314
|
+
chat_generator=OpenAIResponsesChatGenerator(),
|
|
276
315
|
tools=[search, calculator],
|
|
277
316
|
streaming_callback=print_streaming_chunk,
|
|
278
317
|
)
|
|
@@ -293,7 +332,7 @@ class Agent:
|
|
|
293
332
|
|
|
294
333
|
```python
|
|
295
334
|
from haystack.components.agents import Agent
|
|
296
|
-
from haystack.components.generators.chat import
|
|
335
|
+
from haystack.components.generators.chat import OpenAIResponsesChatGenerator
|
|
297
336
|
from haystack.tools import tool
|
|
298
337
|
from typing import Annotated
|
|
299
338
|
|
|
@@ -308,7 +347,7 @@ class Agent:
|
|
|
308
347
|
return f"[Translated '{text}' to {target_language}]"
|
|
309
348
|
|
|
310
349
|
agent = Agent(
|
|
311
|
-
chat_generator=
|
|
350
|
+
chat_generator=OpenAIResponsesChatGenerator(),
|
|
312
351
|
tools=[translate],
|
|
313
352
|
system_prompt="You are a helpful translation assistant.",
|
|
314
353
|
user_prompt=\"\"\"{% message role="user"%}
|
|
@@ -341,13 +380,13 @@ class Agent:
|
|
|
341
380
|
- `on_exit`: runs when the Agent is about to stop on an exit condition. An `on_exit` hook can keep the Agent
|
|
342
381
|
running by setting `state.set("continue_run", True)`.
|
|
343
382
|
|
|
344
|
-
Use the `@hook` decorator to build a hook from a function. This `on_exit` hook keeps the Agent running
|
|
345
|
-
|
|
383
|
+
Use the `@hook` decorator to build a hook from a function. This `on_exit` hook keeps the Agent running when it
|
|
384
|
+
stops with a text reply, until it finishes by calling `save_result`.
|
|
346
385
|
|
|
347
386
|
```python
|
|
348
387
|
from haystack.components.agents import Agent
|
|
349
388
|
from haystack.components.agents.state import State
|
|
350
|
-
from haystack.components.generators.chat import
|
|
389
|
+
from haystack.components.generators.chat import OpenAIResponsesChatGenerator
|
|
351
390
|
from haystack.dataclasses import ChatMessage
|
|
352
391
|
from haystack.hooks import hook
|
|
353
392
|
from haystack.tools import tool
|
|
@@ -363,14 +402,15 @@ class Agent:
|
|
|
363
402
|
|
|
364
403
|
@hook
|
|
365
404
|
def require_save(state: State) -> None:
|
|
366
|
-
if state.
|
|
367
|
-
state.set("messages", [ChatMessage.
|
|
405
|
+
if state.data.get("exit_reason") == "text":
|
|
406
|
+
state.set("messages", [ChatMessage.from_user("Call `save_result` before finishing.")])
|
|
368
407
|
state.set("continue_run", True) # keep the Agent running instead of stopping
|
|
369
408
|
|
|
370
409
|
|
|
371
410
|
agent = Agent(
|
|
372
|
-
chat_generator=
|
|
411
|
+
chat_generator=OpenAIResponsesChatGenerator(),
|
|
373
412
|
tools=[save_result],
|
|
413
|
+
exit_conditions=["save_result"],
|
|
374
414
|
hooks={"on_exit": [require_save]},
|
|
375
415
|
)
|
|
376
416
|
```
|
|
@@ -601,7 +641,7 @@ class Agent:
|
|
|
601
641
|
|
|
602
642
|
async def warm_up_async(self) -> None:
|
|
603
643
|
"""Warm up the tools, hooks, and the underlying chat generator on the serving event loop."""
|
|
604
|
-
|
|
644
|
+
await warm_up_tools_async(tools=self.tools)
|
|
605
645
|
await warm_up_hooks_async(self.hooks)
|
|
606
646
|
if hasattr(self.chat_generator, "warm_up_async"):
|
|
607
647
|
await self.chat_generator.warm_up_async()
|
|
@@ -609,13 +649,15 @@ class Agent:
|
|
|
609
649
|
self.chat_generator.warm_up()
|
|
610
650
|
|
|
611
651
|
def close(self) -> None:
|
|
612
|
-
"""Release
|
|
652
|
+
"""Release tools, hooks, and chat generator resources."""
|
|
653
|
+
close_tools(tools=self.tools)
|
|
613
654
|
close_hooks(self.hooks)
|
|
614
655
|
if hasattr(self.chat_generator, "close"):
|
|
615
656
|
self.chat_generator.close()
|
|
616
657
|
|
|
617
658
|
async def close_async(self) -> None:
|
|
618
|
-
"""Release
|
|
659
|
+
"""Release async tools, hooks, and chat generator resources."""
|
|
660
|
+
await close_tools_async(tools=self.tools)
|
|
619
661
|
await close_hooks_async(self.hooks)
|
|
620
662
|
if hasattr(self.chat_generator, "close_async"):
|
|
621
663
|
await self.chat_generator.close_async()
|
|
@@ -633,6 +675,15 @@ class Agent:
|
|
|
633
675
|
params: dict[str, Any] = {name: getattr(self, name) for name in init_params if name != "self"}
|
|
634
676
|
return type(self)(**{**params, **overrides})
|
|
635
677
|
|
|
678
|
+
def _get_telemetry_data(self) -> dict[str, Any]:
|
|
679
|
+
"""
|
|
680
|
+
Data that is sent to Posthog for usage analytics.
|
|
681
|
+
"""
|
|
682
|
+
chat_generator_data: dict[str, Any] = {"type": generate_qualified_class_name(type(self.chat_generator))}
|
|
683
|
+
if hasattr(self.chat_generator, "_get_telemetry_data"):
|
|
684
|
+
chat_generator_data.update(self.chat_generator._get_telemetry_data())
|
|
685
|
+
return {"chat_generator": chat_generator_data, "tools": _get_tools_telemetry_data(self.tools)}
|
|
686
|
+
|
|
636
687
|
def to_dict(self) -> dict[str, Any]:
|
|
637
688
|
"""
|
|
638
689
|
Serialize the component to a dictionary.
|
|
@@ -817,9 +868,6 @@ class Agent:
|
|
|
817
868
|
|
|
818
869
|
if isinstance(tools, (Toolset, list)):
|
|
819
870
|
selected = cast(ToolsType, tools) # mypy can't narrow the Union type from the isinstance checks
|
|
820
|
-
# Per-run tools are not covered by the Agent's own warm_up(), so warm them up here.
|
|
821
|
-
# warm_up() is expected to be idempotent, so re-warming on every run is cheap.
|
|
822
|
-
warm_up_tools(tools=selected)
|
|
823
871
|
return _spawn_tools(tools=selected)
|
|
824
872
|
|
|
825
873
|
raise TypeError(
|
|
@@ -847,6 +895,8 @@ class Agent:
|
|
|
847
895
|
precedence, keys set only at initialization are kept.
|
|
848
896
|
:param tools: Optional list of Tool objects, a Toolset, or list of tool names to use for this run.
|
|
849
897
|
When passing tool names, tools are selected from the Agent's originally configured tools.
|
|
898
|
+
Tool and Toolset objects passed here are warmed up automatically; the caller is responsible for
|
|
899
|
+
closing them if they hold resources.
|
|
850
900
|
:param hook_context: Optional dictionary of request-scoped resources made available to hooks via
|
|
851
901
|
`state.data.get("hook_context")`. Useful in web/server environments to provide per-request objects
|
|
852
902
|
(e.g., WebSocket connections, async queues, Redis pub/sub clients) that a hook can use, for
|
|
@@ -874,6 +924,12 @@ class Agent:
|
|
|
874
924
|
"""
|
|
875
925
|
agent_inputs = {"messages": messages, "streaming_callback": streaming_callback, **kwargs}
|
|
876
926
|
self.warm_up()
|
|
927
|
+
# warm up tools passed at runtime
|
|
928
|
+
if isinstance(tools, Toolset):
|
|
929
|
+
tools_to_warm_up: ToolsType = tools
|
|
930
|
+
else:
|
|
931
|
+
tools_to_warm_up = [tool for tool in tools or [] if not isinstance(tool, str)]
|
|
932
|
+
warm_up_tools(tools=tools_to_warm_up)
|
|
877
933
|
|
|
878
934
|
exe_context = self._initialize_fresh_execution(
|
|
879
935
|
messages=messages,
|
|
@@ -934,6 +990,9 @@ class Agent:
|
|
|
934
990
|
with the `generation_kwargs` passed at the chat generator's initialization: keys provided here take
|
|
935
991
|
precedence, keys set only at initialization are kept.
|
|
936
992
|
:param tools: Optional list of Tool objects, a Toolset, or list of tool names to use for this run.
|
|
993
|
+
When passing tool names, tools are selected from the Agent's originally configured tools.
|
|
994
|
+
Tool and Toolset objects passed here are warmed up automatically; the caller is responsible for
|
|
995
|
+
closing them if they hold resources.
|
|
937
996
|
:param hook_context: Optional dictionary of request-scoped resources made available to hooks via
|
|
938
997
|
`state.data.get("hook_context")`. Useful in web/server environments to provide per-request objects
|
|
939
998
|
(e.g., WebSocket connections, async queues, Redis pub/sub clients) that a hook can use, for
|
|
@@ -961,6 +1020,12 @@ class Agent:
|
|
|
961
1020
|
"""
|
|
962
1021
|
agent_inputs = {"messages": messages, "streaming_callback": streaming_callback, **kwargs}
|
|
963
1022
|
await self.warm_up_async()
|
|
1023
|
+
# warm up tools passed at runtime
|
|
1024
|
+
if isinstance(tools, Toolset):
|
|
1025
|
+
tools_to_warm_up: ToolsType = tools
|
|
1026
|
+
else:
|
|
1027
|
+
tools_to_warm_up = [tool for tool in tools or [] if not isinstance(tool, str)]
|
|
1028
|
+
await warm_up_tools_async(tools=tools_to_warm_up)
|
|
964
1029
|
|
|
965
1030
|
exe_context = self._initialize_fresh_execution(
|
|
966
1031
|
messages=messages,
|
|
@@ -17,7 +17,7 @@ from haystack.dataclasses import ChatMessage, ToolCall
|
|
|
17
17
|
from haystack.dataclasses.streaming_chunk import StreamingCallbackT, StreamingChunk, _invoke_streaming_callback
|
|
18
18
|
from haystack.tools import ComponentTool, Tool, ToolsType, _check_duplicate_tool_names, flatten_tools_or_toolsets
|
|
19
19
|
from haystack.tools.errors import ToolInvocationError
|
|
20
|
-
from haystack.tools.parameters_schema_utils import
|
|
20
|
+
from haystack.tools.parameters_schema_utils import _is_state_annotation
|
|
21
21
|
from haystack.tracing.utils import _serializable_value
|
|
22
22
|
|
|
23
23
|
logger = logging.getLogger(__name__)
|
|
@@ -187,7 +187,7 @@ def _create_tool_span(tool: Tool, tool_call: ToolCall, parent_span: tracing.Span
|
|
|
187
187
|
"execute tool" span per invocation
|
|
188
188
|
(https://github.com/open-telemetry/semantic-conventions-genai/blob/main/docs/gen-ai/gen-ai-agent-spans.md#execute-tool-span),
|
|
189
189
|
and tracing backends such as Langfuse follow the same model. The span is tagged with the tool's identity; the caller
|
|
190
|
-
adds the call arguments and result as content tags.
|
|
190
|
+
adds the call arguments and result as content tags, and sets `haystack.tool.error` to `True` when the call fails.
|
|
191
191
|
|
|
192
192
|
The parent is passed in rather than read from the tracer here: the calls of a step run concurrently, so a tracer
|
|
193
193
|
that tracks the active span globally would report a sibling tool span as current and the spans would end up chained
|
|
@@ -218,6 +218,7 @@ def _make_context_bound_invoke(
|
|
|
218
218
|
try:
|
|
219
219
|
result = tool.invoke(**args)
|
|
220
220
|
except ToolInvocationError as e:
|
|
221
|
+
span.set_tag("haystack.tool.error", True)
|
|
221
222
|
span.set_content_tag("haystack.agent.step.tool.output", {"error": str(e)})
|
|
222
223
|
return e
|
|
223
224
|
span.set_content_tag("haystack.agent.step.tool.output", result)
|
|
@@ -255,6 +256,7 @@ def _make_bounded_invoke_async(
|
|
|
255
256
|
try:
|
|
256
257
|
result = await tool.invoke_async(**args)
|
|
257
258
|
except ToolInvocationError as e:
|
|
259
|
+
span.set_tag("haystack.tool.error", True)
|
|
258
260
|
span.set_content_tag("haystack.agent.step.tool.output", {"error": str(e)})
|
|
259
261
|
return e
|
|
260
262
|
span.set_content_tag("haystack.agent.step.tool.output", result)
|
|
@@ -307,7 +309,7 @@ def _inject_state_args(tool: Tool, llm_args: dict[str, Any], state: State) -> di
|
|
|
307
309
|
|
|
308
310
|
# We also inject the full State object for any parameter annotated as State
|
|
309
311
|
for param_name, param_type in func_params.items():
|
|
310
|
-
if
|
|
312
|
+
if _is_state_annotation(param_type):
|
|
311
313
|
final_args[param_name] = state
|
|
312
314
|
|
|
313
315
|
return final_args
|
|
@@ -424,7 +426,7 @@ def _state_io_for_call(tool: Tool, llm_args: dict[str, Any]) -> tuple[_StateKeys
|
|
|
424
426
|
"""
|
|
425
427
|
func_params = _get_func_params(tool)
|
|
426
428
|
# Check if State is in func_params
|
|
427
|
-
if any(
|
|
429
|
+
if any(_is_state_annotation(param_type) for param_type in func_params.values()):
|
|
428
430
|
return _ALL_STATE_KEYS, _ALL_STATE_KEYS
|
|
429
431
|
|
|
430
432
|
# Calculate reads
|
|
@@ -115,7 +115,7 @@ def _select_tools_by_name(configured_tools: ToolsType, names: list[str]) -> list
|
|
|
115
115
|
Standalone Tools are kept when their name is requested. A Toolset with run-scoped state (one overriding
|
|
116
116
|
`spawn()`, such as SearchableToolset) is replaced by a per-run copy carrying the requested names, so its
|
|
117
117
|
dynamic behavior (search/lazy-loading) is preserved without mutating the shared, configured Toolset. Any
|
|
118
|
-
other Toolset is
|
|
118
|
+
other Toolset is reduced to the matching Tools. The caller must warm up the tools before selection.
|
|
119
119
|
|
|
120
120
|
:param configured_tools: The tools configured on the Agent.
|
|
121
121
|
:param names: The requested tool names.
|
{haystack_ai-3.3.0rc1 → haystack_ai-3.4.0}/haystack/components/builders/chat_prompt_builder.py
RENAMED
|
@@ -68,13 +68,13 @@ class ChatPromptBuilder:
|
|
|
68
68
|
|
|
69
69
|
```python
|
|
70
70
|
from haystack.components.builders import ChatPromptBuilder
|
|
71
|
-
from haystack.components.generators.chat import
|
|
71
|
+
from haystack.components.generators.chat import OpenAIResponsesChatGenerator
|
|
72
72
|
from haystack.dataclasses import ChatMessage
|
|
73
73
|
from haystack import Pipeline
|
|
74
74
|
|
|
75
75
|
# no parameter init, we don't use any runtime template variables
|
|
76
76
|
prompt_builder = ChatPromptBuilder()
|
|
77
|
-
llm =
|
|
77
|
+
llm = OpenAIResponsesChatGenerator(model="gpt-5.6-luna")
|
|
78
78
|
|
|
79
79
|
pipe = Pipeline()
|
|
80
80
|
pipe.add_component("prompt_builder", prompt_builder)
|
|
@@ -88,26 +88,18 @@ class ChatPromptBuilder:
|
|
|
88
88
|
|
|
89
89
|
res = pipe.run(data={"prompt_builder": {"template_variables": {"location": location, "language": language},
|
|
90
90
|
"template": messages}})
|
|
91
|
-
print(res)
|
|
92
|
-
# >>
|
|
93
|
-
#
|
|
94
|
-
# and diverse cities in Europe. Here are some key things to know...Enjoy your time exploring the vibrant and dynamic
|
|
95
|
-
# capital of Germany!")], _name=None, _meta={'model': 'gpt-5-mini',
|
|
96
|
-
# 'index': 0, 'finish_reason': 'stop', 'usage': {'prompt_tokens': 27, 'completion_tokens': 681, 'total_tokens':
|
|
97
|
-
# 708}})]}}
|
|
91
|
+
print(res["llm"]["replies"][0].text)
|
|
92
|
+
# >> Berlin is the capital city of Germany and one of the most vibrant and diverse cities in Europe. Here are some
|
|
93
|
+
# >> key things to know...Enjoy your time exploring the vibrant and dynamic capital of Germany!
|
|
98
94
|
|
|
99
95
|
messages = [system_message, ChatMessage.from_user("What's the weather forecast for {{location}} in the next {{day_count}} days?")]
|
|
100
96
|
|
|
101
97
|
res = pipe.run(data={"prompt_builder": {"template_variables": {"location": location, "day_count": "5"},
|
|
102
98
|
"template": messages}})
|
|
103
99
|
|
|
104
|
-
print(res)
|
|
105
|
-
# >>
|
|
106
|
-
#
|
|
107
|
-
# days:\\n\\nDay 1: Mostly cloudy with a high of 22°C (72°F) and...so it's always a good idea to check for updates
|
|
108
|
-
# closer to your visit.")], _name=None, _meta={'model': 'gpt-5-mini',
|
|
109
|
-
# 'index': 0, 'finish_reason': 'stop', 'usage': {'prompt_tokens': 37, 'completion_tokens': 201,
|
|
110
|
-
# 'total_tokens': 238}})]}}
|
|
100
|
+
print(res["llm"]["replies"][0].text)
|
|
101
|
+
# >> Here is the weather forecast for Berlin in the next 5 days: Day 1: Mostly cloudy with a high of 22°C (72°F)
|
|
102
|
+
# >> and...so it's always a good idea to check for updates closer to your visit.
|
|
111
103
|
```
|
|
112
104
|
|
|
113
105
|
#### String prompt template
|
|
@@ -47,7 +47,7 @@ class PromptBuilder:
|
|
|
47
47
|
```python
|
|
48
48
|
from haystack import Pipeline, Document
|
|
49
49
|
from haystack.utils import Secret
|
|
50
|
-
from haystack.components.generators.chat import
|
|
50
|
+
from haystack.components.generators.chat import OpenAIResponsesChatGenerator
|
|
51
51
|
from haystack.components.builders.prompt_builder import PromptBuilder
|
|
52
52
|
|
|
53
53
|
# in a real world use case documents could come from a retriever, web, or any other source
|
|
@@ -64,7 +64,7 @@ class PromptBuilder:
|
|
|
64
64
|
\"\"\"
|
|
65
65
|
p = Pipeline()
|
|
66
66
|
p.add_component(instance=PromptBuilder(template=prompt_template), name="prompt_builder")
|
|
67
|
-
p.add_component(instance=
|
|
67
|
+
p.add_component(instance=OpenAIResponsesChatGenerator(api_key=Secret.from_env_var("OPENAI_API_KEY")), name="llm")
|
|
68
68
|
p.connect("prompt_builder", "llm")
|
|
69
69
|
|
|
70
70
|
question = "Where does Joe live?"
|
|
@@ -215,7 +215,7 @@ class CSVToDocument:
|
|
|
215
215
|
Remaining row columns are added to ``meta`` with collision-safe
|
|
216
216
|
keys (prefixed with ``csv_`` if needed).
|
|
217
217
|
"""
|
|
218
|
-
row_meta =
|
|
218
|
+
row_meta = {**base_meta, "row_number": row_index}
|
|
219
219
|
|
|
220
220
|
# content (strict: content_column must exist; validated by caller)
|
|
221
221
|
content = self._safe_value(row.get(content_column))
|
|
@@ -235,5 +235,4 @@ class CSVToDocument:
|
|
|
235
235
|
suffix += 1
|
|
236
236
|
row_meta[key_to_use] = self._safe_value(v)
|
|
237
237
|
|
|
238
|
-
row_meta["row_number"] = row_index
|
|
239
238
|
return Document(content=content, meta=row_meta)
|
|
@@ -19,6 +19,14 @@ from haystack.lazy_imports import LazyImport
|
|
|
19
19
|
|
|
20
20
|
logger = logging.getLogger(__name__)
|
|
21
21
|
|
|
22
|
+
_WORD_NS = "http://schemas.openxmlformats.org/wordprocessingml/2006/main"
|
|
23
|
+
_MARKUP_COMPATIBILITY_NS = "http://schemas.openxmlformats.org/markup-compatibility/2006"
|
|
24
|
+
# A text box keeps its paragraphs here, nested under the drawing inside a run.
|
|
25
|
+
_TEXT_BOX_CONTENT_TAG = f"{{{_WORD_NS}}}txbxContent"
|
|
26
|
+
# Word writes a text box twice inside `mc:AlternateContent`: a `wps:txbx` under
|
|
27
|
+
# `mc:Choice` and the same text as a VML text box under `mc:Fallback`.
|
|
28
|
+
_FALLBACK_TAG = f"{{{_MARKUP_COMPATIBILITY_NS}}}Fallback"
|
|
29
|
+
|
|
22
30
|
# A Markdown table row ends at a line break and its columns are separated by pipes, so
|
|
23
31
|
# neither can survive inside a cell.
|
|
24
32
|
_MARKDOWN_CELL_BREAK_PATTERN = re.compile(r"\s*(?:\r\n|\r|\n)\s*")
|
|
@@ -251,6 +259,7 @@ class DOCXToDocument:
|
|
|
251
259
|
else:
|
|
252
260
|
para_text = self._process_links_in_paragraph(paragraph)
|
|
253
261
|
elements.append(para_text)
|
|
262
|
+
elements.extend(self._extract_text_boxes(element, document))
|
|
254
263
|
elif element.tag.endswith("tbl"):
|
|
255
264
|
table = docx.table.Table(element, document)
|
|
256
265
|
table_str = (
|
|
@@ -262,6 +271,34 @@ class DOCXToDocument:
|
|
|
262
271
|
|
|
263
272
|
return elements
|
|
264
273
|
|
|
274
|
+
def _extract_text_boxes(self, element: Any, document: "DocxDocument") -> list[str]:
|
|
275
|
+
"""
|
|
276
|
+
Extracts the text of any text box anchored to a paragraph.
|
|
277
|
+
|
|
278
|
+
A text box holds its own paragraphs and tables under `w:txbxContent`, nested in
|
|
279
|
+
the drawing inside a run, so the anchoring paragraph's text never reaches them.
|
|
280
|
+
|
|
281
|
+
:param element: The `w:p` element to look under.
|
|
282
|
+
:param document: The DOCX Document object.
|
|
283
|
+
:returns: List of strings, one per paragraph or table found inside a text box.
|
|
284
|
+
"""
|
|
285
|
+
elements = []
|
|
286
|
+
for text_box in element.iter(_TEXT_BOX_CONTENT_TAG):
|
|
287
|
+
# Skip the `mc:Fallback` copy, or the same text is emitted twice.
|
|
288
|
+
if any(ancestor.tag == _FALLBACK_TAG for ancestor in text_box.iterancestors()):
|
|
289
|
+
continue
|
|
290
|
+
for child in text_box:
|
|
291
|
+
if child.tag.endswith("p"):
|
|
292
|
+
elements.append(self._process_links_in_paragraph(Paragraph(child, document)))
|
|
293
|
+
elif child.tag.endswith("tbl"):
|
|
294
|
+
table = docx.table.Table(child, document)
|
|
295
|
+
elements.append(
|
|
296
|
+
self._table_to_markdown(table)
|
|
297
|
+
if self.table_format == DOCXTableFormat.MARKDOWN
|
|
298
|
+
else self._table_to_csv(table)
|
|
299
|
+
)
|
|
300
|
+
return elements
|
|
301
|
+
|
|
265
302
|
def _process_paragraph_with_page_breaks(self, paragraph: "Paragraph") -> str:
|
|
266
303
|
"""
|
|
267
304
|
Processes a paragraph with page breaks.
|
|
@@ -303,10 +340,12 @@ class DOCXToDocument:
|
|
|
303
340
|
if isinstance(content, Run):
|
|
304
341
|
text += content.text
|
|
305
342
|
elif isinstance(content, Hyperlink):
|
|
343
|
+
# Use `.url` instead of `.address` so a URL fragment stored
|
|
344
|
+
# separately by Word (in `w:anchor`) is not dropped.
|
|
306
345
|
if self.link_format == DOCXLinkFormat.MARKDOWN:
|
|
307
|
-
formatted_link = f"[{content.text}]({content.
|
|
346
|
+
formatted_link = f"[{content.text}]({content.url})"
|
|
308
347
|
else: # PLAIN format
|
|
309
|
-
formatted_link = f"{content.text} ({content.
|
|
348
|
+
formatted_link = f"{content.text} ({content.url})"
|
|
310
349
|
text += formatted_link
|
|
311
350
|
|
|
312
351
|
return text
|
|
@@ -36,7 +36,10 @@ class HTMLToDocument:
|
|
|
36
36
|
"""
|
|
37
37
|
|
|
38
38
|
def __init__(
|
|
39
|
-
self,
|
|
39
|
+
self,
|
|
40
|
+
extraction_kwargs: dict[str, Any] | None = None,
|
|
41
|
+
store_full_path: bool = False,
|
|
42
|
+
encoding: str | None = None,
|
|
40
43
|
) -> None:
|
|
41
44
|
"""
|
|
42
45
|
Create an HTMLToDocument component.
|
|
@@ -48,8 +51,9 @@ class HTMLToDocument:
|
|
|
48
51
|
If True, the full path of the file is stored in the metadata of the document.
|
|
49
52
|
If False, only the file name is stored.
|
|
50
53
|
:param encoding:
|
|
51
|
-
The
|
|
52
|
-
|
|
54
|
+
The encoding to use when decoding HTML files. If None (the default), Trafilatura detects the encoding.
|
|
55
|
+
If detection produces incorrect characters, install `trafilatura[all]` for additional encoding detection
|
|
56
|
+
support, or set this parameter if you know the encoding.
|
|
53
57
|
"""
|
|
54
58
|
trafilatura_import.check()
|
|
55
59
|
|
|
@@ -124,8 +128,9 @@ class HTMLToDocument:
|
|
|
124
128
|
continue
|
|
125
129
|
|
|
126
130
|
try:
|
|
127
|
-
|
|
128
|
-
|
|
131
|
+
html_bytes = bytestream.data
|
|
132
|
+
html_content = html_bytes.decode(self.encoding) if self.encoding is not None else html_bytes
|
|
133
|
+
text = extract(html_content, **merged_extraction_kwargs)
|
|
129
134
|
except Exception as conversion_e:
|
|
130
135
|
logger.warning(
|
|
131
136
|
"Failed to extract text from {source}. Skipping it. Error: {error}",
|
|
@@ -134,6 +139,10 @@ class HTMLToDocument:
|
|
|
134
139
|
)
|
|
135
140
|
continue
|
|
136
141
|
|
|
142
|
+
if text is None:
|
|
143
|
+
logger.warning("No text could be extracted from {source}. Skipping it.", source=source)
|
|
144
|
+
continue
|
|
145
|
+
|
|
137
146
|
merged_metadata = {**bytestream.meta, **metadata}
|
|
138
147
|
|
|
139
148
|
if not self.store_full_path and "file_path" in bytestream.meta:
|
{haystack_ai-3.3.0rc1 → haystack_ai-3.4.0}/haystack/components/converters/image/image_utils.py
RENAMED
|
@@ -7,9 +7,7 @@ import mimetypes
|
|
|
7
7
|
from collections import defaultdict
|
|
8
8
|
from io import BytesIO
|
|
9
9
|
from pathlib import Path
|
|
10
|
-
from typing import TypedDict, Union
|
|
11
|
-
|
|
12
|
-
from typing_extensions import NotRequired
|
|
10
|
+
from typing import NotRequired, TypedDict, Union
|
|
13
11
|
|
|
14
12
|
from haystack import logging
|
|
15
13
|
from haystack.dataclasses import ByteStream, Document
|
|
@@ -162,54 +160,55 @@ def _convert_pdf_to_images(
|
|
|
162
160
|
|
|
163
161
|
resolved_page_range = page_range or range(1, num_pages + 1)
|
|
164
162
|
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
# Get dimensions of the page
|
|
175
|
-
page = pdf[max(page_number - 1, 0)] # Adjust for 0-based indexing
|
|
176
|
-
_, _, width, height = page.get_mediabox()
|
|
163
|
+
try:
|
|
164
|
+
for page_number in resolved_page_range:
|
|
165
|
+
if page_number < 1 or page_number > num_pages:
|
|
166
|
+
logger.warning(
|
|
167
|
+
"Page {page_number} is out of range for the PDF file {file_path}. Skipping it.",
|
|
168
|
+
page_number=page_number,
|
|
169
|
+
file_path=bytestream.meta.get("file_path"),
|
|
170
|
+
)
|
|
171
|
+
continue
|
|
177
172
|
|
|
178
|
-
|
|
173
|
+
# Get dimensions of the page
|
|
174
|
+
page = pdf[max(page_number - 1, 0)] # Adjust for 0-based indexing
|
|
175
|
+
_, _, width, height = page.get_mediabox()
|
|
179
176
|
|
|
180
|
-
|
|
181
|
-
# the resolution of the image. To convert a DPI value to a scale factor, multiply it by the size of 1 canvas
|
|
182
|
-
# unit in inches (usually 1/72in).
|
|
183
|
-
# https://pypdfium2.readthedocs.io/en/stable/python_api.html#pypdfium2._helpers.page.PdfPage.render
|
|
184
|
-
target_scale = target_resolution_dpi / 72.0
|
|
177
|
+
target_resolution_dpi = 300.0
|
|
185
178
|
|
|
186
|
-
|
|
187
|
-
|
|
179
|
+
# From pypdfium2 docs: scale (float) – A factor scaling the number of pixels per PDF canvas unit. This
|
|
180
|
+
# defines the resolution of the image. To convert a DPI value to a scale factor, multiply it by the size of
|
|
181
|
+
# 1 canvas unit in inches (usually 1/72in).
|
|
182
|
+
# https://pypdfium2.readthedocs.io/en/stable/python_api.html#pypdfium2._helpers.page.PdfPage.render
|
|
183
|
+
target_scale = target_resolution_dpi / 72.0
|
|
188
184
|
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
pixel_limit = pil_max_pixels * 0.9
|
|
185
|
+
# Calculate potential pixels for target_dpi
|
|
186
|
+
pixels_for_target_scale = width * height * target_scale**2
|
|
192
187
|
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
"Large PDF detected ({pixels:.2f} pixels). Resizing the image to fit the pixel limit.",
|
|
197
|
-
pixels=pixels_for_target_scale,
|
|
198
|
-
)
|
|
199
|
-
scale = (pixel_limit / (width * height)) ** 0.5
|
|
188
|
+
pil_max_pixels = PILImage.MAX_IMAGE_PIXELS or int(1024 * 1024 * 1024 // 4 // 3)
|
|
189
|
+
# 90% of PIL's default limit to prevent borderline cases
|
|
190
|
+
pixel_limit = pil_max_pixels * 0.9
|
|
200
191
|
|
|
201
|
-
|
|
192
|
+
scale = target_scale
|
|
193
|
+
if pixels_for_target_scale > pixel_limit:
|
|
194
|
+
logger.info(
|
|
195
|
+
"Large PDF detected ({pixels:.2f} pixels). Resizing the image to fit the pixel limit.",
|
|
196
|
+
pixels=pixels_for_target_scale,
|
|
197
|
+
)
|
|
198
|
+
scale = (pixel_limit / (width * height)) ** 0.5
|
|
202
199
|
|
|
203
|
-
|
|
204
|
-
pdf_bitmap.close()
|
|
205
|
-
if size is not None:
|
|
206
|
-
# Set reducing_gap=None to disable multi-step shrink; better quality.
|
|
207
|
-
# https://pillow.readthedocs.io/en/latest/reference/Image.html#PIL.Image.Image.thumbnail
|
|
208
|
-
image.thumbnail(size=size, reducing_gap=None)
|
|
200
|
+
pdf_bitmap = page.render(scale=scale)
|
|
209
201
|
|
|
210
|
-
|
|
202
|
+
image: "Image" = pdf_bitmap.to_pil()
|
|
203
|
+
pdf_bitmap.close()
|
|
204
|
+
if size is not None:
|
|
205
|
+
# Set reducing_gap=None to disable multi-step shrink; better quality.
|
|
206
|
+
# https://pillow.readthedocs.io/en/latest/reference/Image.html#PIL.Image.Image.thumbnail
|
|
207
|
+
image.thumbnail(size=size, reducing_gap=None)
|
|
211
208
|
|
|
212
|
-
|
|
209
|
+
all_pdf_images.append((page_number, image))
|
|
210
|
+
finally:
|
|
211
|
+
pdf.close()
|
|
213
212
|
|
|
214
213
|
if return_base64:
|
|
215
214
|
return [
|