tooluniverse 1.0.2__tar.gz → 1.0.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.

Potentially problematic release.


This version of tooluniverse might be problematic. Click here for more details.

Files changed (199) hide show
  1. {tooluniverse-1.0.2/src/tooluniverse.egg-info → tooluniverse-1.0.4}/PKG-INFO +7 -6
  2. {tooluniverse-1.0.2 → tooluniverse-1.0.4}/README.md +5 -5
  3. {tooluniverse-1.0.2 → tooluniverse-1.0.4}/pyproject.toml +2 -1
  4. tooluniverse-1.0.4/src/tooluniverse/agentic_tool.py +495 -0
  5. {tooluniverse-1.0.2 → tooluniverse-1.0.4}/src/tooluniverse/compose_scripts/output_summarizer.py +21 -15
  6. {tooluniverse-1.0.2 → tooluniverse-1.0.4}/src/tooluniverse/compose_scripts/tool_metadata_generator.py +6 -0
  7. {tooluniverse-1.0.2 → tooluniverse-1.0.4}/src/tooluniverse/data/agentic_tools.json +1 -1
  8. {tooluniverse-1.0.2 → tooluniverse-1.0.4}/src/tooluniverse/data/output_summarization_tools.json +2 -2
  9. {tooluniverse-1.0.2 → tooluniverse-1.0.4}/src/tooluniverse/execute_function.py +185 -70
  10. tooluniverse-1.0.4/src/tooluniverse/llm_clients.py +369 -0
  11. {tooluniverse-1.0.2 → tooluniverse-1.0.4}/src/tooluniverse/output_hook.py +92 -3
  12. tooluniverse-1.0.4/src/tooluniverse/scripts/filter_tool_files.py +194 -0
  13. {tooluniverse-1.0.2 → tooluniverse-1.0.4}/src/tooluniverse/smcp_server.py +19 -13
  14. tooluniverse-1.0.4/src/tooluniverse/test/list_azure_openai_models.py +210 -0
  15. tooluniverse-1.0.4/src/tooluniverse/test/test_agentic_tool_azure_models.py +91 -0
  16. tooluniverse-1.0.4/src/tooluniverse/test/test_api_key_validation_min.py +64 -0
  17. tooluniverse-1.0.4/src/tooluniverse/test/test_global_fallback.py +288 -0
  18. tooluniverse-1.0.4/src/tooluniverse/test/test_hooks_direct.py +219 -0
  19. tooluniverse-1.0.4/src/tooluniverse/test/test_list_built_in_tools.py +33 -0
  20. tooluniverse-1.0.4/src/tooluniverse/test/test_stdio_hooks.py +285 -0
  21. {tooluniverse-1.0.2 → tooluniverse-1.0.4/src/tooluniverse.egg-info}/PKG-INFO +7 -6
  22. {tooluniverse-1.0.2 → tooluniverse-1.0.4}/src/tooluniverse.egg-info/SOURCES.txt +9 -0
  23. {tooluniverse-1.0.2 → tooluniverse-1.0.4}/src/tooluniverse.egg-info/requires.txt +1 -0
  24. tooluniverse-1.0.2/src/tooluniverse/agentic_tool.py +0 -563
  25. {tooluniverse-1.0.2 → tooluniverse-1.0.4}/LICENSE +0 -0
  26. {tooluniverse-1.0.2 → tooluniverse-1.0.4}/setup.cfg +0 -0
  27. {tooluniverse-1.0.2 → tooluniverse-1.0.4}/src/tooluniverse/__init__.py +0 -0
  28. {tooluniverse-1.0.2 → tooluniverse-1.0.4}/src/tooluniverse/admetai_tool.py +0 -0
  29. {tooluniverse-1.0.2 → tooluniverse-1.0.4}/src/tooluniverse/alphafold_tool.py +0 -0
  30. {tooluniverse-1.0.2 → tooluniverse-1.0.4}/src/tooluniverse/base_tool.py +0 -0
  31. {tooluniverse-1.0.2 → tooluniverse-1.0.4}/src/tooluniverse/boltz_tool.py +0 -0
  32. {tooluniverse-1.0.2 → tooluniverse-1.0.4}/src/tooluniverse/chem_tool.py +0 -0
  33. {tooluniverse-1.0.2 → tooluniverse-1.0.4}/src/tooluniverse/compose_scripts/__init__.py +0 -0
  34. {tooluniverse-1.0.2 → tooluniverse-1.0.4}/src/tooluniverse/compose_scripts/biomarker_discovery.py +0 -0
  35. {tooluniverse-1.0.2 → tooluniverse-1.0.4}/src/tooluniverse/compose_scripts/comprehensive_drug_discovery.py +0 -0
  36. {tooluniverse-1.0.2 → tooluniverse-1.0.4}/src/tooluniverse/compose_scripts/drug_safety_analyzer.py +0 -0
  37. {tooluniverse-1.0.2 → tooluniverse-1.0.4}/src/tooluniverse/compose_scripts/literature_tool.py +0 -0
  38. {tooluniverse-1.0.2 → tooluniverse-1.0.4}/src/tooluniverse/compose_scripts/tool_description_optimizer.py +0 -0
  39. {tooluniverse-1.0.2 → tooluniverse-1.0.4}/src/tooluniverse/compose_scripts/tool_discover.py +0 -0
  40. {tooluniverse-1.0.2 → tooluniverse-1.0.4}/src/tooluniverse/compose_scripts/tool_graph_composer.py +0 -0
  41. {tooluniverse-1.0.2 → tooluniverse-1.0.4}/src/tooluniverse/compose_scripts/tool_graph_generation.py +0 -0
  42. {tooluniverse-1.0.2 → tooluniverse-1.0.4}/src/tooluniverse/compose_tool.py +0 -0
  43. {tooluniverse-1.0.2 → tooluniverse-1.0.4}/src/tooluniverse/ctg_tool.py +0 -0
  44. {tooluniverse-1.0.2 → tooluniverse-1.0.4}/src/tooluniverse/custom_tool.py +0 -0
  45. {tooluniverse-1.0.2 → tooluniverse-1.0.4}/src/tooluniverse/dailymed_tool.py +0 -0
  46. {tooluniverse-1.0.2 → tooluniverse-1.0.4}/src/tooluniverse/data/__init__.py +0 -0
  47. {tooluniverse-1.0.2 → tooluniverse-1.0.4}/src/tooluniverse/data/admetai_tools.json +0 -0
  48. {tooluniverse-1.0.2 → tooluniverse-1.0.4}/src/tooluniverse/data/adverse_event_tools.json +0 -0
  49. {tooluniverse-1.0.2 → tooluniverse-1.0.4}/src/tooluniverse/data/alphafold_tools.json +0 -0
  50. {tooluniverse-1.0.2 → tooluniverse-1.0.4}/src/tooluniverse/data/boltz_tools.json +0 -0
  51. {tooluniverse-1.0.2 → tooluniverse-1.0.4}/src/tooluniverse/data/chembl_tools.json +0 -0
  52. {tooluniverse-1.0.2 → tooluniverse-1.0.4}/src/tooluniverse/data/clinicaltrials_gov_tools.json +0 -0
  53. {tooluniverse-1.0.2 → tooluniverse-1.0.4}/src/tooluniverse/data/compose_tools.json +0 -0
  54. {tooluniverse-1.0.2 → tooluniverse-1.0.4}/src/tooluniverse/data/dailymed_tools.json +0 -0
  55. {tooluniverse-1.0.2 → tooluniverse-1.0.4}/src/tooluniverse/data/dataset_tools.json +0 -0
  56. {tooluniverse-1.0.2 → tooluniverse-1.0.4}/src/tooluniverse/data/disease_target_score_tools.json +0 -0
  57. {tooluniverse-1.0.2 → tooluniverse-1.0.4}/src/tooluniverse/data/efo_tools.json +0 -0
  58. {tooluniverse-1.0.2 → tooluniverse-1.0.4}/src/tooluniverse/data/embedding_tools.json +0 -0
  59. {tooluniverse-1.0.2 → tooluniverse-1.0.4}/src/tooluniverse/data/enrichr_tools.json +0 -0
  60. {tooluniverse-1.0.2 → tooluniverse-1.0.4}/src/tooluniverse/data/europe_pmc_tools.json +0 -0
  61. {tooluniverse-1.0.2 → tooluniverse-1.0.4}/src/tooluniverse/data/expert_feedback_tools.json +0 -0
  62. {tooluniverse-1.0.2 → tooluniverse-1.0.4}/src/tooluniverse/data/fda_drug_adverse_event_tools.json +0 -0
  63. {tooluniverse-1.0.2 → tooluniverse-1.0.4}/src/tooluniverse/data/fda_drug_labeling_tools.json +0 -0
  64. {tooluniverse-1.0.2 → tooluniverse-1.0.4}/src/tooluniverse/data/fda_drugs_with_brand_generic_names_for_tool.py +0 -0
  65. {tooluniverse-1.0.2 → tooluniverse-1.0.4}/src/tooluniverse/data/finder_tools.json +0 -0
  66. {tooluniverse-1.0.2 → tooluniverse-1.0.4}/src/tooluniverse/data/gene_ontology_tools.json +0 -0
  67. {tooluniverse-1.0.2 → tooluniverse-1.0.4}/src/tooluniverse/data/gwas_tools.json +0 -0
  68. {tooluniverse-1.0.2 → tooluniverse-1.0.4}/src/tooluniverse/data/hpa_tools.json +0 -0
  69. {tooluniverse-1.0.2 → tooluniverse-1.0.4}/src/tooluniverse/data/humanbase_tools.json +0 -0
  70. {tooluniverse-1.0.2 → tooluniverse-1.0.4}/src/tooluniverse/data/idmap_tools.json +0 -0
  71. {tooluniverse-1.0.2 → tooluniverse-1.0.4}/src/tooluniverse/data/mcp_client_tools_example.json +0 -0
  72. {tooluniverse-1.0.2 → tooluniverse-1.0.4}/src/tooluniverse/data/mcpautoloadertool_defaults.json +0 -0
  73. {tooluniverse-1.0.2 → tooluniverse-1.0.4}/src/tooluniverse/data/medlineplus_tools.json +0 -0
  74. {tooluniverse-1.0.2 → tooluniverse-1.0.4}/src/tooluniverse/data/monarch_tools.json +0 -0
  75. {tooluniverse-1.0.2 → tooluniverse-1.0.4}/src/tooluniverse/data/openalex_tools.json +0 -0
  76. {tooluniverse-1.0.2 → tooluniverse-1.0.4}/src/tooluniverse/data/opentarget_tools.json +0 -0
  77. {tooluniverse-1.0.2 → tooluniverse-1.0.4}/src/tooluniverse/data/packages/bioinformatics_core_tools.json +0 -0
  78. {tooluniverse-1.0.2 → tooluniverse-1.0.4}/src/tooluniverse/data/packages/categorized_tools.txt +0 -0
  79. {tooluniverse-1.0.2 → tooluniverse-1.0.4}/src/tooluniverse/data/packages/cheminformatics_tools.json +0 -0
  80. {tooluniverse-1.0.2 → tooluniverse-1.0.4}/src/tooluniverse/data/packages/earth_sciences_tools.json +0 -0
  81. {tooluniverse-1.0.2 → tooluniverse-1.0.4}/src/tooluniverse/data/packages/genomics_tools.json +0 -0
  82. {tooluniverse-1.0.2 → tooluniverse-1.0.4}/src/tooluniverse/data/packages/image_processing_tools.json +0 -0
  83. {tooluniverse-1.0.2 → tooluniverse-1.0.4}/src/tooluniverse/data/packages/machine_learning_tools.json +0 -0
  84. {tooluniverse-1.0.2 → tooluniverse-1.0.4}/src/tooluniverse/data/packages/neuroscience_tools.json +0 -0
  85. {tooluniverse-1.0.2 → tooluniverse-1.0.4}/src/tooluniverse/data/packages/original_tools.txt +0 -0
  86. {tooluniverse-1.0.2 → tooluniverse-1.0.4}/src/tooluniverse/data/packages/physics_astronomy_tools.json +0 -0
  87. {tooluniverse-1.0.2 → tooluniverse-1.0.4}/src/tooluniverse/data/packages/scientific_computing_tools.json +0 -0
  88. {tooluniverse-1.0.2 → tooluniverse-1.0.4}/src/tooluniverse/data/packages/single_cell_tools.json +0 -0
  89. {tooluniverse-1.0.2 → tooluniverse-1.0.4}/src/tooluniverse/data/packages/structural_biology_tools.json +0 -0
  90. {tooluniverse-1.0.2 → tooluniverse-1.0.4}/src/tooluniverse/data/packages/visualization_tools.json +0 -0
  91. {tooluniverse-1.0.2 → tooluniverse-1.0.4}/src/tooluniverse/data/pubchem_tools.json +0 -0
  92. {tooluniverse-1.0.2 → tooluniverse-1.0.4}/src/tooluniverse/data/pubtator_tools.json +0 -0
  93. {tooluniverse-1.0.2 → tooluniverse-1.0.4}/src/tooluniverse/data/rcsb_pdb_tools.json +0 -0
  94. {tooluniverse-1.0.2 → tooluniverse-1.0.4}/src/tooluniverse/data/reactome_tools.json +0 -0
  95. {tooluniverse-1.0.2 → tooluniverse-1.0.4}/src/tooluniverse/data/semantic_scholar_tools.json +0 -0
  96. {tooluniverse-1.0.2 → tooluniverse-1.0.4}/src/tooluniverse/data/special_tools.json +0 -0
  97. {tooluniverse-1.0.2 → tooluniverse-1.0.4}/src/tooluniverse/data/tool_composition_tools.json +0 -0
  98. {tooluniverse-1.0.2 → tooluniverse-1.0.4}/src/tooluniverse/data/toolfinderkeyword_defaults.json +0 -0
  99. {tooluniverse-1.0.2 → tooluniverse-1.0.4}/src/tooluniverse/data/txagent_client_tools.json +0 -0
  100. {tooluniverse-1.0.2 → tooluniverse-1.0.4}/src/tooluniverse/data/uniprot_tools.json +0 -0
  101. {tooluniverse-1.0.2 → tooluniverse-1.0.4}/src/tooluniverse/data/url_fetch_tools.json +0 -0
  102. {tooluniverse-1.0.2 → tooluniverse-1.0.4}/src/tooluniverse/data/uspto_downloader_tools.json +0 -0
  103. {tooluniverse-1.0.2 → tooluniverse-1.0.4}/src/tooluniverse/data/uspto_tools.json +0 -0
  104. {tooluniverse-1.0.2 → tooluniverse-1.0.4}/src/tooluniverse/data/xml_tools.json +0 -0
  105. {tooluniverse-1.0.2 → tooluniverse-1.0.4}/src/tooluniverse/dataset_tool.py +0 -0
  106. {tooluniverse-1.0.2 → tooluniverse-1.0.4}/src/tooluniverse/default_config.py +0 -0
  107. {tooluniverse-1.0.2 → tooluniverse-1.0.4}/src/tooluniverse/efo_tool.py +0 -0
  108. {tooluniverse-1.0.2 → tooluniverse-1.0.4}/src/tooluniverse/embedding_database.py +0 -0
  109. {tooluniverse-1.0.2 → tooluniverse-1.0.4}/src/tooluniverse/embedding_sync.py +0 -0
  110. {tooluniverse-1.0.2 → tooluniverse-1.0.4}/src/tooluniverse/enrichr_tool.py +0 -0
  111. {tooluniverse-1.0.2 → tooluniverse-1.0.4}/src/tooluniverse/europe_pmc_tool.py +0 -0
  112. {tooluniverse-1.0.2 → tooluniverse-1.0.4}/src/tooluniverse/extended_hooks.py +0 -0
  113. {tooluniverse-1.0.2 → tooluniverse-1.0.4}/src/tooluniverse/gene_ontology_tool.py +0 -0
  114. {tooluniverse-1.0.2 → tooluniverse-1.0.4}/src/tooluniverse/graphql_tool.py +0 -0
  115. {tooluniverse-1.0.2 → tooluniverse-1.0.4}/src/tooluniverse/gwas_tool.py +0 -0
  116. {tooluniverse-1.0.2 → tooluniverse-1.0.4}/src/tooluniverse/hpa_tool.py +0 -0
  117. {tooluniverse-1.0.2 → tooluniverse-1.0.4}/src/tooluniverse/humanbase_tool.py +0 -0
  118. {tooluniverse-1.0.2 → tooluniverse-1.0.4}/src/tooluniverse/logging_config.py +0 -0
  119. {tooluniverse-1.0.2 → tooluniverse-1.0.4}/src/tooluniverse/mcp_client_tool.py +0 -0
  120. {tooluniverse-1.0.2 → tooluniverse-1.0.4}/src/tooluniverse/mcp_integration.py +0 -0
  121. {tooluniverse-1.0.2 → tooluniverse-1.0.4}/src/tooluniverse/mcp_tool_registry.py +0 -0
  122. {tooluniverse-1.0.2 → tooluniverse-1.0.4}/src/tooluniverse/medlineplus_tool.py +0 -0
  123. {tooluniverse-1.0.2 → tooluniverse-1.0.4}/src/tooluniverse/openalex_tool.py +0 -0
  124. {tooluniverse-1.0.2 → tooluniverse-1.0.4}/src/tooluniverse/openfda_adv_tool.py +0 -0
  125. {tooluniverse-1.0.2 → tooluniverse-1.0.4}/src/tooluniverse/openfda_tool.py +0 -0
  126. {tooluniverse-1.0.2 → tooluniverse-1.0.4}/src/tooluniverse/package_tool.py +0 -0
  127. {tooluniverse-1.0.2 → tooluniverse-1.0.4}/src/tooluniverse/pubchem_tool.py +0 -0
  128. {tooluniverse-1.0.2 → tooluniverse-1.0.4}/src/tooluniverse/pubtator_tool.py +0 -0
  129. {tooluniverse-1.0.2 → tooluniverse-1.0.4}/src/tooluniverse/rcsb_pdb_tool.py +0 -0
  130. {tooluniverse-1.0.2 → tooluniverse-1.0.4}/src/tooluniverse/reactome_tool.py +0 -0
  131. {tooluniverse-1.0.2 → tooluniverse-1.0.4}/src/tooluniverse/remote/boltz/boltz_mcp_server.py +0 -0
  132. {tooluniverse-1.0.2 → tooluniverse-1.0.4}/src/tooluniverse/remote/depmap_24q2/depmap_24q2_mcp_tool.py +0 -0
  133. {tooluniverse-1.0.2 → tooluniverse-1.0.4}/src/tooluniverse/remote/expert_feedback/human_expert_mcp_tools.py +0 -0
  134. {tooluniverse-1.0.2 → tooluniverse-1.0.4}/src/tooluniverse/remote/expert_feedback/simple_test.py +0 -0
  135. {tooluniverse-1.0.2 → tooluniverse-1.0.4}/src/tooluniverse/remote/expert_feedback/start_web_interface.py +0 -0
  136. {tooluniverse-1.0.2 → tooluniverse-1.0.4}/src/tooluniverse/remote/expert_feedback/web_only_interface.py +0 -0
  137. {tooluniverse-1.0.2 → tooluniverse-1.0.4}/src/tooluniverse/remote/immune_compass/compass_tool.py +0 -0
  138. {tooluniverse-1.0.2 → tooluniverse-1.0.4}/src/tooluniverse/remote/pinnacle/pinnacle_tool.py +0 -0
  139. {tooluniverse-1.0.2 → tooluniverse-1.0.4}/src/tooluniverse/remote/transcriptformer/transcriptformer_tool.py +0 -0
  140. {tooluniverse-1.0.2 → tooluniverse-1.0.4}/src/tooluniverse/remote/uspto_downloader/uspto_downloader_mcp_server.py +0 -0
  141. {tooluniverse-1.0.2 → tooluniverse-1.0.4}/src/tooluniverse/remote/uspto_downloader/uspto_downloader_tool.py +0 -0
  142. {tooluniverse-1.0.2 → tooluniverse-1.0.4}/src/tooluniverse/remote_tool.py +0 -0
  143. {tooluniverse-1.0.2 → tooluniverse-1.0.4}/src/tooluniverse/restful_tool.py +0 -0
  144. {tooluniverse-1.0.2 → tooluniverse-1.0.4}/src/tooluniverse/scripts/generate_tool_graph.py +0 -0
  145. {tooluniverse-1.0.2 → tooluniverse-1.0.4}/src/tooluniverse/scripts/visualize_tool_graph.py +0 -0
  146. {tooluniverse-1.0.2 → tooluniverse-1.0.4}/src/tooluniverse/semantic_scholar_tool.py +0 -0
  147. {tooluniverse-1.0.2 → tooluniverse-1.0.4}/src/tooluniverse/smcp.py +0 -0
  148. {tooluniverse-1.0.2 → tooluniverse-1.0.4}/src/tooluniverse/test/mcp_server_test.py +0 -0
  149. {tooluniverse-1.0.2 → tooluniverse-1.0.4}/src/tooluniverse/test/test_admetai_tool.py +0 -0
  150. {tooluniverse-1.0.2 → tooluniverse-1.0.4}/src/tooluniverse/test/test_agentic_tool.py +0 -0
  151. {tooluniverse-1.0.2 → tooluniverse-1.0.4}/src/tooluniverse/test/test_alphafold_tool.py +0 -0
  152. {tooluniverse-1.0.2 → tooluniverse-1.0.4}/src/tooluniverse/test/test_chem_tool.py +0 -0
  153. {tooluniverse-1.0.2 → tooluniverse-1.0.4}/src/tooluniverse/test/test_compose_lieraturereview.py +0 -0
  154. {tooluniverse-1.0.2 → tooluniverse-1.0.4}/src/tooluniverse/test/test_compose_tool.py +0 -0
  155. {tooluniverse-1.0.2 → tooluniverse-1.0.4}/src/tooluniverse/test/test_dailymed.py +0 -0
  156. {tooluniverse-1.0.2 → tooluniverse-1.0.4}/src/tooluniverse/test/test_dataset_tool.py +0 -0
  157. {tooluniverse-1.0.2 → tooluniverse-1.0.4}/src/tooluniverse/test/test_disease_target_score.py +0 -0
  158. {tooluniverse-1.0.2 → tooluniverse-1.0.4}/src/tooluniverse/test/test_drugbank_filter_examples.py +0 -0
  159. {tooluniverse-1.0.2 → tooluniverse-1.0.4}/src/tooluniverse/test/test_efo.py +0 -0
  160. {tooluniverse-1.0.2 → tooluniverse-1.0.4}/src/tooluniverse/test/test_enrichr_tool.py +0 -0
  161. {tooluniverse-1.0.2 → tooluniverse-1.0.4}/src/tooluniverse/test/test_europe_pmc_tool.py +0 -0
  162. {tooluniverse-1.0.2 → tooluniverse-1.0.4}/src/tooluniverse/test/test_fda_adv.py +0 -0
  163. {tooluniverse-1.0.2 → tooluniverse-1.0.4}/src/tooluniverse/test/test_fda_drug_labeling.py +0 -0
  164. {tooluniverse-1.0.2 → tooluniverse-1.0.4}/src/tooluniverse/test/test_gene_ontology_tools.py +0 -0
  165. {tooluniverse-1.0.2 → tooluniverse-1.0.4}/src/tooluniverse/test/test_gwas_tool.py +0 -0
  166. {tooluniverse-1.0.2 → tooluniverse-1.0.4}/src/tooluniverse/test/test_hpa.py +0 -0
  167. {tooluniverse-1.0.2 → tooluniverse-1.0.4}/src/tooluniverse/test/test_humanbase_tool.py +0 -0
  168. {tooluniverse-1.0.2 → tooluniverse-1.0.4}/src/tooluniverse/test/test_idmap_tools.py +0 -0
  169. {tooluniverse-1.0.2 → tooluniverse-1.0.4}/src/tooluniverse/test/test_mcp_server.py +0 -0
  170. {tooluniverse-1.0.2 → tooluniverse-1.0.4}/src/tooluniverse/test/test_mcp_tool.py +0 -0
  171. {tooluniverse-1.0.2 → tooluniverse-1.0.4}/src/tooluniverse/test/test_medlineplus.py +0 -0
  172. {tooluniverse-1.0.2 → tooluniverse-1.0.4}/src/tooluniverse/test/test_openalex_tool.py +0 -0
  173. {tooluniverse-1.0.2 → tooluniverse-1.0.4}/src/tooluniverse/test/test_opentargets.py +0 -0
  174. {tooluniverse-1.0.2 → tooluniverse-1.0.4}/src/tooluniverse/test/test_pubchem_tool.py +0 -0
  175. {tooluniverse-1.0.2 → tooluniverse-1.0.4}/src/tooluniverse/test/test_pubtator_tool.py +0 -0
  176. {tooluniverse-1.0.2 → tooluniverse-1.0.4}/src/tooluniverse/test/test_rcsb_pdb_tool.py +0 -0
  177. {tooluniverse-1.0.2 → tooluniverse-1.0.4}/src/tooluniverse/test/test_reactome.py +0 -0
  178. {tooluniverse-1.0.2 → tooluniverse-1.0.4}/src/tooluniverse/test/test_semantic_scholar_tool.py +0 -0
  179. {tooluniverse-1.0.2 → tooluniverse-1.0.4}/src/tooluniverse/test/test_software_tools.py +0 -0
  180. {tooluniverse-1.0.2 → tooluniverse-1.0.4}/src/tooluniverse/test/test_tool_description_optimizer.py +0 -0
  181. {tooluniverse-1.0.2 → tooluniverse-1.0.4}/src/tooluniverse/test/test_tool_finder.py +0 -0
  182. {tooluniverse-1.0.2 → tooluniverse-1.0.4}/src/tooluniverse/test/test_tool_finder_llm.py +0 -0
  183. {tooluniverse-1.0.2 → tooluniverse-1.0.4}/src/tooluniverse/test/test_tools_find.py +0 -0
  184. {tooluniverse-1.0.2 → tooluniverse-1.0.4}/src/tooluniverse/test/test_uniprot_tools.py +0 -0
  185. {tooluniverse-1.0.2 → tooluniverse-1.0.4}/src/tooluniverse/test/test_uspto_tool.py +0 -0
  186. {tooluniverse-1.0.2 → tooluniverse-1.0.4}/src/tooluniverse/test/test_xml_tool.py +0 -0
  187. {tooluniverse-1.0.2 → tooluniverse-1.0.4}/src/tooluniverse/tool_finder_embedding.py +0 -0
  188. {tooluniverse-1.0.2 → tooluniverse-1.0.4}/src/tooluniverse/tool_finder_keyword.py +0 -0
  189. {tooluniverse-1.0.2 → tooluniverse-1.0.4}/src/tooluniverse/tool_finder_llm.py +0 -0
  190. {tooluniverse-1.0.2 → tooluniverse-1.0.4}/src/tooluniverse/tool_graph_web_ui.py +0 -0
  191. {tooluniverse-1.0.2 → tooluniverse-1.0.4}/src/tooluniverse/tool_registry.py +0 -0
  192. {tooluniverse-1.0.2 → tooluniverse-1.0.4}/src/tooluniverse/uniprot_tool.py +0 -0
  193. {tooluniverse-1.0.2 → tooluniverse-1.0.4}/src/tooluniverse/url_tool.py +0 -0
  194. {tooluniverse-1.0.2 → tooluniverse-1.0.4}/src/tooluniverse/uspto_tool.py +0 -0
  195. {tooluniverse-1.0.2 → tooluniverse-1.0.4}/src/tooluniverse/utils.py +0 -0
  196. {tooluniverse-1.0.2 → tooluniverse-1.0.4}/src/tooluniverse/xml_tool.py +0 -0
  197. {tooluniverse-1.0.2 → tooluniverse-1.0.4}/src/tooluniverse.egg-info/dependency_links.txt +0 -0
  198. {tooluniverse-1.0.2 → tooluniverse-1.0.4}/src/tooluniverse.egg-info/entry_points.txt +0 -0
  199. {tooluniverse-1.0.2 → tooluniverse-1.0.4}/src/tooluniverse.egg-info/top_level.txt +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: tooluniverse
