unique_toolkit 0.8.29__tar.gz → 0.8.31__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.
Files changed (128) hide show
  1. {unique_toolkit-0.8.29 → unique_toolkit-0.8.31}/CHANGELOG.md +19 -0
  2. {unique_toolkit-0.8.29 → unique_toolkit-0.8.31}/PKG-INFO +20 -1
  3. {unique_toolkit-0.8.29 → unique_toolkit-0.8.31}/pyproject.toml +1 -1
  4. {unique_toolkit-0.8.29 → unique_toolkit-0.8.31}/unique_toolkit/language_model/infos.py +156 -0
  5. unique_toolkit-0.8.31/unique_toolkit/tools/a2a/__init__.py +4 -0
  6. unique_toolkit-0.8.31/unique_toolkit/tools/a2a/config.py +27 -0
  7. unique_toolkit-0.8.31/unique_toolkit/tools/a2a/manager.py +49 -0
  8. unique_toolkit-0.8.31/unique_toolkit/tools/a2a/memory.py +26 -0
  9. unique_toolkit-0.8.31/unique_toolkit/tools/a2a/schema.py +15 -0
  10. unique_toolkit-0.8.31/unique_toolkit/tools/a2a/service.py +152 -0
  11. {unique_toolkit-0.8.29 → unique_toolkit-0.8.31}/unique_toolkit/tools/config.py +1 -0
  12. {unique_toolkit-0.8.29 → unique_toolkit-0.8.31}/unique_toolkit/tools/test/test_mcp_manager.py +46 -1
  13. {unique_toolkit-0.8.29 → unique_toolkit-0.8.31}/unique_toolkit/tools/tool_manager.py +8 -1
  14. {unique_toolkit-0.8.29 → unique_toolkit-0.8.31}/LICENSE +0 -0
  15. {unique_toolkit-0.8.29 → unique_toolkit-0.8.31}/README.md +0 -0
  16. {unique_toolkit-0.8.29 → unique_toolkit-0.8.31}/unique_toolkit/__init__.py +0 -0
  17. {unique_toolkit-0.8.29 → unique_toolkit-0.8.31}/unique_toolkit/_common/_base_service.py +0 -0
  18. {unique_toolkit-0.8.29 → unique_toolkit-0.8.31}/unique_toolkit/_common/_time_utils.py +0 -0
  19. {unique_toolkit-0.8.29 → unique_toolkit-0.8.31}/unique_toolkit/_common/default_language_model.py +0 -0
  20. {unique_toolkit-0.8.29 → unique_toolkit-0.8.31}/unique_toolkit/_common/endpoint_builder.py +0 -0
  21. {unique_toolkit-0.8.29 → unique_toolkit-0.8.31}/unique_toolkit/_common/exception.py +0 -0
  22. {unique_toolkit-0.8.29 → unique_toolkit-0.8.31}/unique_toolkit/_common/token/image_token_counting.py +0 -0
  23. {unique_toolkit-0.8.29 → unique_toolkit-0.8.31}/unique_toolkit/_common/token/token_counting.py +0 -0
  24. {unique_toolkit-0.8.29 → unique_toolkit-0.8.31}/unique_toolkit/_common/validate_required_values.py +0 -0
  25. {unique_toolkit-0.8.29 → unique_toolkit-0.8.31}/unique_toolkit/_common/validators.py +0 -0
  26. {unique_toolkit-0.8.29 → unique_toolkit-0.8.31}/unique_toolkit/app/__init__.py +0 -0
  27. {unique_toolkit-0.8.29 → unique_toolkit-0.8.31}/unique_toolkit/app/dev_util.py +0 -0
  28. {unique_toolkit-0.8.29 → unique_toolkit-0.8.31}/unique_toolkit/app/init_logging.py +0 -0
  29. {unique_toolkit-0.8.29 → unique_toolkit-0.8.31}/unique_toolkit/app/init_sdk.py +0 -0
  30. {unique_toolkit-0.8.29 → unique_toolkit-0.8.31}/unique_toolkit/app/performance/async_tasks.py +0 -0
  31. {unique_toolkit-0.8.29 → unique_toolkit-0.8.31}/unique_toolkit/app/performance/async_wrapper.py +0 -0
  32. {unique_toolkit-0.8.29 → unique_toolkit-0.8.31}/unique_toolkit/app/schemas.py +0 -0
  33. {unique_toolkit-0.8.29 → unique_toolkit-0.8.31}/unique_toolkit/app/unique_settings.py +0 -0
  34. {unique_toolkit-0.8.29 → unique_toolkit-0.8.31}/unique_toolkit/app/verification.py +0 -0
  35. {unique_toolkit-0.8.29 → unique_toolkit-0.8.31}/unique_toolkit/chat/__init__.py +0 -0
  36. {unique_toolkit-0.8.29 → unique_toolkit-0.8.31}/unique_toolkit/chat/constants.py +0 -0
  37. {unique_toolkit-0.8.29 → unique_toolkit-0.8.31}/unique_toolkit/chat/functions.py +0 -0
  38. {unique_toolkit-0.8.29 → unique_toolkit-0.8.31}/unique_toolkit/chat/schemas.py +0 -0
  39. {unique_toolkit-0.8.29 → unique_toolkit-0.8.31}/unique_toolkit/chat/service.py +0 -0
  40. {unique_toolkit-0.8.29 → unique_toolkit-0.8.31}/unique_toolkit/chat/state.py +0 -0
  41. {unique_toolkit-0.8.29 → unique_toolkit-0.8.31}/unique_toolkit/chat/utils.py +0 -0
  42. {unique_toolkit-0.8.29 → unique_toolkit-0.8.31}/unique_toolkit/content/__init__.py +0 -0
  43. {unique_toolkit-0.8.29 → unique_toolkit-0.8.31}/unique_toolkit/content/constants.py +0 -0
  44. {unique_toolkit-0.8.29 → unique_toolkit-0.8.31}/unique_toolkit/content/functions.py +0 -0
  45. {unique_toolkit-0.8.29 → unique_toolkit-0.8.31}/unique_toolkit/content/schemas.py +0 -0
  46. {unique_toolkit-0.8.29 → unique_toolkit-0.8.31}/unique_toolkit/content/service.py +0 -0
  47. {unique_toolkit-0.8.29 → unique_toolkit-0.8.31}/unique_toolkit/content/utils.py +0 -0
  48. {unique_toolkit-0.8.29 → unique_toolkit-0.8.31}/unique_toolkit/debug_info_manager/debug_info_manager.py +0 -0
  49. {unique_toolkit-0.8.29 → unique_toolkit-0.8.31}/unique_toolkit/embedding/__init__.py +0 -0
  50. {unique_toolkit-0.8.29 → unique_toolkit-0.8.31}/unique_toolkit/embedding/constants.py +0 -0
  51. {unique_toolkit-0.8.29 → unique_toolkit-0.8.31}/unique_toolkit/embedding/functions.py +0 -0
  52. {unique_toolkit-0.8.29 → unique_toolkit-0.8.31}/unique_toolkit/embedding/schemas.py +0 -0
  53. {unique_toolkit-0.8.29 → unique_toolkit-0.8.31}/unique_toolkit/embedding/service.py +0 -0
  54. {unique_toolkit-0.8.29 → unique_toolkit-0.8.31}/unique_toolkit/embedding/utils.py +0 -0
  55. {unique_toolkit-0.8.29 → unique_toolkit-0.8.31}/unique_toolkit/evals/config.py +0 -0
  56. {unique_toolkit-0.8.29 → unique_toolkit-0.8.31}/unique_toolkit/evals/context_relevancy/prompts.py +0 -0
  57. {unique_toolkit-0.8.29 → unique_toolkit-0.8.31}/unique_toolkit/evals/context_relevancy/schema.py +0 -0
  58. {unique_toolkit-0.8.29 → unique_toolkit-0.8.31}/unique_toolkit/evals/context_relevancy/service.py +0 -0
  59. {unique_toolkit-0.8.29 → unique_toolkit-0.8.31}/unique_toolkit/evals/evaluation_manager.py +0 -0
  60. {unique_toolkit-0.8.29 → unique_toolkit-0.8.31}/unique_toolkit/evals/exception.py +0 -0
  61. {unique_toolkit-0.8.29 → unique_toolkit-0.8.31}/unique_toolkit/evals/hallucination/constants.py +0 -0
  62. {unique_toolkit-0.8.29 → unique_toolkit-0.8.31}/unique_toolkit/evals/hallucination/hallucination_evaluation.py +0 -0
  63. {unique_toolkit-0.8.29 → unique_toolkit-0.8.31}/unique_toolkit/evals/hallucination/prompts.py +0 -0
  64. {unique_toolkit-0.8.29 → unique_toolkit-0.8.31}/unique_toolkit/evals/hallucination/service.py +0 -0
  65. {unique_toolkit-0.8.29 → unique_toolkit-0.8.31}/unique_toolkit/evals/hallucination/utils.py +0 -0
  66. {unique_toolkit-0.8.29 → unique_toolkit-0.8.31}/unique_toolkit/evals/output_parser.py +0 -0
  67. {unique_toolkit-0.8.29 → unique_toolkit-0.8.31}/unique_toolkit/evals/schemas.py +0 -0
  68. {unique_toolkit-0.8.29 → unique_toolkit-0.8.31}/unique_toolkit/evals/tests/test_context_relevancy_service.py +0 -0
  69. {unique_toolkit-0.8.29 → unique_toolkit-0.8.31}/unique_toolkit/evals/tests/test_output_parser.py +0 -0
  70. {unique_toolkit-0.8.29 → unique_toolkit-0.8.31}/unique_toolkit/evaluators/__init__.py +0 -0
  71. {unique_toolkit-0.8.29 → unique_toolkit-0.8.31}/unique_toolkit/evaluators/config.py +0 -0
  72. {unique_toolkit-0.8.29 → unique_toolkit-0.8.31}/unique_toolkit/evaluators/constants.py +0 -0
  73. {unique_toolkit-0.8.29 → unique_toolkit-0.8.31}/unique_toolkit/evaluators/context_relevancy/constants.py +0 -0
  74. {unique_toolkit-0.8.29 → unique_toolkit-0.8.31}/unique_toolkit/evaluators/context_relevancy/prompts.py +0 -0
  75. {unique_toolkit-0.8.29 → unique_toolkit-0.8.31}/unique_toolkit/evaluators/context_relevancy/service.py +0 -0
  76. {unique_toolkit-0.8.29 → unique_toolkit-0.8.31}/unique_toolkit/evaluators/context_relevancy/utils.py +0 -0
  77. {unique_toolkit-0.8.29 → unique_toolkit-0.8.31}/unique_toolkit/evaluators/exception.py +0 -0
  78. {unique_toolkit-0.8.29 → unique_toolkit-0.8.31}/unique_toolkit/evaluators/hallucination/constants.py +0 -0
  79. {unique_toolkit-0.8.29 → unique_toolkit-0.8.31}/unique_toolkit/evaluators/hallucination/prompts.py +0 -0
  80. {unique_toolkit-0.8.29 → unique_toolkit-0.8.31}/unique_toolkit/evaluators/hallucination/service.py +0 -0
  81. {unique_toolkit-0.8.29 → unique_toolkit-0.8.31}/unique_toolkit/evaluators/hallucination/utils.py +0 -0
  82. {unique_toolkit-0.8.29 → unique_toolkit-0.8.31}/unique_toolkit/evaluators/output_parser.py +0 -0
  83. {unique_toolkit-0.8.29 → unique_toolkit-0.8.31}/unique_toolkit/evaluators/schemas.py +0 -0
  84. {unique_toolkit-0.8.29 → unique_toolkit-0.8.31}/unique_toolkit/framework_utilities/__init__.py +0 -0
  85. {unique_toolkit-0.8.29 → unique_toolkit-0.8.31}/unique_toolkit/framework_utilities/langchain/client.py +0 -0
  86. {unique_toolkit-0.8.29 → unique_toolkit-0.8.31}/unique_toolkit/framework_utilities/langchain/history.py +0 -0
  87. {unique_toolkit-0.8.29 → unique_toolkit-0.8.31}/unique_toolkit/framework_utilities/openai/__init__.py +0 -0
  88. {unique_toolkit-0.8.29 → unique_toolkit-0.8.31}/unique_toolkit/framework_utilities/openai/client.py +0 -0
  89. {unique_toolkit-0.8.29 → unique_toolkit-0.8.31}/unique_toolkit/framework_utilities/openai/message_builder.py +0 -0
  90. {unique_toolkit-0.8.29 → unique_toolkit-0.8.31}/unique_toolkit/framework_utilities/utils.py +0 -0
  91. {unique_toolkit-0.8.29 → unique_toolkit-0.8.31}/unique_toolkit/history_manager/history_construction_with_contents.py +0 -0
  92. {unique_toolkit-0.8.29 → unique_toolkit-0.8.31}/unique_toolkit/history_manager/history_manager.py +0 -0
  93. {unique_toolkit-0.8.29 → unique_toolkit-0.8.31}/unique_toolkit/history_manager/loop_token_reducer.py +0 -0
  94. {unique_toolkit-0.8.29 → unique_toolkit-0.8.31}/unique_toolkit/history_manager/utils.py +0 -0
  95. {unique_toolkit-0.8.29 → unique_toolkit-0.8.31}/unique_toolkit/language_model/__init__.py +0 -0
  96. {unique_toolkit-0.8.29 → unique_toolkit-0.8.31}/unique_toolkit/language_model/builder.py +0 -0
  97. {unique_toolkit-0.8.29 → unique_toolkit-0.8.31}/unique_toolkit/language_model/constants.py +0 -0
  98. {unique_toolkit-0.8.29 → unique_toolkit-0.8.31}/unique_toolkit/language_model/functions.py +0 -0
  99. {unique_toolkit-0.8.29 → unique_toolkit-0.8.31}/unique_toolkit/language_model/prompt.py +0 -0
  100. {unique_toolkit-0.8.29 → unique_toolkit-0.8.31}/unique_toolkit/language_model/reference.py +0 -0
  101. {unique_toolkit-0.8.29 → unique_toolkit-0.8.31}/unique_toolkit/language_model/schemas.py +0 -0
  102. {unique_toolkit-0.8.29 → unique_toolkit-0.8.31}/unique_toolkit/language_model/service.py +0 -0
  103. {unique_toolkit-0.8.29 → unique_toolkit-0.8.31}/unique_toolkit/language_model/utils.py +0 -0
  104. {unique_toolkit-0.8.29 → unique_toolkit-0.8.31}/unique_toolkit/postprocessor/postprocessor_manager.py +0 -0
  105. {unique_toolkit-0.8.29 → unique_toolkit-0.8.31}/unique_toolkit/protocols/support.py +0 -0
  106. {unique_toolkit-0.8.29 → unique_toolkit-0.8.31}/unique_toolkit/reference_manager/reference_manager.py +0 -0
  107. {unique_toolkit-0.8.29 → unique_toolkit-0.8.31}/unique_toolkit/short_term_memory/__init__.py +0 -0
  108. {unique_toolkit-0.8.29 → unique_toolkit-0.8.31}/unique_toolkit/short_term_memory/constants.py +0 -0
  109. {unique_toolkit-0.8.29 → unique_toolkit-0.8.31}/unique_toolkit/short_term_memory/functions.py +0 -0
  110. {unique_toolkit-0.8.29 → unique_toolkit-0.8.31}/unique_toolkit/short_term_memory/persistent_short_term_memory_manager.py +0 -0
  111. {unique_toolkit-0.8.29 → unique_toolkit-0.8.31}/unique_toolkit/short_term_memory/schemas.py +0 -0
  112. {unique_toolkit-0.8.29 → unique_toolkit-0.8.31}/unique_toolkit/short_term_memory/service.py +0 -0
  113. {unique_toolkit-0.8.29 → unique_toolkit-0.8.31}/unique_toolkit/smart_rules/__init__.py +0 -0
  114. {unique_toolkit-0.8.29 → unique_toolkit-0.8.31}/unique_toolkit/smart_rules/compile.py +0 -0
  115. {unique_toolkit-0.8.29 → unique_toolkit-0.8.31}/unique_toolkit/thinking_manager/thinking_manager.py +0 -0
  116. {unique_toolkit-0.8.29 → unique_toolkit-0.8.31}/unique_toolkit/tools/factory.py +0 -0
  117. {unique_toolkit-0.8.29 → unique_toolkit-0.8.31}/unique_toolkit/tools/mcp/__init__.py +0 -0
  118. {unique_toolkit-0.8.29 → unique_toolkit-0.8.31}/unique_toolkit/tools/mcp/manager.py +0 -0
  119. {unique_toolkit-0.8.29 → unique_toolkit-0.8.31}/unique_toolkit/tools/mcp/models.py +0 -0
  120. {unique_toolkit-0.8.29 → unique_toolkit-0.8.31}/unique_toolkit/tools/mcp/tool_wrapper.py +0 -0
  121. {unique_toolkit-0.8.29 → unique_toolkit-0.8.31}/unique_toolkit/tools/schemas.py +0 -0
  122. {unique_toolkit-0.8.29 → unique_toolkit-0.8.31}/unique_toolkit/tools/test/test_tool_progress_reporter.py +0 -0
  123. {unique_toolkit-0.8.29 → unique_toolkit-0.8.31}/unique_toolkit/tools/tool.py +0 -0
  124. {unique_toolkit-0.8.29 → unique_toolkit-0.8.31}/unique_toolkit/tools/tool_progress_reporter.py +0 -0
  125. {unique_toolkit-0.8.29 → unique_toolkit-0.8.31}/unique_toolkit/tools/utils/execution/execution.py +0 -0
  126. {unique_toolkit-0.8.29 → unique_toolkit-0.8.31}/unique_toolkit/tools/utils/source_handling/schema.py +0 -0
  127. {unique_toolkit-0.8.29 → unique_toolkit-0.8.31}/unique_toolkit/tools/utils/source_handling/source_formatting.py +0 -0
  128. {unique_toolkit-0.8.29 → unique_toolkit-0.8.31}/unique_toolkit/tools/utils/source_handling/tests/test_source_formatting.py +0 -0
