unique_toolkit 1.68.12__tar.gz → 1.69.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (297) hide show
  1. {unique_toolkit-1.68.12 → unique_toolkit-1.69.0}/PKG-INFO +3 -1
  2. {unique_toolkit-1.68.12 → unique_toolkit-1.69.0}/pyproject.toml +4 -1
  3. {unique_toolkit-1.68.12 → unique_toolkit-1.69.0}/unique_toolkit/agentic/tools/openai_builtin/code_interpreter/postprocessors/generated_files.py +84 -0
  4. {unique_toolkit-1.68.12 → unique_toolkit-1.69.0}/unique_toolkit/app/fast_api_factory.py +15 -0
  5. unique_toolkit-1.69.0/unique_toolkit/monitoring/__init__.py +39 -0
  6. unique_toolkit-1.69.0/unique_toolkit/monitoring/middleware.py +90 -0
  7. unique_toolkit-1.69.0/unique_toolkit/monitoring/registry.py +107 -0
  8. {unique_toolkit-1.68.12 → unique_toolkit-1.69.0}/README.md +0 -0
  9. {unique_toolkit-1.68.12 → unique_toolkit-1.69.0}/unique_toolkit/__init__.py +0 -0
  10. {unique_toolkit-1.68.12 → unique_toolkit-1.69.0}/unique_toolkit/_common/__init__.py +0 -0
  11. {unique_toolkit-1.68.12 → unique_toolkit-1.69.0}/unique_toolkit/_common/_base_service.py +0 -0
  12. {unique_toolkit-1.68.12 → unique_toolkit-1.69.0}/unique_toolkit/_common/_time_utils.py +0 -0
  13. {unique_toolkit-1.68.12 → unique_toolkit-1.69.0}/unique_toolkit/_common/api_calling/human_verification_manager.py +0 -0
  14. {unique_toolkit-1.68.12 → unique_toolkit-1.69.0}/unique_toolkit/_common/base_model_type_attribute.py +0 -0
  15. {unique_toolkit-1.68.12 → unique_toolkit-1.69.0}/unique_toolkit/_common/chunk_relevancy_sorter/config.py +0 -0
  16. {unique_toolkit-1.68.12 → unique_toolkit-1.69.0}/unique_toolkit/_common/chunk_relevancy_sorter/exception.py +0 -0
  17. {unique_toolkit-1.68.12 → unique_toolkit-1.69.0}/unique_toolkit/_common/chunk_relevancy_sorter/schemas.py +0 -0
  18. {unique_toolkit-1.68.12 → unique_toolkit-1.69.0}/unique_toolkit/_common/chunk_relevancy_sorter/service.py +0 -0
  19. {unique_toolkit-1.68.12 → unique_toolkit-1.69.0}/unique_toolkit/_common/chunk_relevancy_sorter/tests/test_service.py +0 -0
  20. {unique_toolkit-1.68.12 → unique_toolkit-1.69.0}/unique_toolkit/_common/config_checker/README.md +0 -0
  21. {unique_toolkit-1.68.12 → unique_toolkit-1.69.0}/unique_toolkit/_common/config_checker/__init__.py +0 -0
  22. {unique_toolkit-1.68.12 → unique_toolkit-1.69.0}/unique_toolkit/_common/config_checker/__main__.py +0 -0
  23. {unique_toolkit-1.68.12 → unique_toolkit-1.69.0}/unique_toolkit/_common/config_checker/cli.py +0 -0
  24. {unique_toolkit-1.68.12 → unique_toolkit-1.69.0}/unique_toolkit/_common/config_checker/differ.py +0 -0
  25. {unique_toolkit-1.68.12 → unique_toolkit-1.69.0}/unique_toolkit/_common/config_checker/exporter.py +0 -0
  26. {unique_toolkit-1.68.12 → unique_toolkit-1.69.0}/unique_toolkit/_common/config_checker/models.py +0 -0
  27. {unique_toolkit-1.68.12 → unique_toolkit-1.69.0}/unique_toolkit/_common/config_checker/registry.py +0 -0
  28. {unique_toolkit-1.68.12 → unique_toolkit-1.69.0}/unique_toolkit/_common/config_checker/validator.py +0 -0
  29. {unique_toolkit-1.68.12 → unique_toolkit-1.69.0}/unique_toolkit/_common/default_language_model.py +0 -0
  30. {unique_toolkit-1.68.12 → unique_toolkit-1.69.0}/unique_toolkit/_common/docx_generator/__init__.py +0 -0
  31. {unique_toolkit-1.68.12 → unique_toolkit-1.69.0}/unique_toolkit/_common/docx_generator/config.py +0 -0
  32. {unique_toolkit-1.68.12 → unique_toolkit-1.69.0}/unique_toolkit/_common/docx_generator/pandoc_converter.py +0 -0
  33. {unique_toolkit-1.68.12 → unique_toolkit-1.69.0}/unique_toolkit/_common/docx_generator/schemas.py +0 -0
  34. {unique_toolkit-1.68.12 → unique_toolkit-1.69.0}/unique_toolkit/_common/docx_generator/service.py +0 -0
  35. {unique_toolkit-1.68.12 → unique_toolkit-1.69.0}/unique_toolkit/_common/docx_generator/template/Doc Template.docx +0 -0
  36. {unique_toolkit-1.68.12 → unique_toolkit-1.69.0}/unique_toolkit/_common/endpoint_builder.py +0 -0
  37. {unique_toolkit-1.68.12 → unique_toolkit-1.69.0}/unique_toolkit/_common/endpoint_requestor.py +0 -0
  38. {unique_toolkit-1.68.12 → unique_toolkit-1.69.0}/unique_toolkit/_common/event_bus.py +0 -0
  39. {unique_toolkit-1.68.12 → unique_toolkit-1.69.0}/unique_toolkit/_common/exception.py +0 -0
  40. {unique_toolkit-1.68.12 → unique_toolkit-1.69.0}/unique_toolkit/_common/execution.py +0 -0
  41. {unique_toolkit-1.68.12 → unique_toolkit-1.69.0}/unique_toolkit/_common/experimental/endpoint_builder.py +0 -0
  42. {unique_toolkit-1.68.12 → unique_toolkit-1.69.0}/unique_toolkit/_common/experimental/endpoint_requestor.py +0 -0
  43. {unique_toolkit-1.68.12 → unique_toolkit-1.69.0}/unique_toolkit/_common/experimental/write_up_agent/README.md +0 -0
  44. {unique_toolkit-1.68.12 → unique_toolkit-1.69.0}/unique_toolkit/_common/experimental/write_up_agent/__init__.py +0 -0
  45. {unique_toolkit-1.68.12 → unique_toolkit-1.69.0}/unique_toolkit/_common/experimental/write_up_agent/agent.py +0 -0
  46. {unique_toolkit-1.68.12 → unique_toolkit-1.69.0}/unique_toolkit/_common/experimental/write_up_agent/config.py +0 -0
  47. {unique_toolkit-1.68.12 → unique_toolkit-1.69.0}/unique_toolkit/_common/experimental/write_up_agent/examples/data.csv +0 -0
  48. {unique_toolkit-1.68.12 → unique_toolkit-1.69.0}/unique_toolkit/_common/experimental/write_up_agent/examples/example_usage.py +0 -0
  49. {unique_toolkit-1.68.12 → unique_toolkit-1.69.0}/unique_toolkit/_common/experimental/write_up_agent/schemas.py +0 -0
  50. {unique_toolkit-1.68.12 → unique_toolkit-1.69.0}/unique_toolkit/_common/experimental/write_up_agent/services/__init__.py +0 -0
  51. {unique_toolkit-1.68.12 → unique_toolkit-1.69.0}/unique_toolkit/_common/experimental/write_up_agent/services/dataframe_handler/__init__.py +0 -0
  52. {unique_toolkit-1.68.12 → unique_toolkit-1.69.0}/unique_toolkit/_common/experimental/write_up_agent/services/dataframe_handler/exceptions.py +0 -0
  53. {unique_toolkit-1.68.12 → unique_toolkit-1.69.0}/unique_toolkit/_common/experimental/write_up_agent/services/dataframe_handler/service.py +0 -0
  54. {unique_toolkit-1.68.12 → unique_toolkit-1.69.0}/unique_toolkit/_common/experimental/write_up_agent/services/dataframe_handler/utils.py +0 -0
  55. {unique_toolkit-1.68.12 → unique_toolkit-1.69.0}/unique_toolkit/_common/experimental/write_up_agent/services/generation_handler/__init__.py +0 -0
  56. {unique_toolkit-1.68.12 → unique_toolkit-1.69.0}/unique_toolkit/_common/experimental/write_up_agent/services/generation_handler/config.py +0 -0
  57. {unique_toolkit-1.68.12 → unique_toolkit-1.69.0}/unique_toolkit/_common/experimental/write_up_agent/services/generation_handler/exceptions.py +0 -0
  58. {unique_toolkit-1.68.12 → unique_toolkit-1.69.0}/unique_toolkit/_common/experimental/write_up_agent/services/generation_handler/prompts/config.py +0 -0
  59. {unique_toolkit-1.68.12 → unique_toolkit-1.69.0}/unique_toolkit/_common/experimental/write_up_agent/services/generation_handler/prompts/system_prompt.j2 +0 -0
  60. {unique_toolkit-1.68.12 → unique_toolkit-1.69.0}/unique_toolkit/_common/experimental/write_up_agent/services/generation_handler/prompts/user_prompt.j2 +0 -0
  61. {unique_toolkit-1.68.12 → unique_toolkit-1.69.0}/unique_toolkit/_common/experimental/write_up_agent/services/generation_handler/service.py +0 -0
  62. {unique_toolkit-1.68.12 → unique_toolkit-1.69.0}/unique_toolkit/_common/experimental/write_up_agent/services/template_handler/__init__.py +0 -0
  63. {unique_toolkit-1.68.12 → unique_toolkit-1.69.0}/unique_toolkit/_common/experimental/write_up_agent/services/template_handler/default_template.j2 +0 -0
  64. {unique_toolkit-1.68.12 → unique_toolkit-1.69.0}/unique_toolkit/_common/experimental/write_up_agent/services/template_handler/exceptions.py +0 -0
  65. {unique_toolkit-1.68.12 → unique_toolkit-1.69.0}/unique_toolkit/_common/experimental/write_up_agent/services/template_handler/service.py +0 -0
  66. {unique_toolkit-1.68.12 → unique_toolkit-1.69.0}/unique_toolkit/_common/experimental/write_up_agent/services/template_handler/utils.py +0 -0
  67. {unique_toolkit-1.68.12 → unique_toolkit-1.69.0}/unique_toolkit/_common/experimental/write_up_agent/utils.py +0 -0
  68. {unique_toolkit-1.68.12 → unique_toolkit-1.69.0}/unique_toolkit/_common/feature_flags/schema.py +0 -0
  69. {unique_toolkit-1.68.12 → unique_toolkit-1.69.0}/unique_toolkit/_common/pydantic/rjsf_tags.py +0 -0
  70. {unique_toolkit-1.68.12 → unique_toolkit-1.69.0}/unique_toolkit/_common/pydantic_helpers.py +0 -0
  71. {unique_toolkit-1.68.12 → unique_toolkit-1.69.0}/unique_toolkit/_common/referencing.py +0 -0
  72. {unique_toolkit-1.68.12 → unique_toolkit-1.69.0}/unique_toolkit/_common/string_utilities.py +0 -0
  73. {unique_toolkit-1.68.12 → unique_toolkit-1.69.0}/unique_toolkit/_common/tests/test_referencing.py +0 -0
  74. {unique_toolkit-1.68.12 → unique_toolkit-1.69.0}/unique_toolkit/_common/tests/test_string_utilities.py +0 -0
  75. {unique_toolkit-1.68.12 → unique_toolkit-1.69.0}/unique_toolkit/_common/token/__init__.py +0 -0
  76. {unique_toolkit-1.68.12 → unique_toolkit-1.69.0}/unique_toolkit/_common/token/image_token_counting.py +0 -0
  77. {unique_toolkit-1.68.12 → unique_toolkit-1.69.0}/unique_toolkit/_common/token/token_counting.py +0 -0
  78. {unique_toolkit-1.68.12 → unique_toolkit-1.69.0}/unique_toolkit/_common/token/tokenizers/deepseek/tokenizer.json +0 -0
  79. {unique_toolkit-1.68.12 → unique_toolkit-1.69.0}/unique_toolkit/_common/token/tokenizers/qwen/tokenizer.json +0 -0
  80. {unique_toolkit-1.68.12 → unique_toolkit-1.69.0}/unique_toolkit/_common/utils/__init__.py +0 -0
  81. {unique_toolkit-1.68.12 → unique_toolkit-1.69.0}/unique_toolkit/_common/utils/files.py +0 -0
  82. {unique_toolkit-1.68.12 → unique_toolkit-1.69.0}/unique_toolkit/_common/utils/image/encode.py +0 -0
  83. {unique_toolkit-1.68.12 → unique_toolkit-1.69.0}/unique_toolkit/_common/utils/jinja/helpers.py +0 -0
  84. {unique_toolkit-1.68.12 → unique_toolkit-1.69.0}/unique_toolkit/_common/utils/jinja/render.py +0 -0
  85. {unique_toolkit-1.68.12 → unique_toolkit-1.69.0}/unique_toolkit/_common/utils/jinja/schema.py +0 -0
  86. {unique_toolkit-1.68.12 → unique_toolkit-1.69.0}/unique_toolkit/_common/utils/jinja/utils.py +0 -0
  87. {unique_toolkit-1.68.12 → unique_toolkit-1.69.0}/unique_toolkit/_common/utils/structured_output/__init__.py +0 -0
  88. {unique_toolkit-1.68.12 → unique_toolkit-1.69.0}/unique_toolkit/_common/utils/structured_output/schema.py +0 -0
  89. {unique_toolkit-1.68.12 → unique_toolkit-1.69.0}/unique_toolkit/_common/utils/write_configuration.py +0 -0
  90. {unique_toolkit-1.68.12 → unique_toolkit-1.69.0}/unique_toolkit/_common/validate_required_values.py +0 -0
  91. {unique_toolkit-1.68.12 → unique_toolkit-1.69.0}/unique_toolkit/_common/validators.py +0 -0
  92. {unique_toolkit-1.68.12 → unique_toolkit-1.69.0}/unique_toolkit/agentic/__init__.py +0 -0
  93. {unique_toolkit-1.68.12 → unique_toolkit-1.69.0}/unique_toolkit/agentic/debug_info_manager/debug_info_manager.py +0 -0
  94. {unique_toolkit-1.68.12 → unique_toolkit-1.69.0}/unique_toolkit/agentic/debug_info_manager/test/test_debug_info_manager.py +0 -0
  95. {unique_toolkit-1.68.12 → unique_toolkit-1.69.0}/unique_toolkit/agentic/evaluation/config.py +0 -0
  96. {unique_toolkit-1.68.12 → unique_toolkit-1.69.0}/unique_toolkit/agentic/evaluation/context_relevancy/prompts/__init__.py +0 -0
  97. {unique_toolkit-1.68.12 → unique_toolkit-1.69.0}/unique_toolkit/agentic/evaluation/context_relevancy/prompts/system_prompt.j2 +0 -0
  98. {unique_toolkit-1.68.12 → unique_toolkit-1.69.0}/unique_toolkit/agentic/evaluation/context_relevancy/prompts/user_prompt.j2 +0 -0
  99. {unique_toolkit-1.68.12 → unique_toolkit-1.69.0}/unique_toolkit/agentic/evaluation/context_relevancy/schema.py +0 -0
  100. {unique_toolkit-1.68.12 → unique_toolkit-1.69.0}/unique_toolkit/agentic/evaluation/context_relevancy/service.py +0 -0
  101. {unique_toolkit-1.68.12 → unique_toolkit-1.69.0}/unique_toolkit/agentic/evaluation/evaluation_manager.py +0 -0
  102. {unique_toolkit-1.68.12 → unique_toolkit-1.69.0}/unique_toolkit/agentic/evaluation/exception.py +0 -0
  103. {unique_toolkit-1.68.12 → unique_toolkit-1.69.0}/unique_toolkit/agentic/evaluation/hallucination/constants.py +0 -0
  104. {unique_toolkit-1.68.12 → unique_toolkit-1.69.0}/unique_toolkit/agentic/evaluation/hallucination/hallucination_evaluation.py +0 -0
  105. {unique_toolkit-1.68.12 → unique_toolkit-1.69.0}/unique_toolkit/agentic/evaluation/hallucination/prompts/__init__.py +0 -0
  106. {unique_toolkit-1.68.12 → unique_toolkit-1.69.0}/unique_toolkit/agentic/evaluation/hallucination/prompts/system_prompt.j2 +0 -0
  107. {unique_toolkit-1.68.12 → unique_toolkit-1.69.0}/unique_toolkit/agentic/evaluation/hallucination/prompts/user_prompt.j2 +0 -0
  108. {unique_toolkit-1.68.12 → unique_toolkit-1.69.0}/unique_toolkit/agentic/evaluation/hallucination/service.py +0 -0
  109. {unique_toolkit-1.68.12 → unique_toolkit-1.69.0}/unique_toolkit/agentic/evaluation/hallucination/utils.py +0 -0
  110. {unique_toolkit-1.68.12 → unique_toolkit-1.69.0}/unique_toolkit/agentic/evaluation/output_parser.py +0 -0
  111. {unique_toolkit-1.68.12 → unique_toolkit-1.69.0}/unique_toolkit/agentic/evaluation/schemas.py +0 -0
  112. {unique_toolkit-1.68.12 → unique_toolkit-1.69.0}/unique_toolkit/agentic/evaluation/tests/fixtures.py +0 -0
  113. {unique_toolkit-1.68.12 → unique_toolkit-1.69.0}/unique_toolkit/agentic/evaluation/tests/test_config.py +0 -0
  114. {unique_toolkit-1.68.12 → unique_toolkit-1.69.0}/unique_toolkit/agentic/evaluation/tests/test_context_relevancy_service.py +0 -0
  115. {unique_toolkit-1.68.12 → unique_toolkit-1.69.0}/unique_toolkit/agentic/evaluation/tests/test_hallucination_constants.py +0 -0
  116. {unique_toolkit-1.68.12 → unique_toolkit-1.69.0}/unique_toolkit/agentic/evaluation/tests/test_hallucination_utils.py +0 -0
  117. {unique_toolkit-1.68.12 → unique_toolkit-1.69.0}/unique_toolkit/agentic/evaluation/tests/test_output_parser.py +0 -0
  118. {unique_toolkit-1.68.12 → unique_toolkit-1.69.0}/unique_toolkit/agentic/evaluation/tests/test_prompt_loaders.py +0 -0
  119. {unique_toolkit-1.68.12 → unique_toolkit-1.69.0}/unique_toolkit/agentic/evaluation/utils.py +0 -0
  120. {unique_toolkit-1.68.12 → unique_toolkit-1.69.0}/unique_toolkit/agentic/feature_flags/__init__.py +0 -0
  121. {unique_toolkit-1.68.12 → unique_toolkit-1.69.0}/unique_toolkit/agentic/feature_flags/feature_flags.py +0 -0
  122. {unique_toolkit-1.68.12 → unique_toolkit-1.69.0}/unique_toolkit/agentic/history_manager/history_construction_with_contents.py +0 -0
  123. {unique_toolkit-1.68.12 → unique_toolkit-1.69.0}/unique_toolkit/agentic/history_manager/history_manager.py +0 -0
  124. {unique_toolkit-1.68.12 → unique_toolkit-1.69.0}/unique_toolkit/agentic/history_manager/loop_token_reducer.py +0 -0
  125. {unique_toolkit-1.68.12 → unique_toolkit-1.69.0}/unique_toolkit/agentic/history_manager/utils.py +0 -0
  126. {unique_toolkit-1.68.12 → unique_toolkit-1.69.0}/unique_toolkit/agentic/loop_runner/__init__.py +0 -0
  127. {unique_toolkit-1.68.12 → unique_toolkit-1.69.0}/unique_toolkit/agentic/loop_runner/_iteration_handler_utils.py +0 -0
  128. {unique_toolkit-1.68.12 → unique_toolkit-1.69.0}/unique_toolkit/agentic/loop_runner/_responses_iteration_handler_utils.py +0 -0
  129. {unique_toolkit-1.68.12 → unique_toolkit-1.69.0}/unique_toolkit/agentic/loop_runner/_responses_stream_handler_utils.py +0 -0
  130. {unique_toolkit-1.68.12 → unique_toolkit-1.69.0}/unique_toolkit/agentic/loop_runner/_stream_handler_utils.py +0 -0
  131. {unique_toolkit-1.68.12 → unique_toolkit-1.69.0}/unique_toolkit/agentic/loop_runner/base.py +0 -0
  132. {unique_toolkit-1.68.12 → unique_toolkit-1.69.0}/unique_toolkit/agentic/loop_runner/middleware/__init__.py +0 -0
  133. {unique_toolkit-1.68.12 → unique_toolkit-1.69.0}/unique_toolkit/agentic/loop_runner/middleware/planning/__init__.py +0 -0
  134. {unique_toolkit-1.68.12 → unique_toolkit-1.69.0}/unique_toolkit/agentic/loop_runner/middleware/planning/planning.py +0 -0
  135. {unique_toolkit-1.68.12 → unique_toolkit-1.69.0}/unique_toolkit/agentic/loop_runner/middleware/planning/schema.py +0 -0
  136. {unique_toolkit-1.68.12 → unique_toolkit-1.69.0}/unique_toolkit/agentic/loop_runner/runners/__init__.py +0 -0
  137. {unique_toolkit-1.68.12 → unique_toolkit-1.69.0}/unique_toolkit/agentic/loop_runner/runners/basic.py +0 -0
  138. {unique_toolkit-1.68.12 → unique_toolkit-1.69.0}/unique_toolkit/agentic/loop_runner/runners/mistral/__init__.py +0 -0
  139. {unique_toolkit-1.68.12 → unique_toolkit-1.69.0}/unique_toolkit/agentic/loop_runner/runners/mistral/mistral_runner.py +0 -0
  140. {unique_toolkit-1.68.12 → unique_toolkit-1.69.0}/unique_toolkit/agentic/loop_runner/runners/qwen/__init__.py +0 -0
  141. {unique_toolkit-1.68.12 → unique_toolkit-1.69.0}/unique_toolkit/agentic/loop_runner/runners/qwen/helpers.py +0 -0
  142. {unique_toolkit-1.68.12 → unique_toolkit-1.69.0}/unique_toolkit/agentic/loop_runner/runners/qwen/qwen_runner.py +0 -0
  143. {unique_toolkit-1.68.12 → unique_toolkit-1.69.0}/unique_toolkit/agentic/message_log_manager/__init__.py +0 -0
  144. {unique_toolkit-1.68.12 → unique_toolkit-1.69.0}/unique_toolkit/agentic/message_log_manager/service.py +0 -0
  145. {unique_toolkit-1.68.12 → unique_toolkit-1.69.0}/unique_toolkit/agentic/postprocessor/postprocessor_manager.py +0 -0
  146. {unique_toolkit-1.68.12 → unique_toolkit-1.69.0}/unique_toolkit/agentic/reference_manager/reference_manager.py +0 -0
  147. {unique_toolkit-1.68.12 → unique_toolkit-1.69.0}/unique_toolkit/agentic/responses_api/__init__.py +0 -0
  148. {unique_toolkit-1.68.12 → unique_toolkit-1.69.0}/unique_toolkit/agentic/responses_api/postprocessors/code_display.py +0 -0
  149. {unique_toolkit-1.68.12 → unique_toolkit-1.69.0}/unique_toolkit/agentic/responses_api/postprocessors/generated_files.py +0 -0
  150. {unique_toolkit-1.68.12 → unique_toolkit-1.69.0}/unique_toolkit/agentic/responses_api/stream_handler.py +0 -0
  151. {unique_toolkit-1.68.12 → unique_toolkit-1.69.0}/unique_toolkit/agentic/short_term_memory_manager/persistent_short_term_memory_manager.py +0 -0
  152. {unique_toolkit-1.68.12 → unique_toolkit-1.69.0}/unique_toolkit/agentic/thinking_manager/thinking_manager.py +0 -0
  153. {unique_toolkit-1.68.12 → unique_toolkit-1.69.0}/unique_toolkit/agentic/tools/__init__.py +0 -0
  154. {unique_toolkit-1.68.12 → unique_toolkit-1.69.0}/unique_toolkit/agentic/tools/a2a/__init__.py +0 -0
  155. {unique_toolkit-1.68.12 → unique_toolkit-1.69.0}/unique_toolkit/agentic/tools/a2a/config.py +0 -0
  156. {unique_toolkit-1.68.12 → unique_toolkit-1.69.0}/unique_toolkit/agentic/tools/a2a/evaluation/__init__.py +0 -0
  157. {unique_toolkit-1.68.12 → unique_toolkit-1.69.0}/unique_toolkit/agentic/tools/a2a/evaluation/_utils.py +0 -0
  158. {unique_toolkit-1.68.12 → unique_toolkit-1.69.0}/unique_toolkit/agentic/tools/a2a/evaluation/config.py +0 -0
  159. {unique_toolkit-1.68.12 → unique_toolkit-1.69.0}/unique_toolkit/agentic/tools/a2a/evaluation/evaluator.py +0 -0
  160. {unique_toolkit-1.68.12 → unique_toolkit-1.69.0}/unique_toolkit/agentic/tools/a2a/evaluation/summarization_user_message.j2 +0 -0
  161. {unique_toolkit-1.68.12 → unique_toolkit-1.69.0}/unique_toolkit/agentic/tools/a2a/manager.py +0 -0
  162. {unique_toolkit-1.68.12 → unique_toolkit-1.69.0}/unique_toolkit/agentic/tools/a2a/postprocessing/__init__.py +0 -0
  163. {unique_toolkit-1.68.12 → unique_toolkit-1.69.0}/unique_toolkit/agentic/tools/a2a/postprocessing/_display_utils.py +0 -0
  164. {unique_toolkit-1.68.12 → unique_toolkit-1.69.0}/unique_toolkit/agentic/tools/a2a/postprocessing/_ref_utils.py +0 -0
  165. {unique_toolkit-1.68.12 → unique_toolkit-1.69.0}/unique_toolkit/agentic/tools/a2a/postprocessing/config.py +0 -0
  166. {unique_toolkit-1.68.12 → unique_toolkit-1.69.0}/unique_toolkit/agentic/tools/a2a/postprocessing/display.py +0 -0
  167. {unique_toolkit-1.68.12 → unique_toolkit-1.69.0}/unique_toolkit/agentic/tools/a2a/postprocessing/references.py +0 -0
  168. {unique_toolkit-1.68.12 → unique_toolkit-1.69.0}/unique_toolkit/agentic/tools/a2a/postprocessing/test/test_display.py +0 -0
  169. {unique_toolkit-1.68.12 → unique_toolkit-1.69.0}/unique_toolkit/agentic/tools/a2a/postprocessing/test/test_display_utils.py +0 -0
  170. {unique_toolkit-1.68.12 → unique_toolkit-1.69.0}/unique_toolkit/agentic/tools/a2a/postprocessing/test/test_ref_utils.py +0 -0
  171. {unique_toolkit-1.68.12 → unique_toolkit-1.69.0}/unique_toolkit/agentic/tools/a2a/prompts.py +0 -0
  172. {unique_toolkit-1.68.12 → unique_toolkit-1.69.0}/unique_toolkit/agentic/tools/a2a/response_watcher/__init__.py +0 -0
  173. {unique_toolkit-1.68.12 → unique_toolkit-1.69.0}/unique_toolkit/agentic/tools/a2a/response_watcher/service.py +0 -0
  174. {unique_toolkit-1.68.12 → unique_toolkit-1.69.0}/unique_toolkit/agentic/tools/a2a/tool/__init__.py +0 -0
  175. {unique_toolkit-1.68.12 → unique_toolkit-1.69.0}/unique_toolkit/agentic/tools/a2a/tool/_memory.py +0 -0
  176. {unique_toolkit-1.68.12 → unique_toolkit-1.69.0}/unique_toolkit/agentic/tools/a2a/tool/_schema.py +0 -0
  177. {unique_toolkit-1.68.12 → unique_toolkit-1.69.0}/unique_toolkit/agentic/tools/a2a/tool/config.py +0 -0
  178. {unique_toolkit-1.68.12 → unique_toolkit-1.69.0}/unique_toolkit/agentic/tools/a2a/tool/service.py +0 -0
  179. {unique_toolkit-1.68.12 → unique_toolkit-1.69.0}/unique_toolkit/agentic/tools/a2a/tool/test/test_service_utils.py +0 -0
  180. {unique_toolkit-1.68.12 → unique_toolkit-1.69.0}/unique_toolkit/agentic/tools/agent_chunks_hanlder.py +0 -0
  181. {unique_toolkit-1.68.12 → unique_toolkit-1.69.0}/unique_toolkit/agentic/tools/config.py +0 -0
  182. {unique_toolkit-1.68.12 → unique_toolkit-1.69.0}/unique_toolkit/agentic/tools/experimental/__init__.py +0 -0
  183. {unique_toolkit-1.68.12 → unique_toolkit-1.69.0}/unique_toolkit/agentic/tools/experimental/open_file_tool/__init__.py +0 -0
  184. {unique_toolkit-1.68.12 → unique_toolkit-1.69.0}/unique_toolkit/agentic/tools/experimental/open_file_tool/config.py +0 -0
  185. {unique_toolkit-1.68.12 → unique_toolkit-1.69.0}/unique_toolkit/agentic/tools/experimental/open_file_tool/runtime.py +0 -0
  186. {unique_toolkit-1.68.12 → unique_toolkit-1.69.0}/unique_toolkit/agentic/tools/experimental/open_file_tool/tool.py +0 -0
  187. {unique_toolkit-1.68.12 → unique_toolkit-1.69.0}/unique_toolkit/agentic/tools/factory.py +0 -0
  188. {unique_toolkit-1.68.12 → unique_toolkit-1.69.0}/unique_toolkit/agentic/tools/mcp/__init__.py +0 -0
  189. {unique_toolkit-1.68.12 → unique_toolkit-1.69.0}/unique_toolkit/agentic/tools/mcp/manager.py +0 -0
  190. {unique_toolkit-1.68.12 → unique_toolkit-1.69.0}/unique_toolkit/agentic/tools/mcp/models.py +0 -0
  191. {unique_toolkit-1.68.12 → unique_toolkit-1.69.0}/unique_toolkit/agentic/tools/mcp/tool_wrapper.py +0 -0
  192. {unique_toolkit-1.68.12 → unique_toolkit-1.69.0}/unique_toolkit/agentic/tools/openai_builtin/__init__.py +0 -0
  193. {unique_toolkit-1.68.12 → unique_toolkit-1.69.0}/unique_toolkit/agentic/tools/openai_builtin/base.py +0 -0
  194. {unique_toolkit-1.68.12 → unique_toolkit-1.69.0}/unique_toolkit/agentic/tools/openai_builtin/code_interpreter/__init__.py +0 -0
  195. {unique_toolkit-1.68.12 → unique_toolkit-1.69.0}/unique_toolkit/agentic/tools/openai_builtin/code_interpreter/config.py +0 -0
  196. {unique_toolkit-1.68.12 → unique_toolkit-1.69.0}/unique_toolkit/agentic/tools/openai_builtin/code_interpreter/postprocessors/__init__.py +0 -0
  197. {unique_toolkit-1.68.12 → unique_toolkit-1.69.0}/unique_toolkit/agentic/tools/openai_builtin/code_interpreter/postprocessors/code_display.py +0 -0
  198. {unique_toolkit-1.68.12 → unique_toolkit-1.69.0}/unique_toolkit/agentic/tools/openai_builtin/code_interpreter/schemas.py +0 -0
  199. {unique_toolkit-1.68.12 → unique_toolkit-1.69.0}/unique_toolkit/agentic/tools/openai_builtin/code_interpreter/service.py +0 -0
  200. {unique_toolkit-1.68.12 → unique_toolkit-1.69.0}/unique_toolkit/agentic/tools/openai_builtin/manager.py +0 -0
  201. {unique_toolkit-1.68.12 → unique_toolkit-1.69.0}/unique_toolkit/agentic/tools/schemas.py +0 -0
  202. {unique_toolkit-1.68.12 → unique_toolkit-1.69.0}/unique_toolkit/agentic/tools/test/test_mcp_manager.py +0 -0
  203. {unique_toolkit-1.68.12 → unique_toolkit-1.69.0}/unique_toolkit/agentic/tools/test/test_tool_progress_reporter.py +0 -0
  204. {unique_toolkit-1.68.12 → unique_toolkit-1.69.0}/unique_toolkit/agentic/tools/tool.py +0 -0
  205. {unique_toolkit-1.68.12 → unique_toolkit-1.69.0}/unique_toolkit/agentic/tools/tool_manager.py +0 -0
  206. {unique_toolkit-1.68.12 → unique_toolkit-1.69.0}/unique_toolkit/agentic/tools/tool_progress_reporter.py +0 -0
  207. {unique_toolkit-1.68.12 → unique_toolkit-1.69.0}/unique_toolkit/agentic/tools/utils/__init__.py +0 -0
  208. {unique_toolkit-1.68.12 → unique_toolkit-1.69.0}/unique_toolkit/agentic/tools/utils/source_handling/__init__.py +0 -0
  209. {unique_toolkit-1.68.12 → unique_toolkit-1.69.0}/unique_toolkit/agentic/tools/utils/source_handling/schema.py +0 -0
  210. {unique_toolkit-1.68.12 → unique_toolkit-1.69.0}/unique_toolkit/agentic/tools/utils/source_handling/source_formatting.py +0 -0
  211. {unique_toolkit-1.68.12 → unique_toolkit-1.69.0}/unique_toolkit/agentic/tools/utils/source_handling/tests/test_source_formatting.py +0 -0
  212. {unique_toolkit-1.68.12 → unique_toolkit-1.69.0}/unique_toolkit/agentic_table/__init__.py +0 -0
  213. {unique_toolkit-1.68.12 → unique_toolkit-1.69.0}/unique_toolkit/agentic_table/schemas.py +0 -0
  214. {unique_toolkit-1.68.12 → unique_toolkit-1.69.0}/unique_toolkit/agentic_table/service.py +0 -0
  215. {unique_toolkit-1.68.12 → unique_toolkit-1.69.0}/unique_toolkit/app/__init__.py +0 -0
  216. {unique_toolkit-1.68.12 → unique_toolkit-1.69.0}/unique_toolkit/app/dev_util.py +0 -0
  217. {unique_toolkit-1.68.12 → unique_toolkit-1.69.0}/unique_toolkit/app/init_logging.py +0 -0
  218. {unique_toolkit-1.68.12 → unique_toolkit-1.69.0}/unique_toolkit/app/init_sdk.py +0 -0
  219. {unique_toolkit-1.68.12 → unique_toolkit-1.69.0}/unique_toolkit/app/performance/async_tasks.py +0 -0
  220. {unique_toolkit-1.68.12 → unique_toolkit-1.69.0}/unique_toolkit/app/performance/async_wrapper.py +0 -0
  221. {unique_toolkit-1.68.12 → unique_toolkit-1.69.0}/unique_toolkit/app/schemas.py +0 -0
  222. {unique_toolkit-1.68.12 → unique_toolkit-1.69.0}/unique_toolkit/app/unique_settings.py +0 -0
  223. {unique_toolkit-1.68.12 → unique_toolkit-1.69.0}/unique_toolkit/app/verification.py +0 -0
  224. {unique_toolkit-1.68.12 → unique_toolkit-1.69.0}/unique_toolkit/app/webhook.py +0 -0
  225. {unique_toolkit-1.68.12 → unique_toolkit-1.69.0}/unique_toolkit/chat/__init__.py +0 -0
  226. {unique_toolkit-1.68.12 → unique_toolkit-1.69.0}/unique_toolkit/chat/cancellation.py +0 -0
  227. {unique_toolkit-1.68.12 → unique_toolkit-1.69.0}/unique_toolkit/chat/constants.py +0 -0
  228. {unique_toolkit-1.68.12 → unique_toolkit-1.69.0}/unique_toolkit/chat/deprecated/service.py +0 -0
  229. {unique_toolkit-1.68.12 → unique_toolkit-1.69.0}/unique_toolkit/chat/functions.py +0 -0
  230. {unique_toolkit-1.68.12 → unique_toolkit-1.69.0}/unique_toolkit/chat/rendering.py +0 -0
  231. {unique_toolkit-1.68.12 → unique_toolkit-1.69.0}/unique_toolkit/chat/responses_api.py +0 -0
  232. {unique_toolkit-1.68.12 → unique_toolkit-1.69.0}/unique_toolkit/chat/schemas.py +0 -0
  233. {unique_toolkit-1.68.12 → unique_toolkit-1.69.0}/unique_toolkit/chat/service.py +0 -0
  234. {unique_toolkit-1.68.12 → unique_toolkit-1.69.0}/unique_toolkit/chat/state.py +0 -0
  235. {unique_toolkit-1.68.12 → unique_toolkit-1.69.0}/unique_toolkit/chat/utils.py +0 -0
  236. {unique_toolkit-1.68.12 → unique_toolkit-1.69.0}/unique_toolkit/content/__init__.py +0 -0
  237. {unique_toolkit-1.68.12 → unique_toolkit-1.69.0}/unique_toolkit/content/constants.py +0 -0
  238. {unique_toolkit-1.68.12 → unique_toolkit-1.69.0}/unique_toolkit/content/functions.py +0 -0
  239. {unique_toolkit-1.68.12 → unique_toolkit-1.69.0}/unique_toolkit/content/schemas.py +0 -0
  240. {unique_toolkit-1.68.12 → unique_toolkit-1.69.0}/unique_toolkit/content/service.py +0 -0
  241. {unique_toolkit-1.68.12 → unique_toolkit-1.69.0}/unique_toolkit/content/smart_rules.py +0 -0
  242. {unique_toolkit-1.68.12 → unique_toolkit-1.69.0}/unique_toolkit/content/utils.py +0 -0
  243. {unique_toolkit-1.68.12 → unique_toolkit-1.69.0}/unique_toolkit/data_extraction/README.md +0 -0
  244. {unique_toolkit-1.68.12 → unique_toolkit-1.69.0}/unique_toolkit/data_extraction/__init__.py +0 -0
  245. {unique_toolkit-1.68.12 → unique_toolkit-1.69.0}/unique_toolkit/data_extraction/augmented/__init__.py +0 -0
  246. {unique_toolkit-1.68.12 → unique_toolkit-1.69.0}/unique_toolkit/data_extraction/augmented/service.py +0 -0
  247. {unique_toolkit-1.68.12 → unique_toolkit-1.69.0}/unique_toolkit/data_extraction/base.py +0 -0
  248. {unique_toolkit-1.68.12 → unique_toolkit-1.69.0}/unique_toolkit/data_extraction/basic/__init__.py +0 -0
  249. {unique_toolkit-1.68.12 → unique_toolkit-1.69.0}/unique_toolkit/data_extraction/basic/config.py +0 -0
  250. {unique_toolkit-1.68.12 → unique_toolkit-1.69.0}/unique_toolkit/data_extraction/basic/prompt.py +0 -0
  251. {unique_toolkit-1.68.12 → unique_toolkit-1.69.0}/unique_toolkit/data_extraction/basic/service.py +0 -0
  252. {unique_toolkit-1.68.12 → unique_toolkit-1.69.0}/unique_toolkit/elicitation/__init__.py +0 -0
  253. {unique_toolkit-1.68.12 → unique_toolkit-1.69.0}/unique_toolkit/elicitation/constants.py +0 -0
  254. {unique_toolkit-1.68.12 → unique_toolkit-1.69.0}/unique_toolkit/elicitation/exceptions.py +0 -0
  255. {unique_toolkit-1.68.12 → unique_toolkit-1.69.0}/unique_toolkit/elicitation/functions.py +0 -0
  256. {unique_toolkit-1.68.12 → unique_toolkit-1.69.0}/unique_toolkit/elicitation/schemas.py +0 -0
  257. {unique_toolkit-1.68.12 → unique_toolkit-1.69.0}/unique_toolkit/elicitation/service.py +0 -0
  258. {unique_toolkit-1.68.12 → unique_toolkit-1.69.0}/unique_toolkit/embedding/__init__.py +0 -0
  259. {unique_toolkit-1.68.12 → unique_toolkit-1.69.0}/unique_toolkit/embedding/constants.py +0 -0
  260. {unique_toolkit-1.68.12 → unique_toolkit-1.69.0}/unique_toolkit/embedding/functions.py +0 -0
  261. {unique_toolkit-1.68.12 → unique_toolkit-1.69.0}/unique_toolkit/embedding/schemas.py +0 -0
  262. {unique_toolkit-1.68.12 → unique_toolkit-1.69.0}/unique_toolkit/embedding/service.py +0 -0
  263. {unique_toolkit-1.68.12 → unique_toolkit-1.69.0}/unique_toolkit/embedding/utils.py +0 -0
  264. {unique_toolkit-1.68.12 → unique_toolkit-1.69.0}/unique_toolkit/framework_utilities/__init__.py +0 -0
  265. {unique_toolkit-1.68.12 → unique_toolkit-1.69.0}/unique_toolkit/framework_utilities/langchain/__init__.py +0 -0
  266. {unique_toolkit-1.68.12 → unique_toolkit-1.69.0}/unique_toolkit/framework_utilities/langchain/client.py +0 -0
  267. {unique_toolkit-1.68.12 → unique_toolkit-1.69.0}/unique_toolkit/framework_utilities/langchain/history.py +0 -0
  268. {unique_toolkit-1.68.12 → unique_toolkit-1.69.0}/unique_toolkit/framework_utilities/openai/__init__.py +0 -0
  269. {unique_toolkit-1.68.12 → unique_toolkit-1.69.0}/unique_toolkit/framework_utilities/openai/client.py +0 -0
  270. {unique_toolkit-1.68.12 → unique_toolkit-1.69.0}/unique_toolkit/framework_utilities/openai/message_builder.py +0 -0
  271. {unique_toolkit-1.68.12 → unique_toolkit-1.69.0}/unique_toolkit/framework_utilities/utils.py +0 -0
  272. {unique_toolkit-1.68.12 → unique_toolkit-1.69.0}/unique_toolkit/language_model/__init__.py +0 -0
  273. {unique_toolkit-1.68.12 → unique_toolkit-1.69.0}/unique_toolkit/language_model/_responses_api_utils.py +0 -0
  274. {unique_toolkit-1.68.12 → unique_toolkit-1.69.0}/unique_toolkit/language_model/builder.py +0 -0
  275. {unique_toolkit-1.68.12 → unique_toolkit-1.69.0}/unique_toolkit/language_model/constants.py +0 -0
  276. {unique_toolkit-1.68.12 → unique_toolkit-1.69.0}/unique_toolkit/language_model/default_language_model.py +0 -0
  277. {unique_toolkit-1.68.12 → unique_toolkit-1.69.0}/unique_toolkit/language_model/functions.py +0 -0
  278. {unique_toolkit-1.68.12 → unique_toolkit-1.69.0}/unique_toolkit/language_model/infos.py +0 -0
  279. {unique_toolkit-1.68.12 → unique_toolkit-1.69.0}/unique_toolkit/language_model/prompt.py +0 -0
  280. {unique_toolkit-1.68.12 → unique_toolkit-1.69.0}/unique_toolkit/language_model/reference.py +0 -0
  281. {unique_toolkit-1.68.12 → unique_toolkit-1.69.0}/unique_toolkit/language_model/schemas.py +0 -0
  282. {unique_toolkit-1.68.12 → unique_toolkit-1.69.0}/unique_toolkit/language_model/service.py +0 -0
  283. {unique_toolkit-1.68.12 → unique_toolkit-1.69.0}/unique_toolkit/language_model/stream_transform.py +0 -0
  284. {unique_toolkit-1.68.12 → unique_toolkit-1.69.0}/unique_toolkit/language_model/utils.py +0 -0
  285. {unique_toolkit-1.68.12 → unique_toolkit-1.69.0}/unique_toolkit/protocols/support.py +0 -0
  286. {unique_toolkit-1.68.12 → unique_toolkit-1.69.0}/unique_toolkit/services/__init__.py +0 -0
  287. {unique_toolkit-1.68.12 → unique_toolkit-1.69.0}/unique_toolkit/services/chat_service.py +0 -0
  288. {unique_toolkit-1.68.12 → unique_toolkit-1.69.0}/unique_toolkit/services/factory.py +0 -0
  289. {unique_toolkit-1.68.12 → unique_toolkit-1.69.0}/unique_toolkit/services/knowledge_base.py +0 -0
  290. {unique_toolkit-1.68.12 → unique_toolkit-1.69.0}/unique_toolkit/short_term_memory/__init__.py +0 -0
  291. {unique_toolkit-1.68.12 → unique_toolkit-1.69.0}/unique_toolkit/short_term_memory/constants.py +0 -0
  292. {unique_toolkit-1.68.12 → unique_toolkit-1.69.0}/unique_toolkit/short_term_memory/functions.py +0 -0
  293. {unique_toolkit-1.68.12 → unique_toolkit-1.69.0}/unique_toolkit/short_term_memory/schemas.py +0 -0
  294. {unique_toolkit-1.68.12 → unique_toolkit-1.69.0}/unique_toolkit/short_term_memory/service.py +0 -0
  295. {unique_toolkit-1.68.12 → unique_toolkit-1.69.0}/unique_toolkit/smart_rules/__init__.py +0 -0
  296. {unique_toolkit-1.68.12 → unique_toolkit-1.69.0}/unique_toolkit/smart_rules/compile.py +0 -0
  297. {unique_toolkit-1.68.12 → unique_toolkit-1.69.0}/unique_toolkit/test_utilities/events.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: unique-toolkit
