unique_toolkit 1.4.2__tar.gz → 1.4.4__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.
- {unique_toolkit-1.4.2 → unique_toolkit-1.4.4}/CHANGELOG.md +6 -0
- {unique_toolkit-1.4.2 → unique_toolkit-1.4.4}/PKG-INFO +7 -1
- {unique_toolkit-1.4.2 → unique_toolkit-1.4.4}/pyproject.toml +1 -1
- {unique_toolkit-1.4.2 → unique_toolkit-1.4.4}/unique_toolkit/agentic/tools/a2a/config.py +7 -1
- {unique_toolkit-1.4.2 → unique_toolkit-1.4.4}/unique_toolkit/agentic/tools/a2a/evaluation/config.py +1 -0
- {unique_toolkit-1.4.2 → unique_toolkit-1.4.4}/unique_toolkit/agentic/tools/a2a/evaluation/evaluator.py +43 -37
- unique_toolkit-1.4.4/unique_toolkit/agentic/tools/a2a/postprocessing/postprocessor.py +231 -0
- unique_toolkit-1.4.4/unique_toolkit/agentic/tools/a2a/postprocessing/test/test_postprocessor_reference_functions.py +248 -0
- unique_toolkit-1.4.2/unique_toolkit/agentic/tools/a2a/postprocessing/postprocessor.py +0 -204
- {unique_toolkit-1.4.2 → unique_toolkit-1.4.4}/LICENSE +0 -0
- {unique_toolkit-1.4.2 → unique_toolkit-1.4.4}/README.md +0 -0
- {unique_toolkit-1.4.2 → unique_toolkit-1.4.4}/unique_toolkit/__init__.py +0 -0
- {unique_toolkit-1.4.2 → unique_toolkit-1.4.4}/unique_toolkit/_common/_base_service.py +0 -0
- {unique_toolkit-1.4.2 → unique_toolkit-1.4.4}/unique_toolkit/_common/_time_utils.py +0 -0
- {unique_toolkit-1.4.2 → unique_toolkit-1.4.4}/unique_toolkit/_common/api_calling/human_verification_manager.py +0 -0
- {unique_toolkit-1.4.2 → unique_toolkit-1.4.4}/unique_toolkit/_common/base_model_type_attribute.py +0 -0
- {unique_toolkit-1.4.2 → unique_toolkit-1.4.4}/unique_toolkit/_common/chunk_relevancy_sorter/config.py +0 -0
- {unique_toolkit-1.4.2 → unique_toolkit-1.4.4}/unique_toolkit/_common/chunk_relevancy_sorter/exception.py +0 -0
- {unique_toolkit-1.4.2 → unique_toolkit-1.4.4}/unique_toolkit/_common/chunk_relevancy_sorter/schemas.py +0 -0
- {unique_toolkit-1.4.2 → unique_toolkit-1.4.4}/unique_toolkit/_common/chunk_relevancy_sorter/service.py +0 -0
- {unique_toolkit-1.4.2 → unique_toolkit-1.4.4}/unique_toolkit/_common/chunk_relevancy_sorter/tests/test_service.py +0 -0
- {unique_toolkit-1.4.2 → unique_toolkit-1.4.4}/unique_toolkit/_common/default_language_model.py +0 -0
- {unique_toolkit-1.4.2 → unique_toolkit-1.4.4}/unique_toolkit/_common/endpoint_builder.py +0 -0
- {unique_toolkit-1.4.2 → unique_toolkit-1.4.4}/unique_toolkit/_common/endpoint_requestor.py +0 -0
- {unique_toolkit-1.4.2 → unique_toolkit-1.4.4}/unique_toolkit/_common/exception.py +0 -0
- {unique_toolkit-1.4.2 → unique_toolkit-1.4.4}/unique_toolkit/_common/feature_flags/schema.py +0 -0
- {unique_toolkit-1.4.2 → unique_toolkit-1.4.4}/unique_toolkit/_common/pydantic/rjsf_tags.py +0 -0
- {unique_toolkit-1.4.2 → unique_toolkit-1.4.4}/unique_toolkit/_common/pydantic_helpers.py +0 -0
- {unique_toolkit-1.4.2 → unique_toolkit-1.4.4}/unique_toolkit/_common/string_utilities.py +0 -0
- {unique_toolkit-1.4.2 → unique_toolkit-1.4.4}/unique_toolkit/_common/token/image_token_counting.py +0 -0
- {unique_toolkit-1.4.2 → unique_toolkit-1.4.4}/unique_toolkit/_common/token/token_counting.py +0 -0
- {unique_toolkit-1.4.2 → unique_toolkit-1.4.4}/unique_toolkit/_common/utils/__init__.py +0 -0
- {unique_toolkit-1.4.2 → unique_toolkit-1.4.4}/unique_toolkit/_common/utils/structured_output/__init__.py +0 -0
- {unique_toolkit-1.4.2 → unique_toolkit-1.4.4}/unique_toolkit/_common/utils/structured_output/schema.py +0 -0
- {unique_toolkit-1.4.2 → unique_toolkit-1.4.4}/unique_toolkit/_common/utils/write_configuration.py +0 -0
- {unique_toolkit-1.4.2 → unique_toolkit-1.4.4}/unique_toolkit/_common/validate_required_values.py +0 -0
- {unique_toolkit-1.4.2 → unique_toolkit-1.4.4}/unique_toolkit/_common/validators.py +0 -0
- {unique_toolkit-1.4.2 → unique_toolkit-1.4.4}/unique_toolkit/agentic/__init__.py +0 -0
- {unique_toolkit-1.4.2 → unique_toolkit-1.4.4}/unique_toolkit/agentic/debug_info_manager/debug_info_manager.py +0 -0
- {unique_toolkit-1.4.2 → unique_toolkit-1.4.4}/unique_toolkit/agentic/evaluation/config.py +0 -0
- {unique_toolkit-1.4.2 → unique_toolkit-1.4.4}/unique_toolkit/agentic/evaluation/context_relevancy/prompts.py +0 -0
- {unique_toolkit-1.4.2 → unique_toolkit-1.4.4}/unique_toolkit/agentic/evaluation/context_relevancy/schema.py +0 -0
- {unique_toolkit-1.4.2 → unique_toolkit-1.4.4}/unique_toolkit/agentic/evaluation/context_relevancy/service.py +0 -0
- {unique_toolkit-1.4.2 → unique_toolkit-1.4.4}/unique_toolkit/agentic/evaluation/evaluation_manager.py +0 -0
- {unique_toolkit-1.4.2 → unique_toolkit-1.4.4}/unique_toolkit/agentic/evaluation/exception.py +0 -0
- {unique_toolkit-1.4.2 → unique_toolkit-1.4.4}/unique_toolkit/agentic/evaluation/hallucination/constants.py +0 -0
- {unique_toolkit-1.4.2 → unique_toolkit-1.4.4}/unique_toolkit/agentic/evaluation/hallucination/hallucination_evaluation.py +0 -0
- {unique_toolkit-1.4.2 → unique_toolkit-1.4.4}/unique_toolkit/agentic/evaluation/hallucination/prompts.py +0 -0
- {unique_toolkit-1.4.2 → unique_toolkit-1.4.4}/unique_toolkit/agentic/evaluation/hallucination/service.py +0 -0
- {unique_toolkit-1.4.2 → unique_toolkit-1.4.4}/unique_toolkit/agentic/evaluation/hallucination/utils.py +0 -0
- {unique_toolkit-1.4.2 → unique_toolkit-1.4.4}/unique_toolkit/agentic/evaluation/output_parser.py +0 -0
- {unique_toolkit-1.4.2 → unique_toolkit-1.4.4}/unique_toolkit/agentic/evaluation/schemas.py +0 -0
- {unique_toolkit-1.4.2 → unique_toolkit-1.4.4}/unique_toolkit/agentic/evaluation/tests/test_context_relevancy_service.py +0 -0
- {unique_toolkit-1.4.2 → unique_toolkit-1.4.4}/unique_toolkit/agentic/evaluation/tests/test_output_parser.py +0 -0
- {unique_toolkit-1.4.2 → unique_toolkit-1.4.4}/unique_toolkit/agentic/history_manager/history_construction_with_contents.py +0 -0
- {unique_toolkit-1.4.2 → unique_toolkit-1.4.4}/unique_toolkit/agentic/history_manager/history_manager.py +0 -0
- {unique_toolkit-1.4.2 → unique_toolkit-1.4.4}/unique_toolkit/agentic/history_manager/loop_token_reducer.py +0 -0
- {unique_toolkit-1.4.2 → unique_toolkit-1.4.4}/unique_toolkit/agentic/history_manager/utils.py +0 -0
- {unique_toolkit-1.4.2 → unique_toolkit-1.4.4}/unique_toolkit/agentic/postprocessor/postprocessor_manager.py +0 -0
- {unique_toolkit-1.4.2 → unique_toolkit-1.4.4}/unique_toolkit/agentic/reference_manager/reference_manager.py +0 -0
- {unique_toolkit-1.4.2 → unique_toolkit-1.4.4}/unique_toolkit/agentic/short_term_memory_manager/persistent_short_term_memory_manager.py +0 -0
- {unique_toolkit-1.4.2 → unique_toolkit-1.4.4}/unique_toolkit/agentic/thinking_manager/thinking_manager.py +0 -0
- {unique_toolkit-1.4.2 → unique_toolkit-1.4.4}/unique_toolkit/agentic/tools/__init__.py +0 -0
- {unique_toolkit-1.4.2 → unique_toolkit-1.4.4}/unique_toolkit/agentic/tools/a2a/__init__.py +0 -0
- {unique_toolkit-1.4.2 → unique_toolkit-1.4.4}/unique_toolkit/agentic/tools/a2a/evaluation/__init__.py +0 -0
- {unique_toolkit-1.4.2 → unique_toolkit-1.4.4}/unique_toolkit/agentic/tools/a2a/evaluation/summarization_user_message.j2 +0 -0
- {unique_toolkit-1.4.2 → unique_toolkit-1.4.4}/unique_toolkit/agentic/tools/a2a/manager.py +0 -0
- {unique_toolkit-1.4.2 → unique_toolkit-1.4.4}/unique_toolkit/agentic/tools/a2a/memory.py +0 -0
- {unique_toolkit-1.4.2 → unique_toolkit-1.4.4}/unique_toolkit/agentic/tools/a2a/postprocessing/__init__.py +0 -0
- {unique_toolkit-1.4.2 → unique_toolkit-1.4.4}/unique_toolkit/agentic/tools/a2a/postprocessing/display.py +0 -0
- {unique_toolkit-1.4.2 → unique_toolkit-1.4.4}/unique_toolkit/agentic/tools/a2a/postprocessing/test/test_display.py +0 -0
- {unique_toolkit-1.4.2 → unique_toolkit-1.4.4}/unique_toolkit/agentic/tools/a2a/schema.py +0 -0
- {unique_toolkit-1.4.2 → unique_toolkit-1.4.4}/unique_toolkit/agentic/tools/a2a/service.py +0 -0
- {unique_toolkit-1.4.2 → unique_toolkit-1.4.4}/unique_toolkit/agentic/tools/agent_chunks_hanlder.py +0 -0
- {unique_toolkit-1.4.2 → unique_toolkit-1.4.4}/unique_toolkit/agentic/tools/config.py +0 -0
- {unique_toolkit-1.4.2 → unique_toolkit-1.4.4}/unique_toolkit/agentic/tools/factory.py +0 -0
- {unique_toolkit-1.4.2 → unique_toolkit-1.4.4}/unique_toolkit/agentic/tools/mcp/__init__.py +0 -0
- {unique_toolkit-1.4.2 → unique_toolkit-1.4.4}/unique_toolkit/agentic/tools/mcp/manager.py +0 -0
- {unique_toolkit-1.4.2 → unique_toolkit-1.4.4}/unique_toolkit/agentic/tools/mcp/models.py +0 -0
- {unique_toolkit-1.4.2 → unique_toolkit-1.4.4}/unique_toolkit/agentic/tools/mcp/tool_wrapper.py +0 -0
- {unique_toolkit-1.4.2 → unique_toolkit-1.4.4}/unique_toolkit/agentic/tools/schemas.py +0 -0
- {unique_toolkit-1.4.2 → unique_toolkit-1.4.4}/unique_toolkit/agentic/tools/test/test_mcp_manager.py +0 -0
- {unique_toolkit-1.4.2 → unique_toolkit-1.4.4}/unique_toolkit/agentic/tools/test/test_tool_progress_reporter.py +0 -0
- {unique_toolkit-1.4.2 → unique_toolkit-1.4.4}/unique_toolkit/agentic/tools/tool.py +0 -0
- {unique_toolkit-1.4.2 → unique_toolkit-1.4.4}/unique_toolkit/agentic/tools/tool_manager.py +0 -0
- {unique_toolkit-1.4.2 → unique_toolkit-1.4.4}/unique_toolkit/agentic/tools/tool_progress_reporter.py +0 -0
- {unique_toolkit-1.4.2 → unique_toolkit-1.4.4}/unique_toolkit/agentic/tools/utils/__init__.py +0 -0
- {unique_toolkit-1.4.2 → unique_toolkit-1.4.4}/unique_toolkit/agentic/tools/utils/execution/__init__.py +0 -0
- {unique_toolkit-1.4.2 → unique_toolkit-1.4.4}/unique_toolkit/agentic/tools/utils/execution/execution.py +0 -0
- {unique_toolkit-1.4.2 → unique_toolkit-1.4.4}/unique_toolkit/agentic/tools/utils/source_handling/__init__.py +0 -0
- {unique_toolkit-1.4.2 → unique_toolkit-1.4.4}/unique_toolkit/agentic/tools/utils/source_handling/schema.py +0 -0
- {unique_toolkit-1.4.2 → unique_toolkit-1.4.4}/unique_toolkit/agentic/tools/utils/source_handling/source_formatting.py +0 -0
- {unique_toolkit-1.4.2 → unique_toolkit-1.4.4}/unique_toolkit/agentic/tools/utils/source_handling/tests/test_source_formatting.py +0 -0
- {unique_toolkit-1.4.2 → unique_toolkit-1.4.4}/unique_toolkit/app/__init__.py +0 -0
- {unique_toolkit-1.4.2 → unique_toolkit-1.4.4}/unique_toolkit/app/dev_util.py +0 -0
- {unique_toolkit-1.4.2 → unique_toolkit-1.4.4}/unique_toolkit/app/init_logging.py +0 -0
- {unique_toolkit-1.4.2 → unique_toolkit-1.4.4}/unique_toolkit/app/init_sdk.py +0 -0
- {unique_toolkit-1.4.2 → unique_toolkit-1.4.4}/unique_toolkit/app/performance/async_tasks.py +0 -0
- {unique_toolkit-1.4.2 → unique_toolkit-1.4.4}/unique_toolkit/app/performance/async_wrapper.py +0 -0
- {unique_toolkit-1.4.2 → unique_toolkit-1.4.4}/unique_toolkit/app/schemas.py +0 -0
- {unique_toolkit-1.4.2 → unique_toolkit-1.4.4}/unique_toolkit/app/unique_settings.py +0 -0
- {unique_toolkit-1.4.2 → unique_toolkit-1.4.4}/unique_toolkit/app/verification.py +0 -0
- {unique_toolkit-1.4.2 → unique_toolkit-1.4.4}/unique_toolkit/chat/__init__.py +0 -0
- {unique_toolkit-1.4.2 → unique_toolkit-1.4.4}/unique_toolkit/chat/constants.py +0 -0
- {unique_toolkit-1.4.2 → unique_toolkit-1.4.4}/unique_toolkit/chat/functions.py +0 -0
- {unique_toolkit-1.4.2 → unique_toolkit-1.4.4}/unique_toolkit/chat/schemas.py +0 -0
- {unique_toolkit-1.4.2 → unique_toolkit-1.4.4}/unique_toolkit/chat/service.py +0 -0
- {unique_toolkit-1.4.2 → unique_toolkit-1.4.4}/unique_toolkit/chat/state.py +0 -0
- {unique_toolkit-1.4.2 → unique_toolkit-1.4.4}/unique_toolkit/chat/utils.py +0 -0
- {unique_toolkit-1.4.2 → unique_toolkit-1.4.4}/unique_toolkit/content/__init__.py +0 -0
- {unique_toolkit-1.4.2 → unique_toolkit-1.4.4}/unique_toolkit/content/constants.py +0 -0
- {unique_toolkit-1.4.2 → unique_toolkit-1.4.4}/unique_toolkit/content/functions.py +0 -0
- {unique_toolkit-1.4.2 → unique_toolkit-1.4.4}/unique_toolkit/content/schemas.py +0 -0
- {unique_toolkit-1.4.2 → unique_toolkit-1.4.4}/unique_toolkit/content/service.py +0 -0
- {unique_toolkit-1.4.2 → unique_toolkit-1.4.4}/unique_toolkit/content/utils.py +0 -0
- {unique_toolkit-1.4.2 → unique_toolkit-1.4.4}/unique_toolkit/embedding/__init__.py +0 -0
- {unique_toolkit-1.4.2 → unique_toolkit-1.4.4}/unique_toolkit/embedding/constants.py +0 -0
- {unique_toolkit-1.4.2 → unique_toolkit-1.4.4}/unique_toolkit/embedding/functions.py +0 -0
- {unique_toolkit-1.4.2 → unique_toolkit-1.4.4}/unique_toolkit/embedding/schemas.py +0 -0
- {unique_toolkit-1.4.2 → unique_toolkit-1.4.4}/unique_toolkit/embedding/service.py +0 -0
- {unique_toolkit-1.4.2 → unique_toolkit-1.4.4}/unique_toolkit/embedding/utils.py +0 -0
- {unique_toolkit-1.4.2 → unique_toolkit-1.4.4}/unique_toolkit/framework_utilities/__init__.py +0 -0
- {unique_toolkit-1.4.2 → unique_toolkit-1.4.4}/unique_toolkit/framework_utilities/langchain/client.py +0 -0
- {unique_toolkit-1.4.2 → unique_toolkit-1.4.4}/unique_toolkit/framework_utilities/langchain/history.py +0 -0
- {unique_toolkit-1.4.2 → unique_toolkit-1.4.4}/unique_toolkit/framework_utilities/openai/__init__.py +0 -0
- {unique_toolkit-1.4.2 → unique_toolkit-1.4.4}/unique_toolkit/framework_utilities/openai/client.py +0 -0
- {unique_toolkit-1.4.2 → unique_toolkit-1.4.4}/unique_toolkit/framework_utilities/openai/message_builder.py +0 -0
- {unique_toolkit-1.4.2 → unique_toolkit-1.4.4}/unique_toolkit/framework_utilities/utils.py +0 -0
- {unique_toolkit-1.4.2 → unique_toolkit-1.4.4}/unique_toolkit/language_model/__init__.py +0 -0
- {unique_toolkit-1.4.2 → unique_toolkit-1.4.4}/unique_toolkit/language_model/builder.py +0 -0
- {unique_toolkit-1.4.2 → unique_toolkit-1.4.4}/unique_toolkit/language_model/constants.py +0 -0
- {unique_toolkit-1.4.2 → unique_toolkit-1.4.4}/unique_toolkit/language_model/functions.py +0 -0
- {unique_toolkit-1.4.2 → unique_toolkit-1.4.4}/unique_toolkit/language_model/infos.py +0 -0
- {unique_toolkit-1.4.2 → unique_toolkit-1.4.4}/unique_toolkit/language_model/prompt.py +0 -0
- {unique_toolkit-1.4.2 → unique_toolkit-1.4.4}/unique_toolkit/language_model/reference.py +0 -0
- {unique_toolkit-1.4.2 → unique_toolkit-1.4.4}/unique_toolkit/language_model/schemas.py +0 -0
- {unique_toolkit-1.4.2 → unique_toolkit-1.4.4}/unique_toolkit/language_model/service.py +0 -0
- {unique_toolkit-1.4.2 → unique_toolkit-1.4.4}/unique_toolkit/language_model/utils.py +0 -0
- {unique_toolkit-1.4.2 → unique_toolkit-1.4.4}/unique_toolkit/protocols/support.py +0 -0
- {unique_toolkit-1.4.2 → unique_toolkit-1.4.4}/unique_toolkit/short_term_memory/__init__.py +0 -0
- {unique_toolkit-1.4.2 → unique_toolkit-1.4.4}/unique_toolkit/short_term_memory/constants.py +0 -0
- {unique_toolkit-1.4.2 → unique_toolkit-1.4.4}/unique_toolkit/short_term_memory/functions.py +0 -0
- {unique_toolkit-1.4.2 → unique_toolkit-1.4.4}/unique_toolkit/short_term_memory/schemas.py +0 -0
- {unique_toolkit-1.4.2 → unique_toolkit-1.4.4}/unique_toolkit/short_term_memory/service.py +0 -0
- {unique_toolkit-1.4.2 → unique_toolkit-1.4.4}/unique_toolkit/smart_rules/__init__.py +0 -0
- {unique_toolkit-1.4.2 → unique_toolkit-1.4.4}/unique_toolkit/smart_rules/compile.py +0 -0
@@ -5,6 +5,12 @@ All notable changes to this project will be documented in this file.
|
|
5
5
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
6
6
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
7
7
|
|
8
|
+
## [1.4.4] - 2025-09-30
|
9
|
+
- Fix bugs with display of sub-agent answers and evaluations when multiple sub-agent from the same assistant run concurrently.
|
10
|
+
|
11
|
+
## [1.4.3] - 2025-09-30
|
12
|
+
- Fix bug with sub-agent post-processing reference numbers.
|
13
|
+
|
8
14
|
## [1.4.2] - 2025-09-30
|
9
15
|
- Adding litellm models `litellm:anthropic-claude-sonnet-4-5` and `litellm:anthropic-claude-opus-4-1`
|
10
16
|
|
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.1
|
2
2
|
Name: unique_toolkit
|
3
|
-
Version: 1.4.
|
3
|
+
Version: 1.4.4
|
4
4
|
Summary:
|
5
5
|
License: Proprietary
|
6
6
|
Author: Cedric Klinkert
|
@@ -118,6 +118,12 @@ All notable changes to this project will be documented in this file.
|
|
118
118
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
119
119
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
120
120
|
|
121
|
+
## [1.4.4] - 2025-09-30
|
122
|
+
- Fix bugs with display of sub-agent answers and evaluations when multiple sub-agent from the same assistant run concurrently.
|
123
|
+
|
124
|
+
## [1.4.3] - 2025-09-30
|
125
|
+
- Fix bug with sub-agent post-processing reference numbers.
|
126
|
+
|
121
127
|
## [1.4.2] - 2025-09-30
|
122
128
|
- Adding litellm models `litellm:anthropic-claude-sonnet-4-5` and `litellm:anthropic-claude-opus-4-1`
|
123
129
|
|
@@ -9,6 +9,10 @@ DEFAULT_PARAM_DESCRIPTION_SUB_AGENT_USER_MESSAGE = """
|
|
9
9
|
This is the message that will be sent to the sub-agent.
|
10
10
|
""".strip()
|
11
11
|
|
12
|
+
DEFAULT_FORMAT_INFORMATION_SUB_AGENT_SYSTEM_MESSAGE = """
|
13
|
+
NEVER mention any references from sub-agent answers in your response.
|
14
|
+
"""
|
15
|
+
|
12
16
|
|
13
17
|
class ResponseDisplayMode(StrEnum):
|
14
18
|
HIDDEN = "hidden"
|
@@ -40,7 +44,9 @@ class SubAgentToolConfig(BaseToolConfig):
|
|
40
44
|
param_description_sub_agent_user_message: str = (
|
41
45
|
DEFAULT_PARAM_DESCRIPTION_SUB_AGENT_USER_MESSAGE
|
42
46
|
)
|
43
|
-
tool_format_information_for_system_prompt: str =
|
47
|
+
tool_format_information_for_system_prompt: str = (
|
48
|
+
DEFAULT_FORMAT_INFORMATION_SUB_AGENT_SYSTEM_MESSAGE
|
49
|
+
)
|
44
50
|
tool_description_for_user_prompt: str = ""
|
45
51
|
tool_format_information_for_user_prompt: str = ""
|
46
52
|
|
{unique_toolkit-1.4.2 → unique_toolkit-1.4.4}/unique_toolkit/agentic/tools/a2a/evaluation/config.py
RENAMED
@@ -15,6 +15,7 @@ Your task is to give a brief summary (1-10 sentences) of the received assessment
|
|
15
15
|
1. You must NOT in ANY case state a fact that is not stated in the given assessments.
|
16
16
|
2. You must focus first and foremost on the failing assessments, labeled `RED` below.
|
17
17
|
3. You must mention each agent's name when summarizing its list of assessments.
|
18
|
+
4. You must NOT use any markdown formatting in your response as this will FAIL to render in the chat frontend.
|
18
19
|
""".strip()
|
19
20
|
|
20
21
|
with open(Path(__file__).parent / "summarization_user_message.j2", "r") as file:
|
@@ -27,7 +27,7 @@ logger = logging.getLogger(__name__)
|
|
27
27
|
|
28
28
|
|
29
29
|
class _SubAgentToolInfo(TypedDict):
|
30
|
-
|
30
|
+
assessments: list[list[unique_sdk.Space.Assessment]]
|
31
31
|
display_name: str
|
32
32
|
|
33
33
|
|
@@ -53,7 +53,7 @@ class SubAgentsEvaluation(Evaluation):
|
|
53
53
|
if sub_agent_tool.config.evaluation_config.display_evalution:
|
54
54
|
sub_agent_tool.subscribe(self)
|
55
55
|
self._assistant_id_to_tool_info[sub_agent_tool.config.assistant_id] = {
|
56
|
-
"
|
56
|
+
"assessments": [],
|
57
57
|
"display_name": sub_agent_tool.display_name(),
|
58
58
|
}
|
59
59
|
|
@@ -80,47 +80,53 @@ class SubAgentsEvaluation(Evaluation):
|
|
80
80
|
label_comparison_dict[ChatMessageAssessmentLabel.RED] = 0
|
81
81
|
|
82
82
|
for assistant_id, tool_info in self._assistant_id_to_tool_info.items():
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
83
|
+
sub_agent_assessments = tool_info["assessments"] or []
|
84
|
+
for i, assessments in enumerate(sub_agent_assessments, start=1):
|
85
|
+
valid_assessments = []
|
86
|
+
for assessment in assessments:
|
87
|
+
if (
|
88
|
+
assessment["label"] is None
|
89
|
+
or assessment["label"] not in ChatMessageAssessmentLabel
|
90
|
+
):
|
91
|
+
logger.warning(
|
92
|
+
"Unkown assistant label %s for assistant %s will be ignored",
|
93
|
+
assessment["label"],
|
94
|
+
assistant_id,
|
95
|
+
)
|
96
|
+
continue
|
97
|
+
if assessment["status"] != ChatMessageAssessmentStatus.DONE:
|
98
|
+
logger.warning(
|
99
|
+
"Assessment %s for assistant %s is not done (status: %s) will be ignored",
|
100
|
+
assessment["label"],
|
101
|
+
assistant_id,
|
102
|
+
)
|
103
|
+
continue
|
104
|
+
valid_assessments.append(assessment)
|
105
|
+
|
106
|
+
if len(valid_assessments) == 0:
|
107
|
+
logger.info(
|
108
|
+
"No valid assessment found for assistant %s", assistant_id
|
101
109
|
)
|
102
110
|
continue
|
103
|
-
valid_assessments.append(assessment)
|
104
|
-
|
105
|
-
if len(valid_assessments) == 0:
|
106
|
-
logger.info("No valid assessment found for assistant %s", assistant_id)
|
107
|
-
continue
|
108
111
|
|
109
|
-
|
110
|
-
|
111
|
-
)
|
112
|
-
|
113
|
-
for assessment in assessments:
|
114
|
-
value = min(
|
115
|
-
value, assessment["label"], key=lambda x: label_comparison_dict[x]
|
112
|
+
assessments = sorted(
|
113
|
+
valid_assessments, key=lambda x: label_comparison_dict[x["label"]]
|
116
114
|
)
|
117
115
|
|
118
|
-
|
119
|
-
|
116
|
+
for assessment in assessments:
|
117
|
+
value = min(
|
118
|
+
value,
|
119
|
+
assessment["label"],
|
120
|
+
key=lambda x: label_comparison_dict[x],
|
121
|
+
)
|
122
|
+
data = {
|
120
123
|
"name": tool_info["display_name"],
|
121
124
|
"assessments": assessments,
|
122
125
|
}
|
123
|
-
|
126
|
+
if len(sub_agent_assessments) > 1:
|
127
|
+
data["name"] += f" {i}"
|
128
|
+
|
129
|
+
sub_agents_display_data.append(data)
|
124
130
|
|
125
131
|
if len(sub_agents_display_data) == 0:
|
126
132
|
logger.warning("No valid sub agent assessments found")
|
@@ -200,10 +206,10 @@ class SubAgentsEvaluation(Evaluation):
|
|
200
206
|
)
|
201
207
|
return
|
202
208
|
|
203
|
-
self._assistant_id_to_tool_info[sub_agent_assistant_id]["
|
209
|
+
self._assistant_id_to_tool_info[sub_agent_assistant_id]["assessments"].append(
|
204
210
|
response[
|
205
211
|
"assessment"
|
206
212
|
].copy() # Shallow copy as we don't modify individual assessments
|
207
213
|
if response["assessment"] is not None
|
208
|
-
else
|
214
|
+
else []
|
209
215
|
)
|
@@ -0,0 +1,231 @@
|
|
1
|
+
import logging
|
2
|
+
import re
|
3
|
+
from typing import TypedDict, override
|
4
|
+
|
5
|
+
import unique_sdk
|
6
|
+
|
7
|
+
from unique_toolkit.agentic.postprocessor.postprocessor_manager import Postprocessor
|
8
|
+
from unique_toolkit.agentic.tools.a2a.config import (
|
9
|
+
ResponseDisplayMode,
|
10
|
+
SubAgentToolDisplayConfig,
|
11
|
+
)
|
12
|
+
from unique_toolkit.agentic.tools.a2a.postprocessing.display import (
|
13
|
+
build_sub_agent_answer_display,
|
14
|
+
remove_sub_agent_answer_from_text,
|
15
|
+
)
|
16
|
+
from unique_toolkit.agentic.tools.a2a.service import SubAgentTool
|
17
|
+
from unique_toolkit.content.schemas import ContentReference
|
18
|
+
from unique_toolkit.language_model.schemas import LanguageModelStreamResponse
|
19
|
+
|
20
|
+
logger = logging.getLogger(__name__)
|
21
|
+
|
22
|
+
SpaceMessage = unique_sdk.Space.Message
|
23
|
+
|
24
|
+
|
25
|
+
class _SubAgentMessageInfo(TypedDict):
|
26
|
+
text: str | None
|
27
|
+
references: list[unique_sdk.Space.Reference]
|
28
|
+
|
29
|
+
|
30
|
+
class _SubAgentToolInfo(TypedDict):
|
31
|
+
display_name: str
|
32
|
+
display_config: SubAgentToolDisplayConfig
|
33
|
+
responses: list[_SubAgentMessageInfo]
|
34
|
+
|
35
|
+
|
36
|
+
class SubAgentResponsesPostprocessor(Postprocessor):
|
37
|
+
def __init__(
|
38
|
+
self,
|
39
|
+
user_id: str,
|
40
|
+
company_id: str,
|
41
|
+
agent_chat_id: str,
|
42
|
+
sub_agent_tools: list[SubAgentTool],
|
43
|
+
):
|
44
|
+
super().__init__(name=self.__class__.__name__)
|
45
|
+
|
46
|
+
self._user_id = user_id
|
47
|
+
self._company_id = company_id
|
48
|
+
|
49
|
+
self._agent_chat_id = agent_chat_id
|
50
|
+
|
51
|
+
self._assistant_id_to_tool_info: dict[str, _SubAgentToolInfo] = {}
|
52
|
+
|
53
|
+
for sub_agent_tool in sub_agent_tools:
|
54
|
+
sub_agent_tool.subscribe(self)
|
55
|
+
|
56
|
+
self._assistant_id_to_tool_info[sub_agent_tool.config.assistant_id] = (
|
57
|
+
_SubAgentToolInfo(
|
58
|
+
display_config=sub_agent_tool.config.response_display_config,
|
59
|
+
display_name=sub_agent_tool.display_name(),
|
60
|
+
responses=[],
|
61
|
+
)
|
62
|
+
)
|
63
|
+
|
64
|
+
self._sub_agent_message = None
|
65
|
+
|
66
|
+
@override
|
67
|
+
async def run(self, loop_response: LanguageModelStreamResponse) -> None:
|
68
|
+
self._sub_agent_message = await unique_sdk.Space.get_latest_message_async(
|
69
|
+
user_id=self._user_id,
|
70
|
+
company_id=self._company_id,
|
71
|
+
chat_id=self._agent_chat_id,
|
72
|
+
)
|
73
|
+
|
74
|
+
@override
|
75
|
+
def apply_postprocessing_to_response(
|
76
|
+
self, loop_response: LanguageModelStreamResponse
|
77
|
+
) -> bool:
|
78
|
+
logger.info("Adding sub agent responses to the response")
|
79
|
+
|
80
|
+
# Get responses to display
|
81
|
+
displayed = {}
|
82
|
+
for assistant_id, tool_info in self._assistant_id_to_tool_info.items():
|
83
|
+
display_mode = tool_info["display_config"].mode
|
84
|
+
|
85
|
+
if len(tool_info["responses"]) == 0:
|
86
|
+
logger.warning(
|
87
|
+
"No response from assistant %s",
|
88
|
+
assistant_id,
|
89
|
+
)
|
90
|
+
continue
|
91
|
+
|
92
|
+
if display_mode != ResponseDisplayMode.HIDDEN:
|
93
|
+
displayed[assistant_id] = tool_info["responses"]
|
94
|
+
|
95
|
+
existing_refs = {
|
96
|
+
ref.source_id: ref.sequence_number
|
97
|
+
for ref in loop_response.message.references
|
98
|
+
}
|
99
|
+
_consolidate_references_in_place(displayed, existing_refs)
|
100
|
+
|
101
|
+
modified = len(displayed) > 0
|
102
|
+
for assistant_id, messages in reversed(displayed.items()):
|
103
|
+
for i in reversed(range(len(messages))):
|
104
|
+
message = messages[i]
|
105
|
+
tool_info = self._assistant_id_to_tool_info[assistant_id]
|
106
|
+
display_mode = tool_info["display_config"].mode
|
107
|
+
display_name = tool_info["display_name"]
|
108
|
+
if len(messages) > 1:
|
109
|
+
display_name += f" {i + 1}"
|
110
|
+
|
111
|
+
loop_response.message.text = (
|
112
|
+
build_sub_agent_answer_display(
|
113
|
+
display_name=display_name,
|
114
|
+
assistant_id=assistant_id,
|
115
|
+
display_mode=display_mode,
|
116
|
+
answer=message["text"],
|
117
|
+
)
|
118
|
+
+ loop_response.message.text
|
119
|
+
)
|
120
|
+
|
121
|
+
assert self._sub_agent_message is not None
|
122
|
+
|
123
|
+
loop_response.message.references.extend(
|
124
|
+
ContentReference(
|
125
|
+
message_id=self._sub_agent_message["id"],
|
126
|
+
source_id=ref["sourceId"],
|
127
|
+
url=ref["url"],
|
128
|
+
source=ref["source"],
|
129
|
+
name=ref["name"],
|
130
|
+
sequence_number=ref["sequenceNumber"],
|
131
|
+
)
|
132
|
+
for ref in message["references"]
|
133
|
+
)
|
134
|
+
|
135
|
+
return modified
|
136
|
+
|
137
|
+
@override
|
138
|
+
async def remove_from_text(self, text) -> str:
|
139
|
+
for assistant_id, tool_info in self._assistant_id_to_tool_info.items():
|
140
|
+
display_config = tool_info["display_config"]
|
141
|
+
if display_config.remove_from_history:
|
142
|
+
text = remove_sub_agent_answer_from_text(
|
143
|
+
display_mode=display_config.mode,
|
144
|
+
text=text,
|
145
|
+
assistant_id=assistant_id,
|
146
|
+
)
|
147
|
+
return text
|
148
|
+
|
149
|
+
def notify_sub_agent_response(
|
150
|
+
self, sub_agent_assistant_id: str, response: SpaceMessage
|
151
|
+
) -> None:
|
152
|
+
if sub_agent_assistant_id not in self._assistant_id_to_tool_info:
|
153
|
+
logger.warning(
|
154
|
+
"Unknown assistant id %s received, message will be ignored.",
|
155
|
+
sub_agent_assistant_id,
|
156
|
+
)
|
157
|
+
return
|
158
|
+
|
159
|
+
self._assistant_id_to_tool_info[sub_agent_assistant_id]["responses"].append(
|
160
|
+
{
|
161
|
+
"text": response["text"],
|
162
|
+
"references": [
|
163
|
+
{
|
164
|
+
"name": ref["name"],
|
165
|
+
"url": ref["url"],
|
166
|
+
"sequenceNumber": ref["sequenceNumber"],
|
167
|
+
"originalIndex": [],
|
168
|
+
"sourceId": ref["sourceId"],
|
169
|
+
"source": ref["source"],
|
170
|
+
}
|
171
|
+
for ref in response["references"] or []
|
172
|
+
],
|
173
|
+
}
|
174
|
+
)
|
175
|
+
|
176
|
+
|
177
|
+
def _consolidate_references_in_place(
|
178
|
+
messages: dict[str, list[_SubAgentMessageInfo]], existing_refs: dict[str, int]
|
179
|
+
) -> None:
|
180
|
+
start_index = max(existing_refs.values(), default=0) + 1
|
181
|
+
|
182
|
+
for assistant_id, assistant_messages in messages.items():
|
183
|
+
for message in assistant_messages:
|
184
|
+
references = message["references"]
|
185
|
+
if len(references) == 0 or message["text"] is None:
|
186
|
+
logger.info(
|
187
|
+
"Message from assistant %s does not contain any references",
|
188
|
+
assistant_id,
|
189
|
+
)
|
190
|
+
continue
|
191
|
+
|
192
|
+
references = list(sorted(references, key=lambda ref: ref["sequenceNumber"]))
|
193
|
+
|
194
|
+
ref_map = {}
|
195
|
+
|
196
|
+
message_new_refs = []
|
197
|
+
for reference in references:
|
198
|
+
source_id = reference["sourceId"]
|
199
|
+
|
200
|
+
if source_id not in existing_refs:
|
201
|
+
message_new_refs.append(reference)
|
202
|
+
existing_refs[source_id] = start_index
|
203
|
+
start_index += 1
|
204
|
+
|
205
|
+
reference_num = existing_refs[source_id]
|
206
|
+
ref_map[reference["sequenceNumber"]] = reference_num
|
207
|
+
reference["sequenceNumber"] = reference_num
|
208
|
+
|
209
|
+
message["text"] = _replace_references_in_text(message["text"], ref_map)
|
210
|
+
message["references"] = message_new_refs
|
211
|
+
|
212
|
+
|
213
|
+
def _replace_references_in_text_non_overlapping(
|
214
|
+
text: str, ref_map: dict[int, int]
|
215
|
+
) -> str:
|
216
|
+
for orig, repl in ref_map.items():
|
217
|
+
text = re.sub(rf"<sup>{orig}</sup>", f"<sup>{repl}</sup>", text)
|
218
|
+
return text
|
219
|
+
|
220
|
+
|
221
|
+
def _replace_references_in_text(text: str, ref_map: dict[int, int]) -> str:
|
222
|
+
# 2 phase replacement, since the map keys and values can overlap
|
223
|
+
max_ref = max(max(ref_map.keys(), default=0), max(ref_map.values(), default=0)) + 1
|
224
|
+
unique_refs = range(max_ref, max_ref + len(ref_map))
|
225
|
+
|
226
|
+
text = _replace_references_in_text_non_overlapping(
|
227
|
+
text, dict(zip(ref_map.keys(), unique_refs))
|
228
|
+
)
|
229
|
+
return _replace_references_in_text_non_overlapping(
|
230
|
+
text, dict(zip(unique_refs, ref_map.values()))
|
231
|
+
)
|
@@ -0,0 +1,248 @@
|
|
1
|
+
from unique_toolkit.agentic.tools.a2a.postprocessing.postprocessor import (
|
2
|
+
_replace_references_in_text,
|
3
|
+
_replace_references_in_text_non_overlapping,
|
4
|
+
)
|
5
|
+
|
6
|
+
|
7
|
+
class TestReplaceReferencesInTextNonOverlapping:
|
8
|
+
"""Test cases for _replace_references_in_text_non_overlapping function."""
|
9
|
+
|
10
|
+
def test_single_reference_replacement(self):
|
11
|
+
"""Test replacing a single reference."""
|
12
|
+
text = "This is a test<sup>1</sup> with one reference."
|
13
|
+
ref_map = {1: 5}
|
14
|
+
result = _replace_references_in_text_non_overlapping(text, ref_map)
|
15
|
+
expected = "This is a test<sup>5</sup> with one reference."
|
16
|
+
assert result == expected
|
17
|
+
|
18
|
+
def test_multiple_reference_replacements(self):
|
19
|
+
"""Test replacing multiple references."""
|
20
|
+
text = "First<sup>1</sup> and second<sup>2</sup> and third<sup>3</sup>."
|
21
|
+
ref_map = {1: 10, 2: 20, 3: 30}
|
22
|
+
result = _replace_references_in_text_non_overlapping(text, ref_map)
|
23
|
+
expected = "First<sup>10</sup> and second<sup>20</sup> and third<sup>30</sup>."
|
24
|
+
assert result == expected
|
25
|
+
|
26
|
+
def test_no_references_in_text(self):
|
27
|
+
"""Test with text that has no references."""
|
28
|
+
text = "This text has no references at all."
|
29
|
+
ref_map = {1: 5, 2: 10}
|
30
|
+
result = _replace_references_in_text_non_overlapping(text, ref_map)
|
31
|
+
assert result == text
|
32
|
+
|
33
|
+
def test_empty_ref_map(self):
|
34
|
+
"""Test with empty reference map."""
|
35
|
+
text = "This text has<sup>1</sup> references but empty map."
|
36
|
+
ref_map = {}
|
37
|
+
result = _replace_references_in_text_non_overlapping(text, ref_map)
|
38
|
+
assert result == text
|
39
|
+
|
40
|
+
def test_empty_text(self):
|
41
|
+
"""Test with empty text."""
|
42
|
+
text = ""
|
43
|
+
ref_map = {1: 5}
|
44
|
+
result = _replace_references_in_text_non_overlapping(text, ref_map)
|
45
|
+
assert result == ""
|
46
|
+
|
47
|
+
def test_reference_not_in_map(self):
|
48
|
+
"""Test with references in text that are not in the map."""
|
49
|
+
text = "Reference<sup>1</sup> and<sup>2</sup> and<sup>3</sup>."
|
50
|
+
ref_map = {1: 10} # Only maps reference 1
|
51
|
+
result = _replace_references_in_text_non_overlapping(text, ref_map)
|
52
|
+
expected = "Reference<sup>10</sup> and<sup>2</sup> and<sup>3</sup>."
|
53
|
+
assert result == expected
|
54
|
+
|
55
|
+
def test_duplicate_references_in_text(self):
|
56
|
+
"""Test with duplicate references in text."""
|
57
|
+
text = "First<sup>1</sup> and second<sup>1</sup> occurrence."
|
58
|
+
ref_map = {1: 99}
|
59
|
+
result = _replace_references_in_text_non_overlapping(text, ref_map)
|
60
|
+
expected = "First<sup>99</sup> and second<sup>99</sup> occurrence."
|
61
|
+
assert result == expected
|
62
|
+
|
63
|
+
def test_adjacent_references(self):
|
64
|
+
"""Test with adjacent references."""
|
65
|
+
text = "Adjacent<sup>1</sup><sup>2</sup> references."
|
66
|
+
ref_map = {1: 10, 2: 20}
|
67
|
+
result = _replace_references_in_text_non_overlapping(text, ref_map)
|
68
|
+
expected = "Adjacent<sup>10</sup><sup>20</sup> references."
|
69
|
+
assert result == expected
|
70
|
+
|
71
|
+
def test_references_with_multi_digit_numbers(self):
|
72
|
+
"""Test with multi-digit reference numbers."""
|
73
|
+
text = "Reference<sup>123</sup> and<sup>456</sup>."
|
74
|
+
ref_map = {123: 789, 456: 101112}
|
75
|
+
result = _replace_references_in_text_non_overlapping(text, ref_map)
|
76
|
+
expected = "Reference<sup>789</sup> and<sup>101112</sup>."
|
77
|
+
assert result == expected
|
78
|
+
|
79
|
+
def test_references_at_text_boundaries(self):
|
80
|
+
"""Test with references at the beginning and end of text."""
|
81
|
+
text = "<sup>1</sup>Start and end<sup>2</sup>"
|
82
|
+
ref_map = {1: 100, 2: 200}
|
83
|
+
result = _replace_references_in_text_non_overlapping(text, ref_map)
|
84
|
+
expected = "<sup>100</sup>Start and end<sup>200</sup>"
|
85
|
+
assert result == expected
|
86
|
+
|
87
|
+
def test_malformed_references_ignored(self):
|
88
|
+
"""Test that malformed references are ignored."""
|
89
|
+
text = "Good<sup>1</sup> and bad<sup>abc</sup> and<sup></sup>."
|
90
|
+
ref_map = {1: 10}
|
91
|
+
result = _replace_references_in_text_non_overlapping(text, ref_map)
|
92
|
+
expected = "Good<sup>10</sup> and bad<sup>abc</sup> and<sup></sup>."
|
93
|
+
assert result == expected
|
94
|
+
|
95
|
+
def test_zero_reference_number(self):
|
96
|
+
"""Test with zero as reference number."""
|
97
|
+
text = "Zero reference<sup>0</sup> here."
|
98
|
+
ref_map = {0: 100}
|
99
|
+
result = _replace_references_in_text_non_overlapping(text, ref_map)
|
100
|
+
expected = "Zero reference<sup>100</sup> here."
|
101
|
+
assert result == expected
|
102
|
+
|
103
|
+
def test_negative_reference_numbers(self):
|
104
|
+
"""Test with negative reference numbers (edge case)."""
|
105
|
+
text = "Negative<sup>-1</sup> reference."
|
106
|
+
ref_map = {-1: 5}
|
107
|
+
result = _replace_references_in_text_non_overlapping(text, ref_map)
|
108
|
+
expected = "Negative<sup>5</sup> reference."
|
109
|
+
assert result == expected
|
110
|
+
|
111
|
+
|
112
|
+
class TestReplaceReferencesInText:
|
113
|
+
"""Test cases for _replace_references_in_text function."""
|
114
|
+
|
115
|
+
def test_non_overlapping_simple_case(self):
|
116
|
+
"""Test simple non-overlapping case."""
|
117
|
+
text = "Reference<sup>1</sup> and<sup>2</sup>."
|
118
|
+
ref_map = {1: 10, 2: 20}
|
119
|
+
result = _replace_references_in_text(text, ref_map)
|
120
|
+
expected = "Reference<sup>10</sup> and<sup>20</sup>."
|
121
|
+
assert result == expected
|
122
|
+
|
123
|
+
def test_overlapping_references_case1(self):
|
124
|
+
"""Test overlapping case where new reference numbers conflict with existing ones."""
|
125
|
+
text = "First<sup>1</sup> and second<sup>2</sup>."
|
126
|
+
ref_map = {1: 2, 2: 1} # Swap references
|
127
|
+
result = _replace_references_in_text(text, ref_map)
|
128
|
+
expected = "First<sup>2</sup> and second<sup>1</sup>."
|
129
|
+
assert result == expected
|
130
|
+
|
131
|
+
def test_overlapping_references_case2(self):
|
132
|
+
"""Test overlapping case with chain of replacements."""
|
133
|
+
text = "Refs<sup>1</sup><sup>2</sup><sup>3</sup>."
|
134
|
+
ref_map = {1: 2, 2: 3, 3: 1} # Circular replacement
|
135
|
+
result = _replace_references_in_text(text, ref_map)
|
136
|
+
expected = "Refs<sup>2</sup><sup>3</sup><sup>1</sup>."
|
137
|
+
assert result == expected
|
138
|
+
|
139
|
+
def test_overlapping_with_higher_numbers(self):
|
140
|
+
"""Test overlapping where replacement numbers are higher than originals."""
|
141
|
+
text = "Test<sup>1</sup> and<sup>2</sup>."
|
142
|
+
ref_map = {1: 3, 2: 1} # 2 -> 1, but 1 -> 3
|
143
|
+
result = _replace_references_in_text(text, ref_map)
|
144
|
+
expected = "Test<sup>3</sup> and<sup>1</sup>."
|
145
|
+
assert result == expected
|
146
|
+
|
147
|
+
def test_complex_overlapping_scenario(self):
|
148
|
+
"""Test complex overlapping scenario with multiple conflicts."""
|
149
|
+
text = "A<sup>1</sup>B<sup>2</sup>C<sup>3</sup>D<sup>4</sup>."
|
150
|
+
ref_map = {1: 4, 2: 1, 3: 2, 4: 3} # Full rotation
|
151
|
+
result = _replace_references_in_text(text, ref_map)
|
152
|
+
expected = "A<sup>4</sup>B<sup>1</sup>C<sup>2</sup>D<sup>3</sup>."
|
153
|
+
assert result == expected
|
154
|
+
|
155
|
+
def test_empty_ref_map(self):
|
156
|
+
"""Test with empty reference map."""
|
157
|
+
text = "Text with<sup>1</sup> references."
|
158
|
+
ref_map = {}
|
159
|
+
result = _replace_references_in_text(text, ref_map)
|
160
|
+
assert result == text
|
161
|
+
|
162
|
+
def test_empty_text(self):
|
163
|
+
"""Test with empty text."""
|
164
|
+
text = ""
|
165
|
+
ref_map = {1: 2}
|
166
|
+
result = _replace_references_in_text(text, ref_map)
|
167
|
+
assert result == ""
|
168
|
+
|
169
|
+
def test_no_references_in_text(self):
|
170
|
+
"""Test with text containing no references."""
|
171
|
+
text = "This text has no references."
|
172
|
+
ref_map = {1: 10, 2: 20}
|
173
|
+
result = _replace_references_in_text(text, ref_map)
|
174
|
+
assert result == text
|
175
|
+
|
176
|
+
def test_single_reference_no_overlap(self):
|
177
|
+
"""Test single reference with no overlap potential."""
|
178
|
+
text = "Single<sup>5</sup> reference."
|
179
|
+
ref_map = {5: 100}
|
180
|
+
result = _replace_references_in_text(text, ref_map)
|
181
|
+
expected = "Single<sup>100</sup> reference."
|
182
|
+
assert result == expected
|
183
|
+
|
184
|
+
def test_partial_overlap(self):
|
185
|
+
"""Test case where only some references have overlapping numbers."""
|
186
|
+
text = "Mix<sup>1</sup><sup>2</sup><sup>10</sup>."
|
187
|
+
ref_map = {1: 2, 2: 20, 10: 100} # Only 1->2 creates potential overlap
|
188
|
+
result = _replace_references_in_text(text, ref_map)
|
189
|
+
expected = "Mix<sup>2</sup><sup>20</sup><sup>100</sup>."
|
190
|
+
assert result == expected
|
191
|
+
|
192
|
+
def test_self_mapping(self):
|
193
|
+
"""Test case where a reference maps to itself."""
|
194
|
+
text = "Self<sup>1</sup> and other<sup>2</sup>."
|
195
|
+
ref_map = {1: 1, 2: 10} # 1 maps to itself
|
196
|
+
result = _replace_references_in_text(text, ref_map)
|
197
|
+
expected = "Self<sup>1</sup> and other<sup>10</sup>."
|
198
|
+
assert result == expected
|
199
|
+
|
200
|
+
def test_duplicate_references_with_overlap(self):
|
201
|
+
"""Test duplicate references in text with overlapping mappings."""
|
202
|
+
text = "Dup<sup>1</sup> and dup<sup>1</sup> and<sup>2</sup>."
|
203
|
+
ref_map = {1: 2, 2: 1} # Swap
|
204
|
+
result = _replace_references_in_text(text, ref_map)
|
205
|
+
expected = "Dup<sup>2</sup> and dup<sup>2</sup> and<sup>1</sup>."
|
206
|
+
assert result == expected
|
207
|
+
|
208
|
+
def test_large_reference_numbers(self):
|
209
|
+
"""Test with large reference numbers."""
|
210
|
+
text = "Large<sup>999</sup> and<sup>1000</sup>."
|
211
|
+
ref_map = {999: 1000, 1000: 999} # Swap large numbers
|
212
|
+
result = _replace_references_in_text(text, ref_map)
|
213
|
+
expected = "Large<sup>1000</sup> and<sup>999</sup>."
|
214
|
+
assert result == expected
|
215
|
+
|
216
|
+
def test_zero_and_negative_with_overlap(self):
|
217
|
+
"""Test zero and negative numbers with potential overlap."""
|
218
|
+
text = "Zero<sup>0</sup> and neg<sup>-1</sup> and pos<sup>1</sup>."
|
219
|
+
ref_map = {0: 1, -1: 0, 1: -1} # Circular with zero and negative
|
220
|
+
result = _replace_references_in_text(text, ref_map)
|
221
|
+
expected = "Zero<sup>1</sup> and neg<sup>0</sup> and pos<sup>-1</sup>."
|
222
|
+
assert result == expected
|
223
|
+
|
224
|
+
def test_max_ref_calculation_edge_case(self):
|
225
|
+
"""Test edge case for max_ref calculation with empty map."""
|
226
|
+
text = "Some<sup>1</sup> text."
|
227
|
+
ref_map = {}
|
228
|
+
result = _replace_references_in_text(text, ref_map)
|
229
|
+
assert result == text
|
230
|
+
|
231
|
+
def test_phase_separation_correctness(self):
|
232
|
+
"""Test that the two-phase approach correctly handles complex overlaps."""
|
233
|
+
# This test ensures the intermediate unique references don't interfere
|
234
|
+
text = "Test<sup>1</sup><sup>2</sup><sup>3</sup><sup>4</sup><sup>5</sup>."
|
235
|
+
ref_map = {1: 5, 2: 4, 3: 3, 4: 2, 5: 1} # Reverse order
|
236
|
+
result = _replace_references_in_text(text, ref_map)
|
237
|
+
expected = "Test<sup>5</sup><sup>4</sup><sup>3</sup><sup>2</sup><sup>1</sup>."
|
238
|
+
assert result == expected
|
239
|
+
|
240
|
+
def test_intermediate_collision_avoidance(self):
|
241
|
+
"""Test that intermediate unique references don't collide with existing text."""
|
242
|
+
# Create a scenario where intermediate refs might collide
|
243
|
+
text = "Refs<sup>1</sup><sup>2</sup> and existing<sup>6</sup><sup>7</sup>."
|
244
|
+
ref_map = {1: 2, 2: 1} # Simple swap
|
245
|
+
result = _replace_references_in_text(text, ref_map)
|
246
|
+
expected = "Refs<sup>2</sup><sup>1</sup> and existing<sup>6</sup><sup>7</sup>."
|
247
|
+
assert result == expected
|
248
|
+
# The existing <sup>6</sup> and <sup>7</sup> should remain unchanged
|