@@ -4,6 +4,22 @@ All notable changes to this project will be documented in this file.
4
4
 
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
+
8
+
9
+ ## [0.8.31] - 2025-08-29
10
+ - Add various openai models to supported model list
11
+ - o1
12
+ - o3
13
+ - o3-deep-research
14
+ - o3-pro
15
+ - o4-mini
16
+ - o4-mini-deep-research
17
+ - gpt-4-1-mini
18
+ - gpt-4-1-nano
19
+
20
+ ## [0.8.30] - 2025-08-28
21
+ - Added A2A manager
22
+
7
23
  ## [0.8.29] - 2025-08-27
8
24
  - Include `MessageExecution` and `MessageLog` in toolkit
9
25
 
@@ -16,6 +32,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
16
32
  ## [0.8.26] - 2025-08-27
17
33
  - Optimized MCP manager
18
34
 
35
+ ## [0.8.26] - 2025-08-27
36
+ - Optimized MCP manager
37
+
19
38
  ## [0.8.25] - 2025-08-27
20
39
  - Load environment variables automatically from plattform dirs or environment
21
40
  - General Endpoint definition utility
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: unique_toolkit
3
- Version: 0.8.29
3
+ Version: 0.8.31
4
4
  Summary:
5
5
  License: Proprietary