3
- Version: 1.68.12
3
+ Version: 1.69.0
4
4
  Summary:
5
5
  Author: Cedric Klinkert, Martin Fadler, Sadique Sheik, Fabian Schläpfer, Pascal Hauri, Ejiroghene Oki-Peter, Rami Azouz, Ricardo Visini, Sanzio Monti
6
6
  Author-email: Cedric Klinkert <cedric.klinkert@unique.ch>, Martin Fadler <martin.fadler@unique.ch>, Sadique Sheik <sadique@unique.ch>, Fabian Schläpfer <fabian@unique.ch>, Pascal Hauri <pascal@unique.ch>, Ejiroghene Oki-Peter <oki-peter.ejiroghene@unique.ai>, Rami Azouz <rami.ext@unique.ch>, Ricardo Visini <ricardo.visini@unique.ch>, Sanzio Monti <sanzio.monti@unique.ai>
@@ -38,9 +38,11 @@ Requires-Dist: openai>=1.105.0,<2 ; extra == 'langchain'
38
38
  Requires-Dist: langchain>=1.0.0,<2 ; extra == 'langchain'
39
39
  Requires-Dist: langchain-openai>=0.3.28 ; extra == 'langchain'
40
40
  Requires-Dist: langchain-core>=1.0.0,<2 ; extra == 'langchain'