3
- Version: 1.0.2
3
+ Version: 1.0.4
4
4
  Summary: A comprehensive collection of scientific tools for Agentic AI, offering integration with the ToolUniverse SDK and MCP Server to support advanced scientific workflows.
5
5
  Author-email: Shanghua Gao <shanghuagao@gmail.com>
6
6
  Project-URL: Homepage, https://github.com/mims-harvard/TxAgent
@@ -18,6 +18,7 @@ Requires-Dist: networkx>=3.4.0
18
18
  Requires-Dist: openai>=1.107.0
19
19
  Requires-Dist: pyyaml>=6.0.0
20
20
  Requires-Dist: google-genai>=1.36.0
21
+ Requires-Dist: google-generativeai>=0.7.2
21
22
  Requires-Dist: mcp[cli]>=1.9.3
22
23
  Requires-Dist: fastmcp>=2.12.0
23
24
  Requires-Dist: xmltodict>=1.0.0
@@ -63,7 +64,7 @@ Provides-Extra: all
63
64
  Requires-Dist: tooluniverse[dev,docs,graph]; extra == "all"
64
65
  Dynamic: license-file
65
66
 
66
- # ToolUniverse: Democratizing AI scientists
67
+ # <img src="docs/_static/logo.png" alt="ToolUniverse Logo" height="28" style="vertical-align: middle; margin-right: 8px;" /> ToolUniverse: Democratizing AI scientists
67
68
 