6
6
  Author: Martin Fadler
@@ -114,6 +114,22 @@ All notable changes to this project will be documented in this file.
114
114
 
115
115
  The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
116
116
  and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
117
+
118
+
119
+ ## [0.8.31] - 2025-08-29
120
+ - Add various openai models to supported model list
121
+ - o1
122
+ - o3
123
+ - o3-deep-research
124
+ - o3-pro
125
+ - o4-mini
126
+ - o4-mini-deep-research
127
+ - gpt-4-1-mini
128
+ - gpt-4-1-nano
129
+
130
+ ## [0.8.30] - 2025-08-28
131
+ - Added A2A manager
132
+
117
133
  ## [0.8.29] - 2025-08-27
118
134
  - Include `MessageExecution` and `MessageLog` in toolkit
119
135
 
@@ -126,6 +142,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
126
142
  ## [0.8.26] - 2025-08-27
127
143
  - Optimized MCP manager
128
144
 
145
+ ## [0.8.26] - 2025-08-27
146
+ - Optimized MCP manager
147
+
129
148
  ## [0.8.25] - 2025-08-27
130
149
  - Load environment variables automatically from plattform dirs or environment
131
150
  - General Endpoint definition utility
@@ -1,6 +1,6 @@
1
1
  [tool.poetry]