41
+ Requires-Dist: prometheus-client>=0.24.1,<1 ; extra == 'monitoring'
41
42
  Requires-Python: >=3.12
42
43
  Provides-Extra: fastapi
43
44
  Provides-Extra: langchain
45
+ Provides-Extra: monitoring
44
46
  Description-Content-Type: text/markdown
45
47
 
46
48
  # Unique Toolkit
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "unique_toolkit"
3
- version = "1.68.12"
3
+ version = "1.69.0"
4
4
  description = ""
5
5
  readme = "README.md"
6
6
  requires-python = ">=3.12"
@@ -57,6 +57,9 @@ fastapi = [
57
57
  "starlette>=0.49.1",
58
58
  "uvicorn>=0.37.0,<0.38",
59
59
  ]
60
+ monitoring = [
61
+ "prometheus_client>=0.24.1,<1",
62
+ ]
60
63
 
61
64
  [build-system]
62
65
  requires = ["uv_build>=0.8.14,<0.9.0"]
@@ -164,7 +164,9 @@ class _FileProgressTracker:
164
164
  max_retries: int = 0,
165
165
  force_publish: bool = False,
166
166
  ) -> None:
167
+ lock_wait_t0 = time.monotonic()
167
168
  async with self._lock:
169
+ lock_wait_ms = (time.monotonic() - lock_wait_t0) * 1000
168
170
  state = self._states.get(filename)