68
69
  [![ToolUniverse-PIP](https://img.shields.io/badge/PyPI-ToolUniverse-blue)](https://pypi.org/project/tooluniverse/)
69
70
  [![ToolUniverse](https://img.shields.io/badge/Code-ToolUniverse-purple)](https://github.com/mims-harvard/ToolUniverse)
@@ -96,7 +97,7 @@ ToolUniverse is an ecosystem for creating AI scientist systems from any open or
96
97
 
97
98
 
98
99
  ## 🤖 Building AI Scientists with ToolUniverse in 5 minutes
99
- - **[Overview](https://zitniklab.hms.harvard.edu/bioagent/guide/building_ai_scientists/index.html)**: Create AI research assistants from any LLM
100
+ - **[Overview](https://zitniklab.hms.harvard.edu/bioagent/guide/building_ai_scientists/index.html)**: Create AI scientists from any LLM
100
101
  - **[Claude Desktop Integration](https://zitniklab.hms.harvard.edu/bioagent/guide/building_ai_scientists/claude_desktop.html)**: Native MCP integration with Claude Desktop App
101
102
  - **[Claude Code Integration](https://zitniklab.hms.harvard.edu/bioagent/guide/building_ai_scientists/claude_code.html)**: AI scientist development in Claude Code environment
102
103
  - **[Gemini CLI Integration](https://zitniklab.hms.harvard.edu/bioagent/guide/building_ai_scientists/gemini_cli.html)**: Command-line scientific research with Google Gemini
@@ -109,7 +110,7 @@ ToolUniverse is an ecosystem for creating AI scientist systems from any open or
109
110
 
110
111
  AI scientists are emerging computational systems that serve as collaborative partners in discovery. However, these systems remain difficult to build because they are bespoke, tied to rigid workflows, and lack shared environments that unify tools, data, and analysts into a common ecosystem.
111
112
 
112
- ToolUniverse addresses this challenge by providing a standardized ecosystem that transforms any AI model into a powerful research scientist. By abstracting capabilities behind a unified interface, ToolUniverse wraps around any AI model (LLM, AI agent, or large reasoning model) and enables users to create and refine entirely custom AI research assistants without additional training or finetuning.
113
+ ToolUniverse addresses this challenge by providing a standardized ecosystem that transforms any AI model into a powerful research scientist. By abstracting capabilities behind a unified interface, ToolUniverse wraps around any AI model (LLM, AI agent, or large reasoning model) and enables users to create and refine entirely custom AI scientists without additional training or finetuning.
113
114
 
114
115
  **Key Features:**
115
116
 
@@ -212,7 +213,7 @@ tooluniverse-smcp
212
213
 
213
214
 
214
215
  ---
215
- **Hypercholesterolemia Drug Discovery** [[Tutorial]]((https://zitniklab.hms.harvard.edu/bioagent/tutorials/tooluniverse_case_study.html)) [[Code]](https://colab.research.google.com/drive/1UwJ6RwyUoqI5risKQ365EeFdDQWOeOCv?usp=sharing)
216
+ **Hypercholesterolemia Drug Discovery** [[Tutorial]](https://zitniklab.hms.harvard.edu/bioagent/tutorials/tooluniverse_case_study.html) [[Code]](https://colab.research.google.com/drive/1UwJ6RwyUoqI5risKQ365EeFdDQWOeOCv?usp=sharing)
216
217
 
217
218
  ---
218
219
 
@@ -316,4 +317,4 @@ ToolUniverse is developed by the [Zitnik Lab](https://zitniklab.hms.harvard.edu/
316
317
 
317
318
  ---
318
319
 
319
- *Democratizing AI agents for science - one tooluniverse at a time.*
320
+ *Democratizing AI agents for science with ToolUniverse.*
@@ -1,4 +1,4 @@
1
- # ToolUniverse: Democratizing AI scientists
1
+ # <img src="docs/_static/logo.png" alt="ToolUniverse Logo" height="28" style="vertical-align: middle; margin-right: 8px;" /> ToolUniverse: Democratizing AI scientists
2
2
 
3
3
  [![ToolUniverse-PIP](https://img.shields.io/badge/PyPI-ToolUniverse-blue)](https://pypi.org/project/tooluniverse/)
4
4
  [![ToolUniverse](https://img.shields.io/badge/Code-ToolUniverse-purple)](https://github.com/mims-harvard/ToolUniverse)
@@ -31,7 +31,7 @@ ToolUniverse is an ecosystem for creating AI scientist systems from any open or
31
31
 
32
32
 
33
33
  ## 🤖 Building AI Scientists with ToolUniverse in 5 minutes
34
- - **[Overview](https://zitniklab.hms.harvard.edu/bioagent/guide/building_ai_scientists/index.html)**: Create AI research assistants from any LLM
34
+ - **[Overview](https://zitniklab.hms.harvard.edu/bioagent/guide/building_ai_scientists/index.html)**: Create AI scientists from any LLM
35
35
  - **[Claude Desktop Integration](https://zitniklab.hms.harvard.edu/bioagent/guide/building_ai_scientists/claude_desktop.html)**: Native MCP integration with Claude Desktop App
36
36
  - **[Claude Code Integration](https://zitniklab.hms.harvard.edu/bioagent/guide/building_ai_scientists/claude_code.html)**: AI scientist development in Claude Code environment
37
37
  - **[Gemini CLI Integration](https://zitniklab.hms.harvard.edu/bioagent/guide/building_ai_scientists/gemini_cli.html)**: Command-line scientific research with Google Gemini
@@ -44,7 +44,7 @@ ToolUniverse is an ecosystem for creating AI scientist systems from any open or
44
44
 
45
45
  AI scientists are emerging computational systems that serve as collaborative partners in discovery. However, these systems remain difficult to build because they are bespoke, tied to rigid workflows, and lack shared environments that unify tools, data, and analysts into a common ecosystem.
46
46
 
47
- ToolUniverse addresses this challenge by providing a standardized ecosystem that transforms any AI model into a powerful research scientist. By abstracting capabilities behind a unified interface, ToolUniverse wraps around any AI model (LLM, AI agent, or large reasoning model) and enables users to create and refine entirely custom AI research assistants without additional training or finetuning.
47
+ ToolUniverse addresses this challenge by providing a standardized ecosystem that transforms any AI model into a powerful research scientist. By abstracting capabilities behind a unified interface, ToolUniverse wraps around any AI model (LLM, AI agent, or large reasoning model) and enables users to create and refine entirely custom AI scientists without additional training or finetuning.
48
48
 
49
49
  **Key Features:**
50
50
 
@@ -147,7 +147,7 @@ tooluniverse-smcp
147
147
 
148
148
 
149
149
  ---
150
- **Hypercholesterolemia Drug Discovery** [[Tutorial]]((https://zitniklab.hms.harvard.edu/bioagent/tutorials/tooluniverse_case_study.html)) [[Code]](https://colab.research.google.com/drive/1UwJ6RwyUoqI5risKQ365EeFdDQWOeOCv?usp=sharing)
150
+ **Hypercholesterolemia Drug Discovery** [[Tutorial]](https://zitniklab.hms.harvard.edu/bioagent/tutorials/tooluniverse_case_study.html) [[Code]](https://colab.research.google.com/drive/1UwJ6RwyUoqI5risKQ365EeFdDQWOeOCv?usp=sharing)
151
151
 
152
152
  ---
153
153
 
@@ -251,4 +251,4 @@ ToolUniverse is developed by the [Zitnik Lab](https://zitniklab.hms.harvard.edu/
251
251
 
252
252
  ---
253
253
 
254
- *Democratizing AI agents for science - one tooluniverse at a time.*
254
+ *Democratizing AI agents for science with ToolUniverse.*
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "tooluniverse"
7
- version = "1.0.2"
7
+ version = "1.0.4"
8
8
  description = "A comprehensive collection of scientific tools for Agentic AI, offering integration with the ToolUniverse SDK and MCP Server to support advanced scientific workflows."
9
9
  authors = [
10
10
  { name = "Shanghua Gao", email = "shanghuagao@gmail.com" }
@@ -21,6 +21,7 @@ dependencies = [
21
21
  "openai>=1.107.0",
22
22
  "pyyaml>=6.0.0",
23
23
  "google-genai>=1.36.0",
24
+ "google-generativeai>=0.7.2",
24
25
  "mcp[cli]>=1.9.3",
25
26
  "fastmcp>=2.12.0",
26
27
  "xmltodict>=1.0.0",
@@ -0,0 +1,495 @@
1
+ from __future__ import annotations
2
+
3
+ import os
4
+ import json
5
+ from datetime import datetime
6
+ from typing import Any, Dict, List, Optional
7
+
8
+ from .base_tool import BaseTool
9
+ from .tool_registry import register_tool
10
+ from .logging_config import get_logger
11
+ from .llm_clients import AzureOpenAIClient, GeminiClient
12
+
13
+
14
+ # Global default fallback configuration
15
+ DEFAULT_FALLBACK_CHAIN = [
16
+ {"api_type": "CHATGPT", "model_id": "gpt-4o-1120"},
17
+ {"api_type": "GEMINI", "model_id": "gemini-2.0-flash"},
18
+ ]
19
+
20
+ # API key environment variable mapping
21
+ API_KEY_ENV_VARS = {
22
+ "CHATGPT": ["AZURE_OPENAI_API_KEY", "AZURE_OPENAI_ENDPOINT"],
23
+ "GEMINI": ["GEMINI_API_KEY"],
24
+ }
25
+
26
+
27
+ @register_tool("AgenticTool")
28
+ class AgenticTool(BaseTool):
29
+ """Generic wrapper around LLM prompting supporting JSON-defined configs with prompts and input arguments."""
30
+
31
+ @staticmethod
32
+ def has_any_api_keys() -> bool:
33
+ """
34
+ Check if any API keys are available across all supported API types.
35
+
36
+ Returns:
37
+ bool: True if at least one API type has all required keys, False otherwise
38
+ """
39
+ for _api_type, required_vars in API_KEY_ENV_VARS.items():
40
+ all_keys_present = True
41
+ for var in required_vars:
42
+ if not os.getenv(var):
43
+ all_keys_present = False
44
+ break
45
+ if all_keys_present:
46
+ return True
47
+ return False
48
+
49
+ def __init__(self, tool_config: Dict[str, Any]):
50
+ super().__init__(tool_config)
51
+ self.logger = get_logger("AgenticTool")
52
+ self.name: str = tool_config.get("name", "")
53
+ self._prompt_template: str = tool_config.get("prompt", "")
54
+ self._input_arguments: List[str] = tool_config.get("input_arguments", [])
55
+
56
+ # Extract required arguments from parameter schema
57
+ parameter_info = tool_config.get("parameter", {})
58
+ self._required_arguments: List[str] = parameter_info.get("required", [])
59
+ self._argument_defaults: Dict[str, str] = {}
60
+
61
+ # Set up default values for optional arguments
62
+ properties = parameter_info.get("properties", {})
63
+ for arg in self._input_arguments:
64
+ if arg not in self._required_arguments:
65
+ prop_info = properties.get(arg, {})
66
+ if "default" in prop_info:
67
+ self._argument_defaults[arg] = prop_info["default"]
68
+
69
+ # Get configuration from nested 'configs' dict or fallback to top-level
70
+ configs = tool_config.get("configs", {})
71
+
72
+ # Helper function to get config values with fallback
73
+ def get_config(key: str, default: Any) -> Any:
74
+ return configs.get(key, tool_config.get(key, default))
75
+
76
+ # LLM configuration
77
+ self._api_type: str = get_config("api_type", "CHATGPT")
78
+ self._model_id: str = get_config("model_id", "o1-mini")
79
+ self._temperature: Optional[float] = get_config("temperature", 0.1)
80
+ # Ignore configured max_new_tokens; client will resolve per model/env
81
+ self._max_new_tokens: Optional[int] = None
82
+ self._return_json: bool = get_config("return_json", False)
83
+ self._max_retries: int = get_config("max_retries", 5)
84
+ self._retry_delay: int = get_config("retry_delay", 5)
85
+ self.return_metadata: bool = get_config("return_metadata", True)
86
+ self._validate_api_key: bool = get_config("validate_api_key", True)
87
+
88
+ # API fallback configuration
89
+ self._fallback_api_type: Optional[str] = get_config("fallback_api_type", None)
90
+ self._fallback_model_id: Optional[str] = get_config("fallback_model_id", None)
91
+
92
+ # Global fallback configuration
93
+ self._use_global_fallback: bool = get_config("use_global_fallback", True)
94
+ self._global_fallback_chain: List[Dict[str, str]] = (
95
+ self._get_global_fallback_chain()
96
+ )
97
+
98
+ # Gemini model configuration (optional; env override)
99
+ self._gemini_model_id: str = get_config(
100
+ "gemini_model_id",
101
+ __import__("os").getenv("GEMINI_MODEL_ID", "gemini-2.0-flash"),
102
+ )
103
+
104
+ # Validation
105
+ if not self._prompt_template:
106
+ raise ValueError("AgenticTool requires a 'prompt' in the configuration.")
107
+ if not self._input_arguments:
108
+ raise ValueError(
109
+ "AgenticTool requires 'input_arguments' in the configuration."
110
+ )
111
+
112
+ # Validate temperature range (skip if None)
113
+ if (
114
+ isinstance(self._temperature, (int, float))
115
+ and not 0 <= self._temperature <= 2
116
+ ):
117
+ self.logger.warning(
118
+ f"Temperature {self._temperature} is outside recommended range [0, 2]"
119
+ )
120
+
121
+ # Validate model compatibility
122
+ self._validate_model_config()
123
+
124
+ # Initialize the provider client
125
+ self._llm_client = None
126
+ self._initialization_error = None
127
+ self._is_available = False
128
+ self._current_api_type = None
129
+ self._current_model_id = None
130
+
131
+ # Try primary API first, then fallback if configured
132
+ self._try_initialize_api()
133
+
134
+ def _get_global_fallback_chain(self) -> List[Dict[str, str]]:
135
+ """Get the global fallback chain from environment or use default."""
136
+ # Check environment variable for custom fallback chain
137
+ env_chain = os.getenv("AGENTIC_TOOL_FALLBACK_CHAIN")
138
+ if env_chain:
139
+ try:
140
+ chain = json.loads(env_chain)
141
+ if isinstance(chain, list) and all(
142
+ isinstance(item, dict) and "api_type" in item and "model_id" in item
143
+ for item in chain
144
+ ):
145
+ return chain
146
+ else:
147
+ self.logger.warning(
148
+ "Invalid fallback chain format in environment variable"
149
+ )
150
+ except json.JSONDecodeError:
151
+ self.logger.warning(
152
+ "Invalid JSON in AGENTIC_TOOL_FALLBACK_CHAIN environment variable"
153
+ )
154
+
155
+ return DEFAULT_FALLBACK_CHAIN.copy()
156
+
157
+ def _try_initialize_api(self):
158
+ """Try to initialize the primary API, fallback to secondary if configured."""
159
+ # Try primary API first
160
+ if self._try_api(self._api_type, self._model_id):
161
+ return
162
+
163
+ # Try explicit fallback API if configured
164
+ if self._fallback_api_type and self._fallback_model_id:
165
+ self.logger.info(
166
+ f"Primary API {self._api_type} failed, trying explicit fallback {self._fallback_api_type}"
167
+ )
168
+ if self._try_api(self._fallback_api_type, self._fallback_model_id):
169
+ return
170
+
171
+ # Try global fallback chain if enabled
172
+ if self._use_global_fallback:
173
+ self.logger.info(
174
+ f"Primary API {self._api_type} failed, trying global fallback chain"
175
+ )
176
+ for fallback_config in self._global_fallback_chain:
177
+ fallback_api = fallback_config["api_type"]
178
+ fallback_model = fallback_config["model_id"]
179
+
180
+ # Skip if it's the same as primary or explicit fallback
181
+ if (
182
+ fallback_api == self._api_type and fallback_model == self._model_id
183
+ ) or (
184
+ fallback_api == self._fallback_api_type
185
+ and fallback_model == self._fallback_model_id
186
+ ):
187
+ continue
188
+
189
+ self.logger.info(
190
+ f"Trying global fallback: {fallback_api} ({fallback_model})"
191
+ )
192
+ if self._try_api(fallback_api, fallback_model):
193
+ return
194
+
195
+ # If we get here, all APIs failed
196
+ self.logger.warning(
197
+ f"Tool '{self.name}' failed to initialize with all available APIs"
198
+ )
199
+
200
+ def _try_api(self, api_type: str, model_id: str) -> bool:
201
+ """Try to initialize a specific API and model."""
202
+ try:
203
+ if api_type == "CHATGPT":
204
+ self._llm_client = AzureOpenAIClient(model_id, None, self.logger)
205
+ elif api_type == "GEMINI":
206
+ self._llm_client = GeminiClient(model_id, self.logger)
207
+ else:
208
+ raise ValueError(f"Unsupported API type: {api_type}")
209
+
210
+ # Test API key validity after initialization (if enabled)
211
+ if self._validate_api_key:
212
+ self._llm_client.test_api()
213
+ self.logger.debug(
214
+ f"Successfully initialized {api_type} model: {model_id}"
215
+ )
216
+ else:
217
+ self.logger.info("API key validation skipped (validate_api_key=False)")
218
+
219
+ self._is_available = True
220
+ self._current_api_type = api_type
221
+ self._current_model_id = model_id
222
+ self._initialization_error = None
223
+
224
+ if api_type != self._api_type or model_id != self._model_id:
225
+ self.logger.info(
226
+ f"Using fallback API: {api_type} with model {model_id} "
227
+ f"(originally configured: {self._api_type} with {self._model_id})"
228
+ )
229
+
230
+ return True
231
+
232
+ except Exception as e:
233
+ error_msg = f"Failed to initialize {api_type} model {model_id}: {str(e)}"
234
+ self.logger.warning(error_msg)
235
+ self._initialization_error = error_msg
236
+ return False
237
+
238
+ # ------------------------------------------------------------------ LLM utilities -----------
239
+ def _validate_model_config(self):
240
+ supported_api_types = ["CHATGPT", "GEMINI"]
241
+ if self._api_type not in supported_api_types:
242
+ raise ValueError(
243
+ f"Unsupported API type: {self._api_type}. Supported types: {supported_api_types}"
244
+ )
245
+ if self._max_new_tokens is not None and self._max_new_tokens <= 0:
246
+ raise ValueError("max_new_tokens must be positive or None")
247
+
248
+ # ------------------------------------------------------------------ public API --------------
249
+ def run(self, arguments: Dict[str, Any]) -> Dict[str, Any]:
250
+ start_time = datetime.now()
251
+
252
+ # Check if tool is available before attempting to run
253
+ if not self._is_available:
254
+ error_msg = f"Tool '{self.name}' is not available due to initialization error: {self._initialization_error}"
255
+ self.logger.error(error_msg)
256
+ if self.return_metadata:
257
+ return {
258
+ "success": False,
259
+ "error": error_msg,
260
+ "error_type": "ToolUnavailable",
261
+ "metadata": {
262
+ "prompt_used": "Tool unavailable",
263
+ "input_arguments": {
264
+ arg: arguments.get(arg) for arg in self._input_arguments
265
+ },
266
+ "model_info": {
267
+ "api_type": self._api_type,
268
+ "model_id": self._model_id,
269
+ },
270
+ "execution_time_seconds": 0,
271
+ "timestamp": start_time.isoformat(),
272
+ },
273
+ }
274
+ else:
275
+ return f"error: {error_msg} error_type: ToolUnavailable"
276
+
277
+ try:
278
+ # Validate required args
279
+ missing_required_args = [
280
+ arg for arg in self._required_arguments if arg not in arguments
281
+ ]
282
+ if missing_required_args:
283
+ raise ValueError(
284
+ f"Missing required input arguments: {missing_required_args}"
285
+ )
286
+
287
+ # Fill defaults for optional args
288
+ for arg in self._input_arguments:
289
+ if arg not in arguments:
290
+ arguments[arg] = self._argument_defaults.get(arg, "")
291
+
292
+ self._validate_arguments(arguments)
293
+ formatted_prompt = self._format_prompt(arguments)
294
+
295
+ messages = [{"role": "user", "content": formatted_prompt}]
296
+ custom_format = arguments.get("response_format", None)
297
+
298
+ # Delegate to client; client handles provider-specific logic
299
+ response = self._llm_client.infer(
300
+ messages=messages,
301
+ temperature=self._temperature,
302
+ max_tokens=None, # client resolves per-model defaults/env
303
+ return_json=self._return_json,
304
+ custom_format=custom_format,
305
+ max_retries=self._max_retries,
306
+ retry_delay=self._retry_delay,
307
+ )
308
+
309
+ end_time = datetime.now()
310
+ execution_time = (end_time - start_time).total_seconds()
311
+
312
+ if self.return_metadata:
313
+ return {
314
+ "success": True,
315
+ "result": response,
316
+ "metadata": {
317
+ "prompt_used": (
318
+ formatted_prompt
319
+ if len(formatted_prompt) < 1000
320
+ else f"{formatted_prompt[:1000]}..."
321
+ ),
322
+ "input_arguments": {
323
+ arg: arguments.get(arg) for arg in self._input_arguments
324
+ },
325
+ "model_info": {
326
+ "api_type": self._api_type,
327
+ "model_id": self._model_id,
328
+ "temperature": self._temperature,
329
+ "max_new_tokens": self._max_new_tokens,
330
+ },
331
+ "execution_time_seconds": execution_time,
332
+ "timestamp": start_time.isoformat(),
333
+ },
334
+ }
335
+ else:
336
+ return response
337
+ except Exception as e:
338
+ end_time = datetime.now()
339
+ execution_time = (end_time - start_time).total_seconds()
340
+ self.logger.error(f"Error executing {self.name}: {str(e)}")
341
+ if self.return_metadata:
342
+ return {
343
+ "success": False,
344
+ "error": str(e),
345
+ "error_type": type(e).__name__,
346
+ "metadata": {
347
+ "prompt_used": (
348
+ formatted_prompt
349
+ if "formatted_prompt" in locals()
350
+ else "Failed to format prompt"
351
+ ),
352
+ "input_arguments": {
353
+ arg: arguments.get(arg) for arg in self._input_arguments
354
+ },
355
+ "model_info": {
356
+ "api_type": self._api_type,
357
+ "model_id": self._model_id,
358
+ },
359
+ "execution_time_seconds": execution_time,
360
+ "timestamp": start_time.isoformat(),
361
+ },
362
+ }
363
+ else:
364
+ return "error: " + str(e) + " error_type: " + type(e).__name__
365
+
366
+ # ------------------------------------------------------------------ helpers -----------------
367
+ def _validate_arguments(self, arguments: Dict[str, Any]):
368
+ for arg_name, value in arguments.items():
369
+ if arg_name in self._input_arguments:
370
+ if isinstance(value, str) and not value.strip():
371
+ if arg_name in self._required_arguments:
372
+ raise ValueError(
373
+ f"Required argument '{arg_name}' cannot be empty"
374
+ )
375
+ if isinstance(value, str) and len(value) > 100000:
376
+ pass
377
+
378
+ def _format_prompt(self, arguments: Dict[str, Any]) -> str:
379
+ prompt = self._prompt_template
380
+ for arg_name in self._input_arguments:
381
+ placeholder = f"{{{arg_name}}}"
382
+ value = arguments.get(arg_name, "")
383
+ if placeholder in prompt:
384
+ prompt = prompt.replace(placeholder, str(value))
385
+ return prompt
386
+
387
+ def get_prompt_preview(self, arguments: Dict[str, Any]) -> str:
388
+ try:
389
+ args_copy = arguments.copy()
390
+ missing_required_args = [
391
+ arg for arg in self._required_arguments if arg not in args_copy
392
+ ]
393
+ if missing_required_args:
394
+ raise ValueError(
395
+ f"Missing required input arguments: {missing_required_args}"
396
+ )
397
+ for arg in self._input_arguments:
398
+ if arg not in args_copy:
399
+ args_copy[arg] = self._argument_defaults.get(arg, "")
400
+ return self._format_prompt(args_copy)
401
+ except Exception as e:
402
+ return f"Error formatting prompt: {str(e)}"
403
+
404
+ def get_model_info(self) -> Dict[str, Any]:
405
+ return {
406
+ "api_type": self._api_type,
407
+ "model_id": self._model_id,
408
+ "temperature": self._temperature,
409
+ "max_new_tokens": self._max_new_tokens,
410
+ "return_json": self._return_json,
411
+ "max_retries": self._max_retries,
412
+ "retry_delay": self._retry_delay,
413
+ "validate_api_key": self._validate_api_key,
414
+ "gemini_model_id": getattr(self, "_gemini_model_id", None),
415
+ "is_available": self._is_available,
416
+ "initialization_error": self._initialization_error,
417
+ "current_api_type": self._current_api_type,
418
+ "current_model_id": self._current_model_id,
419
+ "fallback_api_type": self._fallback_api_type,
420
+ "fallback_model_id": self._fallback_model_id,
421
+ "use_global_fallback": self._use_global_fallback,
422
+ "global_fallback_chain": self._global_fallback_chain,
423
+ }
424
+
425
+ def is_available(self) -> bool:
426
+ """Check if the tool is available for use."""
427
+ return self._is_available
428
+
429
+ def get_availability_status(self) -> Dict[str, Any]:
430
+ """Get detailed availability status of the tool."""
431
+ return {
432
+ "is_available": self._is_available,
433
+ "initialization_error": self._initialization_error,
434
+ "api_type": self._api_type,
435
+ "model_id": self._model_id,
436
+ }
437
+
438
+ def retry_initialization(self) -> bool:
439
+ """Attempt to reinitialize the tool (useful if API keys were updated)."""
440
+ try:
441
+ if self._api_type == "CHATGPT":
442
+ self._llm_client = AzureOpenAIClient(self._model_id, None, self.logger)
443
+ elif self._api_type == "GEMINI":
444
+ self._llm_client = GeminiClient(self._gemini_model_id, self.logger)
445
+ else:
446
+ raise ValueError(f"Unsupported API type: {self._api_type}")
447
+
448
+ if self._validate_api_key:
449
+ self._llm_client.test_api()
450
+ self.logger.info(
451
+ f"Successfully reinitialized {self._api_type} model: {self._model_id}"
452
+ )
453
+
454
+ self._is_available = True
455
+ self._initialization_error = None
456
+ return True
457
+
458
+ except Exception as e:
459
+ self._initialization_error = str(e)
460
+ self.logger.warning(
461
+ f"Retry initialization failed for {self._api_type} model {self._model_id}: {str(e)}"
462
+ )
463
+ return False
464
+
465
+ def get_prompt_template(self) -> str:
466
+ return self._prompt_template
467
+
468
+ def get_input_arguments(self) -> List[str]:
469
+ return self._input_arguments.copy()
470
+
471
+ def validate_configuration(self) -> Dict[str, Any]:
472
+ validation_results = {"valid": True, "warnings": [], "errors": []}
473
+ try:
474
+ self._validate_model_config()
475
+ except ValueError as e:
476
+ validation_results["valid"] = False
477
+ validation_results["errors"].append(str(e))
478
+ if not self._prompt_template:
479
+ validation_results["valid"] = False
480
+ validation_results["errors"].append("Missing prompt template")
481
+ return validation_results
482
+
483
+ def estimate_token_usage(self, arguments: Dict[str, Any]) -> Dict[str, int]:
484
+ prompt = self._format_prompt(arguments)
485
+ estimated_input_tokens = len(prompt) // 4
486
+ estimated_max_output_tokens = (
487
+ self._max_new_tokens if self._max_new_tokens is not None else 2048
488
+ )
489
+ estimated_total_tokens = estimated_input_tokens + estimated_max_output_tokens
490
+ return {
491
+ "estimated_input_tokens": estimated_input_tokens,
492
+ "max_output_tokens": estimated_max_output_tokens,
493
+ "estimated_total_tokens": estimated_total_tokens,
494
+ "prompt_length_chars": len(prompt),
495
+ }
@@ -81,31 +81,37 @@ def compose(arguments: Dict[str, Any], tooluniverse, call_tool) -> Dict[str, Any
81
81
  else:
82
82
  print(f"❌ Chunk {i+1} summarization failed")
83
83
 
84
- # Step 3: Merge summaries
84
+ # Step 3: Merge summaries (or gracefully fall back)
85
85
  if chunk_summaries:
86
86
  final_summary = _merge_summaries(
87
87
  chunk_summaries, query_context, tool_name, max_summary_length, call_tool
88
88
  )
89
+ print(
90
+ f"✅ Summarization completed. Final length: {len(final_summary)} characters"
91
+ )
92
+ return {
93
+ "success": True,
94
+ "original_length": len(tool_output),
95
+ "summary_length": len(final_summary),
96
+ "chunks_processed": len(chunks),
97
+ "summary": final_summary,
98
+ "tool_name": tool_name,
99
+ }
89
100
  else:
90
- final_summary = "Unable to generate summary due to processing errors."
101
+ # Treat as a non-fatal failure so upstream falls back to original output
91
102
  print("❌ No chunk summaries were generated. This usually indicates:")
92
103
  print(" 1. ToolOutputSummarizer tool is not available")
93
104
  print(" 2. The output_summarization tools are not loaded")
94
105
  print(" 3. There was an error in the summarization process")
95
106
  print(" Please check that the SMCP server is started with hooks enabled.")
96
-
97
- print(
98
- f" Summarization completed. Final length: {len(final_summary)} characters"
99
- )
100
-
101
- return {
102
- "success": True,
103
- "original_length": len(tool_output),
104
- "summary_length": len(final_summary),
105
- "chunks_processed": len(chunks),
106
- "summary": final_summary,
107
- "tool_name": tool_name,
108
- }
107
+ return {
108
+ "success": False,
109
+ "error": "No chunk summaries generated",
110
+ "original_length": len(tool_output),
111
+ "chunks_processed": len(chunks),
112
+ "original_output": tool_output,
113
+ "tool_name": tool_name,
114
+ }
109
115
 
110
116
  except Exception as e:
111
117
  error_msg = f"Error in output summarization: {str(e)}"