2
2
  name = "unique_toolkit"
3
- version = "0.8.29"
3
+ version = "0.8.31"
4
4
  description = ""
5
5
  authors = [
6
6
  "Martin Fadler <martin.fadler@unique.ch>",
@@ -48,6 +48,14 @@ class LanguageModelName(StrEnum):
48
48
  LITELLM_OPENAI_GPT_5_MINI = "litellm:openai-gpt-5-mini"
49
49
  LITELLM_OPENAI_GPT_5_NANO = "litellm:openai-gpt-5-nano"
50
50
  LITELLM_OPENAI_GPT_5_CHAT = "litellm:openai-gpt-5-chat"
51
+ LITELLM_OPENAI_O1 = "litellm:openai-o1"
52
+ LITELLM_OPENAI_O3 = "litellm:openai-o3"
53
+ LITELLM_OPENAI_O3_DEEP_RESEARCH = "litellm:openai-o3-deep-research"
54
+ LITELLM_OPENAI_O3_PRO = "litellm:openai-o3-pro"
55
+ LITELLM_OPENAI_O4_MINI = "litellm:openai-o4-mini"
56
+ LITELLM_OPENAI_O4_MINI_DEEP_RESEARCH = "litellm:openai-o4-mini-deep-research"
57
+ LITELLM_OPENAI_GPT_4_1_MINI = "litellm:openai-gpt-4-1-mini"
58
+ LITELLM_OPENAI_GPT_4_1_NANO = "litellm:openai-gpt-4-1-nano"
51
59
  LITELLM_DEEPSEEK_R1 = "litellm:deepseek-r1"
52
60
  LITELLM_DEEPSEEK_V3 = "litellm:deepseek-v3-1"
53
61
  LITELLM_QWEN_3 = "litellm:qwen-3-235B-A22B"
@@ -83,6 +91,14 @@ def get_encoder_name(model_name: LanguageModelName) -> EncoderName:
83
91
  | LMN.LITELLM_OPENAI_GPT_5_MINI
84
92
  | LMN.LITELLM_OPENAI_GPT_5_NANO
85
93
  | LMN.LITELLM_OPENAI_GPT_5_CHAT
94
+ | LMN.LITELLM_OPENAI_O1
95
+ | LMN.LITELLM_OPENAI_O3
96
+ | LMN.LITELLM_OPENAI_O3_DEEP_RESEARCH
97
+ | LMN.LITELLM_OPENAI_O4_MINI
98
+ | LMN.LITELLM_OPENAI_O4_MINI_DEEP_RESEARCH
99
+ | LMN.LITELLM_OPENAI_GPT_4_1_MINI
100
+ | LMN.LITELLM_OPENAI_GPT_4_1_NANO
101
+ | LMN.LITELLM_OPENAI_O3_PRO
86
102
  ):
87
103
  return EncoderName.O200K_BASE
88
104
  case _:
@@ -879,6 +895,146 @@ class LanguageModelInfo(BaseModel):
879
895
  deprecated_at=date(2026, 8, 7),
880
896
  retirement_at=date(2026, 8, 7),
881
897
  )