169
171
  if state is None:
170
172
  return
@@ -181,8 +183,31 @@ class _FileProgressTracker:
181
183
  or now - self._last_publish_time >= self._min_interval
182
184
  )
183
185
  if should_publish:
186
+ publish_t0 = time.monotonic()
184
187
  await self._publish()
188
+ publish_ms = (time.monotonic() - publish_t0) * 1000
185
189
  self._last_publish_time = now
190
+ if lock_wait_ms > 100 or publish_ms > 500:
191
+ lock_held_ms = (
192
+ time.monotonic() - lock_wait_t0
193
+ ) * 1000 - lock_wait_ms
194
+ self._log.warning(
195
+ "Tracker.update('%s', phase=%s): "
196
+ "lock_wait=%.0fms, publish=%.0fms, "
197
+ "lock_held=%.0fms",
198
+ filename,
199
+ phase,
200
+ lock_wait_ms,
201
+ publish_ms,
202
+ lock_held_ms,
203
+ )
204
+ elif lock_wait_ms > 100:
205
+ self._log.warning(
206
+ "Tracker.update('%s', phase=%s): lock_wait=%.0fms (no publish)",
207
+ filename,
208
+ phase,
209
+ lock_wait_ms,
210
+ )
186
211
 
187
212
  async def tick_elapsed(self, filename: str, elapsed_seconds: float) -> None:
188
213
  """Publish an elapsed-time update without changing percent or retry state.
@@ -190,15 +215,38 @@ class _FileProgressTracker:
190
215
  Used by the background ticker to show time-based progress while the
191
216
  OpenAI API hasn't started returning bytes yet.
192
217
  """
218
+ lock_wait_t0 = time.monotonic()
193
219
  async with self._lock:
220
+ lock_wait_ms = (time.monotonic() - lock_wait_t0) * 1000
194
221
  state = self._states.get(filename)
195
222
  if state is None or state.phase != "downloading":
196
223
  return
197
224
  state.elapsed_seconds = elapsed_seconds
198
225
  now = time.monotonic()
199
226
  if now - self._last_publish_time >= self._min_interval:
227
+ publish_t0 = time.monotonic()
200
228
  await self._publish()
229
+ publish_ms = (time.monotonic() - publish_t0) * 1000
201
230
  self._last_publish_time = now
231
+ if lock_wait_ms > 100 or publish_ms > 500:
232
+ lock_held_ms = (
233
+ time.monotonic() - lock_wait_t0
234
+ ) * 1000 - lock_wait_ms
235
+ self._log.warning(
236
+ "Tracker.tick_elapsed('%s'): "
237
+ "lock_wait=%.0fms, publish=%.0fms, "
238
+ "lock_held=%.0fms",
239
+ filename,
240
+ lock_wait_ms,
241
+ publish_ms,
242
+ lock_held_ms,
243
+ )
244
+ elif lock_wait_ms > 100:
245
+ self._log.warning(
246
+ "Tracker.tick_elapsed('%s'): lock_wait=%.0fms (no publish)",
247
+ filename,
248
+ lock_wait_ms,
249
+ )
202
250
 