898
+ case LanguageModelName.LITELLM_OPENAI_O1:
899
+ return cls(
900
+ name=model_name,
901
+ provider=LanguageModelProvider.LITELLM,
902
+ version="2024-12-17",
903
+ encoder_name=EncoderName.O200K_BASE,
904
+ capabilities=[
905
+ ModelCapabilities.STRUCTURED_OUTPUT,
906
+ ModelCapabilities.FUNCTION_CALLING,
907
+ ModelCapabilities.STREAMING,
908
+ ModelCapabilities.VISION,
909
+ ModelCapabilities.REASONING,
910
+ ],
911
+ token_limits=LanguageModelTokenLimits(
912
+ token_limit_input=200_000, token_limit_output=100_000
913
+ ),
914
+ info_cutoff_at=date(2023, 10, 1),
915
+ published_at=date(2024, 12, 17),
916
+ temperature_bounds=TemperatureBounds(
917
+ min_temperature=1.0, max_temperature=1.0
918
+ ),
919
+ )
920
+ case LanguageModelName.LITELLM_OPENAI_O3:
921
+ return cls(
922
+ name=model_name,
923
+ provider=LanguageModelProvider.LITELLM,
924
+ version="2025-04-16",
925
+ encoder_name=EncoderName.O200K_BASE,
926
+ capabilities=[
927
+ ModelCapabilities.FUNCTION_CALLING,
928
+ ModelCapabilities.STRUCTURED_OUTPUT,
929
+ ModelCapabilities.STREAMING,
930
+ ModelCapabilities.REASONING,
931
+ ],
932
+ token_limits=LanguageModelTokenLimits(
933
+ token_limit_input=200_000, token_limit_output=100_000
934
+ ),
935
+ temperature_bounds=TemperatureBounds(
936
+ min_temperature=1.0, max_temperature=1.0
937
+ ),
938
+ published_at=date(2025, 4, 16),
939
+ info_cutoff_at=date(2024, 6, 1),
940
+ )
941
+ case LanguageModelName.LITELLM_OPENAI_O3_DEEP_RESEARCH:
942
+ return cls(
943
+ name=model_name,
944
+ provider=LanguageModelProvider.LITELLM,
945
+ version="2025-06-26",
946
+ encoder_name=EncoderName.O200K_BASE,
947
+ token_limits=LanguageModelTokenLimits(
948
+ token_limit_input=200_000, token_limit_output=100_000
949
+ ),
950
+ published_at=date(2025, 4, 16),
951
+ capabilities=[ModelCapabilities.STREAMING],
952
+ info_cutoff_at=date(2024, 6, 1),
953
+ )
954
+ case LanguageModelName.LITELLM_OPENAI_O3_PRO:
955
+ return cls(
956
+ name=model_name,
957
+ provider=LanguageModelProvider.LITELLM,
958
+ version="2025-06-10",
959
+ encoder_name=EncoderName.O200K_BASE,
960
+ capabilities=[
961
+ ModelCapabilities.FUNCTION_CALLING,
962
+ ModelCapabilities.REASONING,
963
+ ModelCapabilities.STRUCTURED_OUTPUT,
964
+ ],
965
+ token_limits=LanguageModelTokenLimits(
966
+ token_limit_input=200_000, token_limit_output=100_000
967
+ ),
968
+ published_at=date(2025, 6, 10),
969
+ info_cutoff_at=date(2024, 6, 1),
970
+ )
971
+ case LanguageModelName.LITELLM_OPENAI_O4_MINI:
972
+ return cls(
973
+ name=model_name,
974
+ provider=LanguageModelProvider.LITELLM,
975
+ version="2025-04-16",
976
+ encoder_name=EncoderName.O200K_BASE,
977
+ capabilities=[
978
+ ModelCapabilities.FUNCTION_CALLING,
979
+ ModelCapabilities.STREAMING,
980
+ ModelCapabilities.STRUCTURED_OUTPUT,
981
+ ],
982
+ token_limits=LanguageModelTokenLimits(
983
+ token_limit_input=200_000, token_limit_output=100_000
984
+ ),
985
+ published_at=date(2025, 4, 16),
986
+ info_cutoff_at=date(2024, 6, 1),
987
+ temperature_bounds=TemperatureBounds(
988
+ min_temperature=1.0, max_temperature=1.0
989
+ ),
990
+ )
991
+ case LanguageModelName.LITELLM_OPENAI_O4_MINI_DEEP_RESEARCH:
992
+ return cls(
993
+ name=model_name,
994
+ provider=LanguageModelProvider.LITELLM,
995
+ version="2025-06-26",
996
+ encoder_name=EncoderName.O200K_BASE,
997
+ token_limits=LanguageModelTokenLimits(
998
+ token_limit_input=200_000, token_limit_output=100_000
999
+ ),
1000
+ published_at=date(2025, 4, 16),
1001
+ capabilities=[ModelCapabilities.STREAMING],
1002
+ info_cutoff_at=date(2024, 6, 1),
1003
+ )
1004
+ case LanguageModelName.LITELLM_OPENAI_GPT_4_1_MINI:
1005
+ return cls(
1006
+ name=model_name,
1007
+ provider=LanguageModelProvider.LITELLM,
1008
+ version="2025-04-14",
1009
+ encoder_name=EncoderName.O200K_BASE,
1010
+ published_at=date(2025, 4, 14),
1011
+ info_cutoff_at=date(2024, 6, 1),
1012
+ token_limits=LanguageModelTokenLimits(
1013
+ token_limit_input=1_047_576, token_limit_output=32_768
1014
+ ),
1015
+ capabilities=[
1016
+ ModelCapabilities.STREAMING,
1017
+ ModelCapabilities.FUNCTION_CALLING,
1018
+ ModelCapabilities.STRUCTURED_OUTPUT,
1019
+ ],
1020
+ )
1021
+ case LanguageModelName.LITELLM_OPENAI_GPT_4_1_NANO:
1022
+ return cls(
1023
+ name=model_name,
1024
+ provider=LanguageModelProvider.LITELLM,
1025
+ version="2025-04-14",
1026
+ encoder_name=EncoderName.O200K_BASE,
1027
+ published_at=date(2025, 4, 14),
1028
+ info_cutoff_at=date(2024, 6, 1),
1029
+ token_limits=LanguageModelTokenLimits(
1030
+ token_limit_input=1_047_576, token_limit_output=32_768
1031
+ ),
1032
+ capabilities=[
1033
+ ModelCapabilities.STREAMING,
1034
+ ModelCapabilities.FUNCTION_CALLING,
1035
+ ModelCapabilities.STRUCTURED_OUTPUT,
1036
+ ],
1037
+ )
882
1038
  case LanguageModelName.LITELLM_DEEPSEEK_R1:
883
1039
  return cls(
884
1040
  name=model_name,
@@ -0,0 +1,4 @@
1
+ from unique_toolkit.tools.a2a.config import SubAgentToolConfig
2
+ from unique_toolkit.tools.a2a.service import SubAgentTool
3
+
4
+ __all__ = ["SubAgentToolConfig", "SubAgentTool"]
@@ -0,0 +1,27 @@
1
+ from unique_toolkit.tools.config import get_configuration_dict
2
+ from unique_toolkit.tools.schemas import BaseToolConfig
3
+
4
+ DEFAULT_PARAM_DESCRIPTION_SUB_AGENT_USER_MESSAGE = """
5
+ This is the message that will be sent to the sub-agent.
6
+ """.strip()
7
+
8
+
9
+ class SubAgentToolConfig(BaseToolConfig):
10
+ model_config = get_configuration_dict()
11
+
12
+ name: str = "default_name"
13
+ assistant_id: str = ""
14
+ chat_id: str | None = None
15
+ reuse_chat: bool = True
16
+ tool_description_for_system_prompt: str = ""
17
+ tool_description: str = ""
18
+ param_description_sub_agent_user_message: str = (
19
+ DEFAULT_PARAM_DESCRIPTION_SUB_AGENT_USER_MESSAGE
20
+ )
21
+ tool_format_information_for_system_prompt: str = ""
22
+
23
+ tool_description_for_user_prompt: str = ""
24
+ tool_format_information_for_user_prompt: str = ""
25
+
26
+ poll_interval: float = 1.0
27
+ max_wait: float = 120.0
@@ -0,0 +1,49 @@
1
+ from logging import Logger
2
+
3
+ from unique_toolkit.app.schemas import ChatEvent
4
+ from unique_toolkit.tools.a2a.config import SubAgentToolConfig
5
+ from unique_toolkit.tools.a2a.service import SubAgentTool, ToolProgressReporter
6
+ from unique_toolkit.tools.config import ToolBuildConfig
7
+ from unique_toolkit.tools.schemas import BaseToolConfig
8
+ from unique_toolkit.tools.tool import Tool
9
+
10
+
11
+ class A2AManager:
12
+ def __init__(
13
+ self,
14
+ logger: Logger,
15
+ tool_progress_reporter: ToolProgressReporter,
16
+ ):
17
+ self._logger = logger
18
+ self._tool_progress_reporter = tool_progress_reporter
19
+
20
+ def get_all_sub_agents(
21
+ self, tool_configs: list[ToolBuildConfig], event: ChatEvent
22
+ ) -> tuple[list[ToolBuildConfig], list[Tool[BaseToolConfig]]]:
23
+ sub_agents = []
24
+
25
+ for tool_config in tool_configs:
26
+ if not tool_config.is_sub_agent:
27
+ continue
28
+
29
+ if not isinstance(tool_config.configuration, SubAgentToolConfig):
30
+ self._logger.error(
31
+ "tool_config.configuration must be of type SubAgentToolConfig"
32
+ )
33
+ continue
34
+
35
+ sub_agent_tool_config: SubAgentToolConfig = tool_config.configuration
36
+
37
+ sub_agents.append(
38
+ SubAgentTool(
39
+ configuration=sub_agent_tool_config,
40
+ event=event,
41
+ tool_progress_reporter=self._tool_progress_reporter,
42
+ )
43
+ )
44
+
45
+ filtered_tool_config = [
46
+ tool_config for tool_config in tool_configs if not tool_config.is_sub_agent
47
+ ]
48
+
49
+ return filtered_tool_config, sub_agents
@@ -0,0 +1,26 @@
1
+ from unique_toolkit import ShortTermMemoryService
2
+ from unique_toolkit.short_term_memory.persistent_short_term_memory_manager import (
3
+ PersistentShortMemoryManager,
4
+ )
5
+ from unique_toolkit.tools.a2a.schema import SubAgentShortTermMemorySchema
6
+
7
+
8
+ def _get_short_term_memory_name(assistant_id: str) -> str:
9
+ return f"sub_agent_chat_id_{assistant_id}"
10
+
11
+
12
+ def get_sub_agent_short_term_memory_manager(
13
+ company_id: str, user_id: str, chat_id: str, assistant_id: str
14
+ ) -> PersistentShortMemoryManager[SubAgentShortTermMemorySchema]:
15
+ short_term_memory_service = ShortTermMemoryService(
16
+ company_id=company_id,
17
+ user_id=user_id,
18
+ chat_id=chat_id,
19
+ message_id=None,
20
+ )
21
+ short_term_memory_manager = PersistentShortMemoryManager(
22
+ short_term_memory_service=short_term_memory_service,
23
+ short_term_memory_schema=SubAgentShortTermMemorySchema,
24
+ short_term_memory_name=_get_short_term_memory_name(assistant_id),
25
+ )
26
+ return short_term_memory_manager
@@ -0,0 +1,15 @@
1
+ from pydantic import BaseModel
2
+
3
+ from unique_toolkit.tools.schemas import ToolCallResponse
4
+
5
+
6
+ class SubAgentToolInput(BaseModel):
7
+ user_message: str
8
+
9
+
10
+ class SubAgentToolCallResponse(ToolCallResponse):
11
+ assistant_message: str
12
+
13
+
14
+ class SubAgentShortTermMemorySchema(BaseModel):
15
+ chat_id: str
@@ -0,0 +1,152 @@
1
+ from pydantic import Field, create_model
2
+ from unique_sdk.utils.chat_in_space import send_message_and_wait_for_completion
3
+
4
+ from unique_toolkit.app import ChatEvent
5
+ from unique_toolkit.evaluators.schemas import EvaluationMetricName
6
+ from unique_toolkit.language_model import (
7
+ LanguageModelFunction,
8
+ LanguageModelMessage,
9
+ LanguageModelToolDescription,
10
+ )
11
+ from unique_toolkit.tools.a2a.config import SubAgentToolConfig
12
+ from unique_toolkit.tools.a2a.memory import (
13
+ get_sub_agent_short_term_memory_manager,
14
+ )
15
+ from unique_toolkit.tools.a2a.schema import (
16
+ SubAgentShortTermMemorySchema,
17
+ SubAgentToolInput,
18
+ )
19
+ from unique_toolkit.tools.schemas import ToolCallResponse
20
+ from unique_toolkit.tools.tool import Tool
21
+ from unique_toolkit.tools.tool_progress_reporter import (
22
+ ProgressState,
23
+ ToolProgressReporter,
24
+ )
25
+
26
+
27
+ class SubAgentTool(Tool[SubAgentToolConfig]):
28
+ name: str = "SubAgentTool"
29
+
30
+ def __init__(
31
+ self,
32
+ configuration: SubAgentToolConfig,
33
+ event: ChatEvent,
34
+ tool_progress_reporter: ToolProgressReporter | None = None,
35
+ ):
36
+ super().__init__(configuration, event, tool_progress_reporter)
37
+ self._user_id = event.user_id
38
+ self._company_id = event.company_id
39
+ self.name = configuration.name
40
+
41
+ self._short_term_memory_manager = get_sub_agent_short_term_memory_manager(
42
+ company_id=self._company_id,
43
+ user_id=self._user_id,
44
+ chat_id=event.payload.chat_id,
45
+ assistant_id=self.config.assistant_id,
46
+ )
47
+
48
+ def tool_description(self) -> LanguageModelToolDescription:
49
+ tool_input_model_with_description = create_model(
50
+ "SubAgentToolInput",
51
+ user_message=(
52
+ str,
53
+ Field(description=self.config.param_description_sub_agent_user_message),
54
+ ),
55
+ )
56
+
57
+ return LanguageModelToolDescription(
58
+ name=self.name,
59
+ description=self.config.tool_description,
60
+ parameters=tool_input_model_with_description,
61
+ )
62
+
63
+ def tool_description_for_system_prompt(self) -> str:
64
+ return self.config.tool_description_for_system_prompt
65
+
66
+ def tool_format_information_for_system_prompt(self) -> str:
67
+ return self.config.tool_format_information_for_system_prompt
68
+
69
+ def tool_description_for_user_prompt(self) -> str:
70
+ return self.config.tool_description_for_user_prompt
71
+
72
+ def tool_format_information_for_user_prompt(self) -> str:
73
+ return self.config.tool_format_information_for_user_prompt
74
+
75
+ def evaluation_check_list(self) -> list[EvaluationMetricName]:
76
+ return []
77
+
78
+ def get_evaluation_checks_based_on_tool_response(
79
+ self,
80
+ tool_response: ToolCallResponse,
81
+ ) -> list[EvaluationMetricName]:
82
+ return []
83
+
84
+ async def _get_chat_id(self) -> str | None:
85
+ if not self.config.reuse_chat:
86
+ return None
87
+
88
+ if self.config.chat_id is not None:
89
+ return self.config.chat_id
90
+
91
+ # Check if there is a saved chat id in short term memory
92
+ short_term_memory = await self._short_term_memory_manager.load_async()
93
+
94
+ if short_term_memory is not None:
95
+ return short_term_memory.chat_id
96
+
97
+ return None
98
+
99
+ async def _save_chat_id(self, chat_id: str) -> None:
100
+ if not self.config.reuse_chat:
101
+ return
102
+
103
+ await self._short_term_memory_manager.save_async(
104
+ SubAgentShortTermMemorySchema(chat_id=chat_id)
105
+ )
106
+
107
+ async def run(self, tool_call: LanguageModelFunction) -> ToolCallResponse:
108
+ tool_input = SubAgentToolInput.model_validate(tool_call.arguments)
109
+
110
+ if self.tool_progress_reporter:
111
+ await self.tool_progress_reporter.notify_from_tool_call(
112
+ tool_call=tool_call,
113
+ name=f"{self.name}",
114
+ message=f"{tool_input.user_message}",
115
+ state=ProgressState.RUNNING,
116
+ )
117
+
118
+ # Check if there is a saved chat id in short term memory
119
+ chat_id = await self._get_chat_id()
120
+
121
+ response = await send_message_and_wait_for_completion(
122
+ user_id=self._user_id,
123
+ assistant_id=self.config.assistant_id,
124
+ company_id=self._company_id,
125
+ text=tool_input.user_message, # type: ignore
126
+ chat_id=chat_id, # type: ignore
127
+ poll_interval=self.config.poll_interval,
128
+ max_wait=self.config.max_wait,
129
+ )
130
+
131
+ if chat_id is None:
132
+ await self._save_chat_id(response["chatId"])
133
+
134
+ if response["text"] is None:
135
+ raise ValueError("No response returned from sub agent")
136
+
137
+ self._text = response["text"]
138
+ return ToolCallResponse(
139
+ id=tool_call.id, # type: ignore
140
+ name=tool_call.name,
141
+ content=response["text"],
142
+ )
143
+
144
+ def get_tool_call_result_for_loop_history(
145
+ self,
146
+ tool_response: ToolCallResponse,
147
+ ) -> LanguageModelMessage:
148
+ return ToolCallResponse(
149
+ id=tool_response.id,
150
+ name=tool_response.name,
151
+ content=tool_response["content"],
152
+ )
@@ -72,6 +72,7 @@ class ToolBuildConfig(BaseModel):
72
72
  default=False,
73
73
  description="This tool must be chosen by the user and no other tools are used for this iteration.",
74
74
  )