203
251
  async def publish_initial(self) -> None:
204
252
  """Send the first progress update (marks all files as pending)."""
@@ -211,7 +259,15 @@ class _FileProgressTracker:
211
259
  async def _publish(self) -> None:
212
260
  text = self._build_progress_text()
213
261
  try:
262
+ t0 = time.monotonic()
214
263
  await self._chat_service.modify_assistant_message_async(content=text)
264
+ publish_ms = (time.monotonic() - t0) * 1000
265
+ if publish_ms > 500:
266
+ self._log.warning(
267
+ "modify_assistant_message_async took %.0fms "
268
+ "(SDK singleton / backend contention suspected)",
269
+ publish_ms,
270
+ )
215
271
  except Exception:
216
272
  self._log.debug("Failed to publish progress update", exc_info=True)
217
273
 
@@ -660,6 +716,13 @@ class DisplayCodeInterpreterFilesPostProcessor(
660
716
  hide_in_chat=True,
661
717
  )
662
718
  upload_ms = (time.monotonic() - upload_t0) * 1000
719
+ if upload_ms > 2000:
720
+ self._log.warning(
721
+ "SDK upload for '%s' took %.0fms — "
722
+ "backend contention or slow blob storage suspected",
723
+ container_file.filename,
724
+ upload_ms,
725
+ )
663
726
 