75
+ is_sub_agent: bool = False
75
76
 
76
77
  is_enabled: bool = Field(default=True)
77
78
 
@@ -7,6 +7,7 @@ from pydantic import BaseModel
7
7
  from tests.test_obj_factory import get_event_obj
8
8
  from unique_toolkit.app.schemas import McpServer, McpTool
9
9
  from unique_toolkit.chat.service import ChatService
10
+ from unique_toolkit.tools.a2a.manager import A2AManager
10
11
  from unique_toolkit.tools.config import ToolBuildConfig, ToolIcon, ToolSelectionPolicy
11
12
  from unique_toolkit.tools.factory import ToolFactory
12
13
  from unique_toolkit.tools.mcp.manager import MCPManager
@@ -162,25 +163,38 @@ class TestMCPManager:
162
163
  tool_progress_reporter=tool_progress_reporter,
163
164
  )
164
165
 
166
+ @pytest.fixture
167
+ def a2a_manager(self, tool_progress_reporter):
168
+ """Create MCP manager fixture"""
169
+ return A2AManager(
170
+ logger=self.logger,
171
+ tool_progress_reporter=tool_progress_reporter,
172
+ )
173
+
165
174
  @pytest.fixture
166
175
  def tool_manager_config(self, internal_tools):
167
176
  """Create tool manager configuration fixture"""
168
177
  return ToolManagerConfig(tools=internal_tools, max_tool_calls=10)
169
178
 
170
179
  @pytest.fixture
171
- def tool_manager(self, tool_manager_config, mcp_manager, tool_progress_reporter):
180
+ def tool_manager(
181
+ self, tool_manager_config, mcp_manager, a2a_manager, tool_progress_reporter
182
+ ):
172
183
  """Create tool manager fixture"""
184
+
173
185
  return ToolManager(
174
186
  logger=self.logger,
175
187
  config=tool_manager_config,
176
188
  event=self.event,
177
189
  tool_progress_reporter=tool_progress_reporter,
178
190
  mcp_manager=mcp_manager,
191
+ a2a_manager=a2a_manager,
179
192
  )
180
193
 
181
194
  def test_tool_manager_initialization(self, tool_manager):
182
195
  """Test tool manager is initialized correctly"""
183
196
  assert tool_manager is not None
197
+
184
198
  assert (
185
199
  len(tool_manager.get_tools()) >= 2
186
200
  ) # Should have both internal and MCP tools
@@ -255,12 +269,19 @@ class TestMCPManager:
255
269
  """Test the _init__tools method behavior with different scenarios"""
256
270
 
257
271
  # Test 1: Normal initialization with both tool types
272
+
273
+ a2a_manager = A2AManager(
274
+ logger=self.logger,
275
+ tool_progress_reporter=tool_progress_reporter,
276
+ )
277
+
258
278
  tool_manager = ToolManager(
259
279
  logger=self.logger,
260
280
  config=tool_manager_config,
261
281
  event=self.event,
262
282
  tool_progress_reporter=tool_progress_reporter,
263
283
  mcp_manager=mcp_manager,
284
+ a2a_manager=a2a_manager,
264
285
  )
265
286
 
266
287
  # Verify both tools are loaded
@@ -285,12 +306,18 @@ class TestMCPManager:
285
306
  event_with_disabled.payload.tool_choices = ["internal_search", "mcp_test_tool"]
286
307
  event_with_disabled.payload.disabled_tools = ["internal_search"]
287
308
 
309
+ a2a_manager = A2AManager(
310
+ logger=self.logger,
311
+ tool_progress_reporter=tool_progress_reporter,
312
+ )
313
+
288
314
  tool_manager = ToolManager(
289
315
  logger=self.logger,
290
316
  config=tool_manager_config,
291
317
  event=event_with_disabled,
292
318
  tool_progress_reporter=tool_progress_reporter,
293
319
  mcp_manager=mcp_manager,
320
+ a2a_manager=a2a_manager,
294
321
  )
295
322
 
296
323
  # Should only have MCP tool, internal tool should be filtered out
@@ -315,12 +342,18 @@ class TestMCPManager:
315
342
  event_with_limited_choices.payload.tool_choices = ["internal_search"]
316
343
  event_with_limited_choices.payload.disabled_tools = []
317
344
 
345
+ a2a_manager = A2AManager(
346
+ logger=self.logger,
347
+ tool_progress_reporter=tool_progress_reporter,
348
+ )
349
+
318
350
  tool_manager = ToolManager(
319
351
  logger=self.logger,
320
352
  config=tool_manager_config,
321
353
  event=event_with_limited_choices,
322
354
  tool_progress_reporter=tool_progress_reporter,
323
355
  mcp_manager=mcp_manager,
356
+ a2a_manager=a2a_manager,
324
357
  )
325
358
 
326
359
  # Should only have internal search tool
@@ -348,12 +381,18 @@ class TestMCPManager:
348
381
  tools=[exclusive_tool_config], max_tool_calls=10
349
382
  )
350
383
 
384
+ a2a_manager = A2AManager(
385
+ logger=self.logger,
386
+ tool_progress_reporter=tool_progress_reporter,
387
+ )
388
+
351
389
  tool_manager = ToolManager(
352
390
  logger=self.logger,
353
391
  config=config_with_exclusive,
354
392
  event=self.event,
355
393
  tool_progress_reporter=tool_progress_reporter,
356
394
  mcp_manager=mcp_manager,
395
+ a2a_manager=a2a_manager,
357
396
  )
358
397
 
359
398
  # Should only have the exclusive tool, MCP tools should be ignored
@@ -383,12 +422,18 @@ class TestMCPManager:
383
422
  tools=[disabled_tool_config], max_tool_calls=10
384
423
  )
385
424
 
425
+ a2a_manager = A2AManager(
426
+ logger=self.logger,
427
+ tool_progress_reporter=tool_progress_reporter,
428
+ )
429
+
386
430
  tool_manager = ToolManager(
387
431
  logger=self.logger,
388
432
  config=config_with_disabled,
389
433
  event=self.event,
390
434
  tool_progress_reporter=tool_progress_reporter,
391
435
  mcp_manager=mcp_manager,
436
+ a2a_manager=a2a_manager,
392
437
  )
393
438
 
394
439
  # Should only have MCP tool, disabled internal tool should be filtered out
@@ -11,6 +11,7 @@ from unique_toolkit.language_model.schemas import (
11
11
  LanguageModelTool,
12
12
  LanguageModelToolDescription,
13
13
  )
14
+ from unique_toolkit.tools.a2a.manager import A2AManager
14
15
  from unique_toolkit.tools.config import ToolBuildConfig, _rebuild_config_model
15
16
  from unique_toolkit.tools.factory import ToolFactory
16
17
  from unique_toolkit.tools.mcp.manager import MCPManager
@@ -71,6 +72,7 @@ class ToolManager:
71
72
  event: ChatEvent,
72
73
  tool_progress_reporter: ToolProgressReporter,
73
74
  mcp_manager: MCPManager,
75
+ a2a_manager: A2AManager,
74
76
  ):
75
77
  self._logger = logger
76
78
  self._config = config
@@ -81,6 +83,7 @@ class ToolManager:
81
83
  # this needs to be a set of strings to avoid duplicates
82
84
  self._tool_evaluation_check_list: set[EvaluationMetricName] = set()
83
85
  self._mcp_manager = mcp_manager
86
+ self._a2a_manager = a2a_manager
84
87
  self._init__tools(event)
85
88
 
86
89
  def _init__tools(self, event: ChatEvent) -> None:
@@ -90,6 +93,10 @@ class ToolManager:
90
93
  self._logger.info(f"Tool choices: {tool_choices}")
91
94
  self._logger.info(f"Tool configs: {tool_configs}")
92
95
 
96
+ tool_configs, sub_agents = self._a2a_manager.get_all_sub_agents(
97
+ tool_configs, event
98
+ )
99
+
93
100
  # Build internal tools from configurations
94
101
  internal_tools = [
95
102
  ToolFactory.build_tool_with_settings(
@@ -105,7 +112,7 @@ class ToolManager:
105
112
  # Get MCP tools (these are already properly instantiated)
106
113
  mcp_tools = self._mcp_manager.get_all_mcp_tools()
107
114
  # Combine both types of tools
108
- self.available_tools = internal_tools + mcp_tools
115
+ self.available_tools = internal_tools + mcp_tools + sub_agents
109
116
 
110
117
  for t in self.available_tools:
111
118
  if t.is_exclusive():
File without changes