664
727
  if tracker:
665
728
  await tracker.update(
@@ -796,14 +859,26 @@ class DisplayCodeInterpreterFilesPostProcessor(
796
859
  total = int(content_length) if content_length else None
797
860
  chunks: list[bytes] = []
798
861
  downloaded = 0
862
+ last_chunk_time = time.monotonic()
799
863
  async for chunk in response.iter_bytes(
800
864
  chunk_size=self._config.download_chunk_size
801
865
  ):
866
+ now = time.monotonic()
867
+ chunk_gap_ms = (now - last_chunk_time) * 1000
868
+ if chunk_gap_ms > 500:
869
+ self._log.warning(
870
+ "Download chunk gap for '%s': %.0fms "
871
+ "(downloaded=%d bytes so far)",
872
+ container_file.filename,
873
+ chunk_gap_ms,
874
+ downloaded,
875
+ )
802
876
  chunks.append(chunk)
803
877
  downloaded += len(chunk)
804
878
  if tracker:
805
879
  pct = (downloaded * 100 // total) if total else None
806
880
  elapsed = time.monotonic() - download_start
881
+ update_t0 = time.monotonic()
807
882
  await tracker.update(
808
883
  container_file.filename,
809
884
  "downloading",
@@ -812,6 +887,15 @@ class DisplayCodeInterpreterFilesPostProcessor(
812
887
  retry_attempt=retry_attempt,
813
888
  max_retries=self._config.max_download_retries,
814
889
  )
890
+ update_ms = (time.monotonic() - update_t0) * 1000
891
+ if update_ms > 200:
892
+ self._log.warning(
893
+ "tracker.update() blocked download stream for '%s' "
894
+ "for %.0fms (lock contention or slow modify_message)",
895
+ container_file.filename,
896
+ update_ms,
897
+ )
898
+ last_chunk_time = time.monotonic()
815
899
  transfer_ms = (time.monotonic() - t0) * 1000 - first_byte_ms
816
900
  self._log.info(
817
901
  "Stream complete for '%s' — transfer=%.0fms, total=%d bytes",
@@ -63,6 +63,21 @@ def build_unique_custom_app(
63
63
  """Health check endpoint."""
64
64
  return JSONResponse(content={"status": "healthy", "service": title})
65
65
 
66
+ from unique_toolkit.monitoring import _MONITORING_AVAILABLE
67
+
68
+ if _MONITORING_AVAILABLE:
69
+ from starlette.responses import Response as StarletteResponse
70
+
71
+ from unique_toolkit.monitoring import get_metrics
72
+
73
+ @app.get(path="/metrics")
74
+ async def metrics_endpoint() -> StarletteResponse:
75
+ """Prometheus metrics endpoint."""
76
+ return StarletteResponse(
77
+ content=get_metrics(),
78
+ media_type="text/plain; version=0.0.4; charset=utf-8",
79
+ )
80
+
66
81
  @app.post(path=webhook_path)
67
82
  async def webhook_handler(
68
83
  request: Request, background_tasks: BackgroundTasks
@@ -0,0 +1,39 @@
1
+ try:
2
+ from prometheus_client import CollectorRegistry # noqa: F401
3
+
4
+ _MONITORING_AVAILABLE = True
5
+ except ImportError:
6
+ _MONITORING_AVAILABLE = False
7
+
8
+
9
+ def _check_monitoring_available() -> None:
10
+ if not _MONITORING_AVAILABLE:
11
+ raise ImportError(
12
+ "prometheus_client is not installed. "
13
+ "Install it with: pip install unique_toolkit[monitoring]"
14
+ )
15
+
16
+
17
+ if _MONITORING_AVAILABLE:
18
+ from unique_toolkit.monitoring.middleware import MetricsMiddleware
19
+ from unique_toolkit.monitoring.registry import (
20
+ REGISTRY,
21
+ MetricNamespace,
22
+ get_metrics,
23
+ metric_scope,
24
+ track_execution,
25
+ )
26
+
27
+ __all__ = [
28
+ "REGISTRY",
29
+ "MetricNamespace",
30
+ "MetricsMiddleware",
31
+ "get_metrics",
32
+ "metric_scope",
33
+ "track_execution",
34
+ "_MONITORING_AVAILABLE",
35
+ ]
36
+ else:
37
+
38
+ def __getattr__(name: str):
39
+ _check_monitoring_available()
@@ -0,0 +1,90 @@
1
+ from __future__ import annotations
2
+
3
+ import time
4
+
5
+ from prometheus_client import Counter, Gauge, Histogram
6
+
7
+ from .registry import REGISTRY
8
+
9
+ _http_requests_total = Counter(
10
+ "http_requests_total",
11
+ "Total HTTP requests",
12
+ ["method", "path", "status_code"],
13
+ registry=REGISTRY,
14
+ )
15
+ _http_request_duration_seconds = Histogram(
16
+ "http_request_duration_seconds",
17
+ "HTTP request duration",
18
+ ["method", "path"],
19
+ registry=REGISTRY,
20
+ )
21
+ _http_requests_in_progress = Gauge(
22
+ "http_requests_in_progress",
23
+ "In-flight HTTP requests",
24
+ ["method"],
25
+ registry=REGISTRY,
26
+ )
27
+
28
+ _DEFAULT_EXCLUDED_PATHS = frozenset({"/health", "/metrics", "/"})
29
+
30
+
31
+ class MetricsMiddleware:
32
+ """ASGI middleware that tracks HTTP request metrics.
33
+
34
+ Works with any ASGI app (FastAPI, Quart, Starlette).
35
+
36
+ Handles:
37
+ - Non-HTTP scopes (WebSocket, lifespan) — passed through, not tracked
38
+ - Excluded paths (health, metrics) — passed through, not tracked
39
+ - App exceptions — duration + error still recorded, in-progress gauge cleaned up
40
+ - Streaming responses — status captured from first http.response.start message
41
+
42
+ Usage::
43
+
44
+ # FastAPI
45
+ app.add_middleware(MetricsMiddleware)
46
+
47
+ # Quart
48
+ app.asgi_app = MetricsMiddleware(app.asgi_app)
49
+ """
50
+
51
+ def __init__(self, app, excluded_paths: set[str] | None = None):
52
+ self.app = app
53
+ self._excluded_paths = (
54
+ excluded_paths if excluded_paths is not None else _DEFAULT_EXCLUDED_PATHS
55
+ )
56
+
57
+ async def __call__(self, scope, receive, send):
58
+ # Pass through non-HTTP (WebSocket, lifespan)
59
+ if scope["type"] != "http":
60
+ return await self.app(scope, receive, send)
61
+
62
+ path = scope.get("path", "/")
63
+
64
+ # Pass through excluded paths (health checks, metrics endpoint)
65
+ if path in self._excluded_paths:
66
+ return await self.app(scope, receive, send)
67
+
68
+ method = scope.get("method", "UNKNOWN")
69
+ status_code = "500" # Default if app crashes before sending response
70
+
71
+ _http_requests_in_progress.labels(method=method).inc()
72
+ start = time.perf_counter()
73
+
74
+ async def send_wrapper(message):
75
+ nonlocal status_code
76
+ if message["type"] == "http.response.start":
77
+ status_code = str(message["status"])
78
+ await send(message)
79
+
80
+ try:
81
+ await self.app(scope, receive, send_wrapper)
82
+ finally:
83
+ duration = time.perf_counter() - start
84
+ _http_requests_in_progress.labels(method=method).dec()
85
+ _http_request_duration_seconds.labels(method=method, path=path).observe(
86
+ duration
87
+ )
88
+ _http_requests_total.labels(
89
+ method=method, path=path, status_code=status_code
90
+ ).inc()
@@ -0,0 +1,107 @@
1
+ from __future__ import annotations
2
+
3
+ import asyncio
4
+ import functools
5
+ import time
6
+ from contextlib import contextmanager
7
+ from typing import Callable
8
+
9
+ from prometheus_client import (
10
+ CollectorRegistry,
11
+ Counter,
12
+ Gauge,
13
+ Histogram,
14
+ generate_latest,
15
+ )
16
+
17
+ REGISTRY = CollectorRegistry()
18
+
19
+
20
+ def get_metrics() -> bytes:
21
+ """Returns all registered metrics in Prometheus text format."""
22
+ return generate_latest(REGISTRY)
23
+
24
+
25
+ class MetricNamespace:
26
+ """Factory that auto-prefixes metric names and auto-registers to REGISTRY.
27
+
28
+ Usage::
29
+
30
+ m = MetricNamespace("unique_web_search")
31
+ search_duration = m.histogram("search_duration_seconds", "Search API latency", ["engine"])
32
+ search_total = m.counter("search_total", "Search calls by engine", ["engine"])
33
+ """
34
+
35
+ def __init__(self, prefix: str) -> None:
36
+ self._prefix = prefix
37
+
38
+ def histogram(self, name: str, description: str, labels: list[str]) -> Histogram:
39
+ return Histogram(
40
+ f"{self._prefix}_{name}", description, labels, registry=REGISTRY
41
+ )
42
+
43
+ def counter(self, name: str, description: str, labels: list[str]) -> Counter:
44
+ return Counter(f"{self._prefix}_{name}", description, labels, registry=REGISTRY)
45
+
46
+ def gauge(self, name: str, description: str, labels: list[str]) -> Gauge:
47
+ return Gauge(f"{self._prefix}_{name}", description, labels, registry=REGISTRY)
48
+
49
+
50
+ @contextmanager
51
+ def metric_scope(duration: Histogram, errors: Counter | None = None, **labels):
52
+ """Context manager that auto-times a block and records metrics.
53
+
54
+ Use when labels are dynamic (determined at runtime).
55
+
56
+ Example — search engine with dynamic engine label::
57
+
58
+ with metric_scope(search_duration, search_errors, engine=self.engine_name):
59
+ results = await self._execute_search(query)
60
+
61
+ Example — LLM call with purpose label::
62
+
63
+ with metric_scope(llm_duration, llm_errors, purpose="snippet_judge"):
64
+ response = await lm_service.complete_async(...)
65
+ """
66
+ start = time.perf_counter()
67
+ try:
68
+ yield
69
+ except Exception as e:
70
+ if errors:
71
+ errors.labels(**labels, error_type=type(e).__name__).inc()
72
+ raise
73
+ finally:
74
+ duration.labels(**labels).observe(time.perf_counter() - start)
75
+
76
+
77
+ def track_execution(
78
+ duration: Histogram,
79
+ errors: Counter | None = None,
80
+ **labels,
81
+ ) -> Callable:
82
+ """Decorator that auto-tracks duration + errors for an entire function.
83
+
84
+ Use when labels are static (known at definition time).
85
+ Works with both sync and async functions.
86
+
87
+ Example — crawl with fixed crawler name::
88
+
89
+ @track_execution(crawl_duration, crawl_errors, crawler="basic")
90
+ async def crawl(self, urls):
91
+ ...
92
+ """
93
+
94
+ def decorator(func):
95
+ @functools.wraps(func)
96
+ async def async_wrapper(*args, **kwargs):
97
+ with metric_scope(duration, errors, **labels):
98
+ return await func(*args, **kwargs)
99
+
100
+ @functools.wraps(func)
101
+ def sync_wrapper(*args, **kwargs):
102
+ with metric_scope(duration, errors, **labels):
103
+ return func(*args, **kwargs)
104
+
105
+ return async_wrapper if asyncio.iscoroutinefunction(func) else sync_wrapper
106
+
107
+ return decorator