agent-framework-lib 0.6.3__tar.gz → 0.6.3.post3__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 (238) hide show
  1. {agent_framework_lib-0.6.3/agent_framework_lib.egg-info → agent_framework_lib-0.6.3.post3}/PKG-INFO +3 -1
  2. {agent_framework_lib-0.6.3 → agent_framework_lib-0.6.3.post3}/agent_framework/__init__.py +1 -1
  3. agent_framework_lib-0.6.3.post3/agent_framework/capabilities/__init__.py +10 -0
  4. agent_framework_lib-0.6.3.post3/agent_framework/capabilities/resolver.py +175 -0
  5. {agent_framework_lib-0.6.3 → agent_framework_lib-0.6.3.post3}/agent_framework/core/scratchpad_compressor.py +128 -5
  6. {agent_framework_lib-0.6.3 → agent_framework_lib-0.6.3.post3}/agent_framework/implementations/budget_aware_agent.py +61 -8
  7. {agent_framework_lib-0.6.3 → agent_framework_lib-0.6.3.post3}/agent_framework/implementations/llamaindex_agent.py +17 -4
  8. {agent_framework_lib-0.6.3 → agent_framework_lib-0.6.3.post3}/agent_framework/skills/agent_mixin.py +64 -12
  9. {agent_framework_lib-0.6.3 → agent_framework_lib-0.6.3.post3}/agent_framework/skills/base.py +4 -0
  10. {agent_framework_lib-0.6.3 → agent_framework_lib-0.6.3.post3}/agent_framework/skills/builtin/__init__.py +31 -22
  11. agent_framework_lib-0.6.3.post3/agent_framework/skills/markdown_loader.py +293 -0
  12. agent_framework_lib-0.6.3.post3/agent_framework/subagents/__init__.py +10 -0
  13. agent_framework_lib-0.6.3.post3/agent_framework/subagents/spawn_tool.py +187 -0
  14. agent_framework_lib-0.6.3.post3/agent_framework/tools/activity_callback.py +47 -0
  15. agent_framework_lib-0.6.3.post3/agent_framework/tools/shell_tool.py +165 -0
  16. agent_framework_lib-0.6.3.post3/agent_framework/tools/web_fetch_tool.py +137 -0
  17. {agent_framework_lib-0.6.3 → agent_framework_lib-0.6.3.post3}/agent_framework/tools/web_search_tools.py +85 -18
  18. {agent_framework_lib-0.6.3 → agent_framework_lib-0.6.3.post3/agent_framework_lib.egg-info}/PKG-INFO +3 -1
  19. {agent_framework_lib-0.6.3 → agent_framework_lib-0.6.3.post3}/agent_framework_lib.egg-info/SOURCES.txt +9 -0
  20. {agent_framework_lib-0.6.3 → agent_framework_lib-0.6.3.post3}/agent_framework_lib.egg-info/requires.txt +2 -0
  21. agent_framework_lib-0.6.3.post3/docs/SKILLS_AND_SUBAGENTS_ANALYSIS.md +345 -0
  22. {agent_framework_lib-0.6.3 → agent_framework_lib-0.6.3.post3}/pyproject.toml +3 -1
  23. {agent_framework_lib-0.6.3 → agent_framework_lib-0.6.3.post3}/ARCHITECTURE.md +0 -0
  24. {agent_framework_lib-0.6.3 → agent_framework_lib-0.6.3.post3}/LICENSE +0 -0
  25. {agent_framework_lib-0.6.3 → agent_framework_lib-0.6.3.post3}/MANIFEST.in +0 -0
  26. {agent_framework_lib-0.6.3 → agent_framework_lib-0.6.3.post3}/README.md +0 -0
  27. {agent_framework_lib-0.6.3 → agent_framework_lib-0.6.3.post3}/agent_framework/a2a/__init__.py +0 -0
  28. {agent_framework_lib-0.6.3 → agent_framework_lib-0.6.3.post3}/agent_framework/a2a/base.py +0 -0
  29. {agent_framework_lib-0.6.3 → agent_framework_lib-0.6.3.post3}/agent_framework/a2a/endpoints/__init__.py +0 -0
  30. {agent_framework_lib-0.6.3 → agent_framework_lib-0.6.3.post3}/agent_framework/a2a/endpoints/a2a_router.py +0 -0
  31. {agent_framework_lib-0.6.3 → agent_framework_lib-0.6.3.post3}/agent_framework/a2a/endpoints/agent_card_builder.py +0 -0
  32. {agent_framework_lib-0.6.3 → agent_framework_lib-0.6.3.post3}/agent_framework/a2a/endpoints/agent_card_skill_builder.py +0 -0
  33. {agent_framework_lib-0.6.3 → agent_framework_lib-0.6.3.post3}/agent_framework/a2a/endpoints/jsonrpc_dispatcher.py +0 -0
  34. {agent_framework_lib-0.6.3 → agent_framework_lib-0.6.3.post3}/agent_framework/a2a/endpoints/models_jsonrpc.py +0 -0
  35. {agent_framework_lib-0.6.3 → agent_framework_lib-0.6.3.post3}/agent_framework/a2a/endpoints/sse_wrapper.py +0 -0
  36. {agent_framework_lib-0.6.3 → agent_framework_lib-0.6.3.post3}/agent_framework/a2a/endpoints/translation_layer.py +0 -0
  37. {agent_framework_lib-0.6.3 → agent_framework_lib-0.6.3.post3}/agent_framework/a2a/models.py +0 -0
  38. {agent_framework_lib-0.6.3 → agent_framework_lib-0.6.3.post3}/agent_framework/a2a/providers/__init__.py +0 -0
  39. {agent_framework_lib-0.6.3 → agent_framework_lib-0.6.3.post3}/agent_framework/a2a/providers/elasticsearch_provider.py +0 -0
  40. {agent_framework_lib-0.6.3 → agent_framework_lib-0.6.3.post3}/agent_framework/a2a/providers/postgres_provider.py +0 -0
  41. {agent_framework_lib-0.6.3 → agent_framework_lib-0.6.3.post3}/agent_framework/chart_generation/llm_refinement_loop.py +0 -0
  42. {agent_framework_lib-0.6.3 → agent_framework_lib-0.6.3.post3}/agent_framework/core/__init__.py +0 -0
  43. {agent_framework_lib-0.6.3 → agent_framework_lib-0.6.3.post3}/agent_framework/core/activity_formatter.py +0 -0
  44. {agent_framework_lib-0.6.3 → agent_framework_lib-0.6.3.post3}/agent_framework/core/agent_interface.py +0 -0
  45. {agent_framework_lib-0.6.3 → agent_framework_lib-0.6.3.post3}/agent_framework/core/agent_provider.py +0 -0
  46. {agent_framework_lib-0.6.3 → agent_framework_lib-0.6.3.post3}/agent_framework/core/base_agent.py +0 -0
  47. {agent_framework_lib-0.6.3 → agent_framework_lib-0.6.3.post3}/agent_framework/core/context_budget.py +0 -0
  48. {agent_framework_lib-0.6.3 → agent_framework_lib-0.6.3.post3}/agent_framework/core/context_summarizer.py +0 -0
  49. {agent_framework_lib-0.6.3 → agent_framework_lib-0.6.3.post3}/agent_framework/core/elasticsearch_config_provider.py +0 -0
  50. {agent_framework_lib-0.6.3 → agent_framework_lib-0.6.3.post3}/agent_framework/core/implementation_validator.py +0 -0
  51. {agent_framework_lib-0.6.3 → agent_framework_lib-0.6.3.post3}/agent_framework/core/knowledge_state.py +0 -0
  52. {agent_framework_lib-0.6.3 → agent_framework_lib-0.6.3.post3}/agent_framework/core/loop_detector.py +0 -0
  53. {agent_framework_lib-0.6.3 → agent_framework_lib-0.6.3.post3}/agent_framework/core/model_clients.py +0 -0
  54. {agent_framework_lib-0.6.3 → agent_framework_lib-0.6.3.post3}/agent_framework/core/model_config.py +0 -0
  55. {agent_framework_lib-0.6.3 → agent_framework_lib-0.6.3.post3}/agent_framework/core/model_router.py +0 -0
  56. {agent_framework_lib-0.6.3 → agent_framework_lib-0.6.3.post3}/agent_framework/core/models.py +0 -0
  57. {agent_framework_lib-0.6.3 → agent_framework_lib-0.6.3.post3}/agent_framework/core/provider_calibration.py +0 -0
  58. {agent_framework_lib-0.6.3 → agent_framework_lib-0.6.3.post3}/agent_framework/core/rich_content_prompt_unsused_to_be_deleted.py +0 -0
  59. {agent_framework_lib-0.6.3 → agent_framework_lib-0.6.3.post3}/agent_framework/core/state_manager.py +0 -0
  60. {agent_framework_lib-0.6.3 → agent_framework_lib-0.6.3.post3}/agent_framework/core/step_display_config.py +0 -0
  61. {agent_framework_lib-0.6.3 → agent_framework_lib-0.6.3.post3}/agent_framework/core/streaming_parts_accumulator.py +0 -0
  62. {agent_framework_lib-0.6.3 → agent_framework_lib-0.6.3.post3}/agent_framework/implementations/__init__.py +0 -0
  63. {agent_framework_lib-0.6.3 → agent_framework_lib-0.6.3.post3}/agent_framework/implementations/llamaindex_memory_adapter.py +0 -0
  64. {agent_framework_lib-0.6.3 → agent_framework_lib-0.6.3.post3}/agent_framework/implementations/microsoft_agent.py +0 -0
  65. {agent_framework_lib-0.6.3 → agent_framework_lib-0.6.3.post3}/agent_framework/memory/__init__.py +0 -0
  66. {agent_framework_lib-0.6.3 → agent_framework_lib-0.6.3.post3}/agent_framework/memory/agent_mixin.py +0 -0
  67. {agent_framework_lib-0.6.3 → agent_framework_lib-0.6.3.post3}/agent_framework/memory/base.py +0 -0
  68. {agent_framework_lib-0.6.3 → agent_framework_lib-0.6.3.post3}/agent_framework/memory/config.py +0 -0
  69. {agent_framework_lib-0.6.3 → agent_framework_lib-0.6.3.post3}/agent_framework/memory/manager.py +0 -0
  70. {agent_framework_lib-0.6.3 → agent_framework_lib-0.6.3.post3}/agent_framework/memory/personalization.py +0 -0
  71. {agent_framework_lib-0.6.3 → agent_framework_lib-0.6.3.post3}/agent_framework/memory/providers/__init__.py +0 -0
  72. {agent_framework_lib-0.6.3 → agent_framework_lib-0.6.3.post3}/agent_framework/memory/providers/graphiti_provider.py +0 -0
  73. {agent_framework_lib-0.6.3 → agent_framework_lib-0.6.3.post3}/agent_framework/memory/providers/memori_provider.py +0 -0
  74. {agent_framework_lib-0.6.3 → agent_framework_lib-0.6.3.post3}/agent_framework/memory/tools.py +0 -0
  75. {agent_framework_lib-0.6.3 → agent_framework_lib-0.6.3.post3}/agent_framework/monitoring/__init__.py +0 -0
  76. {agent_framework_lib-0.6.3 → agent_framework_lib-0.6.3.post3}/agent_framework/monitoring/api_timing_tracker.py +0 -0
  77. {agent_framework_lib-0.6.3 → agent_framework_lib-0.6.3.post3}/agent_framework/monitoring/elasticsearch_circuit_breaker.py +0 -0
  78. {agent_framework_lib-0.6.3 → agent_framework_lib-0.6.3.post3}/agent_framework/monitoring/elasticsearch_logging.py +0 -0
  79. {agent_framework_lib-0.6.3 → agent_framework_lib-0.6.3.post3}/agent_framework/monitoring/error_handling.py +0 -0
  80. {agent_framework_lib-0.6.3 → agent_framework_lib-0.6.3.post3}/agent_framework/monitoring/error_logging.py +0 -0
  81. {agent_framework_lib-0.6.3 → agent_framework_lib-0.6.3.post3}/agent_framework/monitoring/llm_auto_instrumentor.py +0 -0
  82. {agent_framework_lib-0.6.3 → agent_framework_lib-0.6.3.post3}/agent_framework/monitoring/llm_metrics.py +0 -0
  83. {agent_framework_lib-0.6.3 → agent_framework_lib-0.6.3.post3}/agent_framework/monitoring/llm_metrics_collector.py +0 -0
  84. {agent_framework_lib-0.6.3 → agent_framework_lib-0.6.3.post3}/agent_framework/monitoring/llm_metrics_extractor.py +0 -0
  85. {agent_framework_lib-0.6.3 → agent_framework_lib-0.6.3.post3}/agent_framework/monitoring/metrics_aggregator.py +0 -0
  86. {agent_framework_lib-0.6.3 → agent_framework_lib-0.6.3.post3}/agent_framework/monitoring/metrics_config.py +0 -0
  87. {agent_framework_lib-0.6.3 → agent_framework_lib-0.6.3.post3}/agent_framework/monitoring/observability_manager.py +0 -0
  88. {agent_framework_lib-0.6.3 → agent_framework_lib-0.6.3.post3}/agent_framework/monitoring/otel_instrumentor.py +0 -0
  89. {agent_framework_lib-0.6.3 → agent_framework_lib-0.6.3.post3}/agent_framework/monitoring/otel_logging_handler.py +0 -0
  90. {agent_framework_lib-0.6.3 → agent_framework_lib-0.6.3.post3}/agent_framework/monitoring/otel_metrics_recorder.py +0 -0
  91. {agent_framework_lib-0.6.3 → agent_framework_lib-0.6.3.post3}/agent_framework/monitoring/otel_setup.py +0 -0
  92. {agent_framework_lib-0.6.3 → agent_framework_lib-0.6.3.post3}/agent_framework/monitoring/performance_monitor.py +0 -0
  93. {agent_framework_lib-0.6.3 → agent_framework_lib-0.6.3.post3}/agent_framework/monitoring/progress_tracker.py +0 -0
  94. {agent_framework_lib-0.6.3 → agent_framework_lib-0.6.3.post3}/agent_framework/monitoring/resource_manager.py +0 -0
  95. {agent_framework_lib-0.6.3 → agent_framework_lib-0.6.3.post3}/agent_framework/monitoring/resource_metrics_collector.py +0 -0
  96. {agent_framework_lib-0.6.3 → agent_framework_lib-0.6.3.post3}/agent_framework/monitoring/streaming_latency_tracer.py +0 -0
  97. {agent_framework_lib-0.6.3 → agent_framework_lib-0.6.3.post3}/agent_framework/monitoring/timing_tracker.py +0 -0
  98. {agent_framework_lib-0.6.3 → agent_framework_lib-0.6.3.post3}/agent_framework/monitoring/token_counter.py +0 -0
  99. {agent_framework_lib-0.6.3 → agent_framework_lib-0.6.3.post3}/agent_framework/monitoring/tracing_context.py +0 -0
  100. {agent_framework_lib-0.6.3 → agent_framework_lib-0.6.3.post3}/agent_framework/processing/__init__.py +0 -0
  101. {agent_framework_lib-0.6.3 → agent_framework_lib-0.6.3.post3}/agent_framework/processing/ai_content_management.py +0 -0
  102. {agent_framework_lib-0.6.3 → agent_framework_lib-0.6.3.post3}/agent_framework/processing/markdown_converter.py +0 -0
  103. {agent_framework_lib-0.6.3 → agent_framework_lib-0.6.3.post3}/agent_framework/processing/multimodal_integration.py +0 -0
  104. {agent_framework_lib-0.6.3 → agent_framework_lib-0.6.3.post3}/agent_framework/processing/rich_content_validation.py +0 -0
  105. {agent_framework_lib-0.6.3 → agent_framework_lib-0.6.3.post3}/agent_framework/py.typed +0 -0
  106. {agent_framework_lib-0.6.3 → agent_framework_lib-0.6.3.post3}/agent_framework/session/__init__.py +0 -0
  107. {agent_framework_lib-0.6.3 → agent_framework_lib-0.6.3.post3}/agent_framework/session/elasticsearch_session_storage.py +0 -0
  108. {agent_framework_lib-0.6.3 → agent_framework_lib-0.6.3.post3}/agent_framework/session/session_storage.py +0 -0
  109. {agent_framework_lib-0.6.3 → agent_framework_lib-0.6.3.post3}/agent_framework/skills/__init__.py +0 -0
  110. {agent_framework_lib-0.6.3 → agent_framework_lib-0.6.3.post3}/agent_framework/skills/builtin/chart_skill.py +0 -0
  111. {agent_framework_lib-0.6.3 → agent_framework_lib-0.6.3.post3}/agent_framework/skills/builtin/drawio_skill.py +0 -0
  112. {agent_framework_lib-0.6.3 → agent_framework_lib-0.6.3.post3}/agent_framework/skills/builtin/excel_skill.py +0 -0
  113. {agent_framework_lib-0.6.3 → agent_framework_lib-0.6.3.post3}/agent_framework/skills/builtin/file_access_skill.py +0 -0
  114. {agent_framework_lib-0.6.3 → agent_framework_lib-0.6.3.post3}/agent_framework/skills/builtin/file_skill.py +0 -0
  115. {agent_framework_lib-0.6.3 → agent_framework_lib-0.6.3.post3}/agent_framework/skills/builtin/form_skill.py +0 -0
  116. {agent_framework_lib-0.6.3 → agent_framework_lib-0.6.3.post3}/agent_framework/skills/builtin/image_display_skill.py +0 -0
  117. {agent_framework_lib-0.6.3 → agent_framework_lib-0.6.3.post3}/agent_framework/skills/builtin/mermaid_skill.py +0 -0
  118. {agent_framework_lib-0.6.3 → agent_framework_lib-0.6.3.post3}/agent_framework/skills/builtin/multimodal_skill.py +0 -0
  119. {agent_framework_lib-0.6.3 → agent_framework_lib-0.6.3.post3}/agent_framework/skills/builtin/optionsblock_skill.py +0 -0
  120. {agent_framework_lib-0.6.3 → agent_framework_lib-0.6.3.post3}/agent_framework/skills/builtin/pdf_skill.py +0 -0
  121. {agent_framework_lib-0.6.3 → agent_framework_lib-0.6.3.post3}/agent_framework/skills/builtin/pdf_with_images_skill.py +0 -0
  122. {agent_framework_lib-0.6.3 → agent_framework_lib-0.6.3.post3}/agent_framework/skills/builtin/table_skill.py +0 -0
  123. {agent_framework_lib-0.6.3 → agent_framework_lib-0.6.3.post3}/agent_framework/skills/builtin/unified_pdf_skill.py +0 -0
  124. {agent_framework_lib-0.6.3 → agent_framework_lib-0.6.3.post3}/agent_framework/skills/builtin/web_search_skill.py +0 -0
  125. {agent_framework_lib-0.6.3 → agent_framework_lib-0.6.3.post3}/agent_framework/skills/discovery_prompt.py +0 -0
  126. {agent_framework_lib-0.6.3 → agent_framework_lib-0.6.3.post3}/agent_framework/skills/tools.py +0 -0
  127. {agent_framework_lib-0.6.3 → agent_framework_lib-0.6.3.post3}/agent_framework/storage/__init__.py +0 -0
  128. {agent_framework_lib-0.6.3 → agent_framework_lib-0.6.3.post3}/agent_framework/storage/file_storages.py +0 -0
  129. {agent_framework_lib-0.6.3 → agent_framework_lib-0.6.3.post3}/agent_framework/storage/file_system_management.py +0 -0
  130. {agent_framework_lib-0.6.3 → agent_framework_lib-0.6.3.post3}/agent_framework/storage/storage_optimizer.py +0 -0
  131. {agent_framework_lib-0.6.3 → agent_framework_lib-0.6.3.post3}/agent_framework/tools/__init__.py +0 -0
  132. {agent_framework_lib-0.6.3 → agent_framework_lib-0.6.3.post3}/agent_framework/tools/adaptive_pdf_css.py +0 -0
  133. {agent_framework_lib-0.6.3 → agent_framework_lib-0.6.3.post3}/agent_framework/tools/base.py +0 -0
  134. {agent_framework_lib-0.6.3 → agent_framework_lib-0.6.3.post3}/agent_framework/tools/chart_tools.py +0 -0
  135. {agent_framework_lib-0.6.3 → agent_framework_lib-0.6.3.post3}/agent_framework/tools/drawio_tools.py +0 -0
  136. {agent_framework_lib-0.6.3 → agent_framework_lib-0.6.3.post3}/agent_framework/tools/excel_tools.py +0 -0
  137. {agent_framework_lib-0.6.3 → agent_framework_lib-0.6.3.post3}/agent_framework/tools/file_access_tools.py +0 -0
  138. {agent_framework_lib-0.6.3 → agent_framework_lib-0.6.3.post3}/agent_framework/tools/file_tools.py +0 -0
  139. {agent_framework_lib-0.6.3 → agent_framework_lib-0.6.3.post3}/agent_framework/tools/html_content_analyzer.py +0 -0
  140. {agent_framework_lib-0.6.3 → agent_framework_lib-0.6.3.post3}/agent_framework/tools/mermaid_tools.py +0 -0
  141. {agent_framework_lib-0.6.3 → agent_framework_lib-0.6.3.post3}/agent_framework/tools/multimodal_tools.py +0 -0
  142. {agent_framework_lib-0.6.3 → agent_framework_lib-0.6.3.post3}/agent_framework/tools/pdf_image_scaler.py +0 -0
  143. {agent_framework_lib-0.6.3 → agent_framework_lib-0.6.3.post3}/agent_framework/tools/pdf_tools.py +0 -0
  144. {agent_framework_lib-0.6.3 → agent_framework_lib-0.6.3.post3}/agent_framework/tools/pdf_with_images_tool.py +0 -0
  145. {agent_framework_lib-0.6.3 → agent_framework_lib-0.6.3.post3}/agent_framework/tools/sizing_config.py +0 -0
  146. {agent_framework_lib-0.6.3 → agent_framework_lib-0.6.3.post3}/agent_framework/tools/tabledata_tools.py +0 -0
  147. {agent_framework_lib-0.6.3 → agent_framework_lib-0.6.3.post3}/agent_framework/tools/unified_pdf_tool.py +0 -0
  148. {agent_framework_lib-0.6.3 → agent_framework_lib-0.6.3.post3}/agent_framework/utils/__init__.py +0 -0
  149. {agent_framework_lib-0.6.3 → agent_framework_lib-0.6.3.post3}/agent_framework/utils/path_utils.py +0 -0
  150. {agent_framework_lib-0.6.3 → agent_framework_lib-0.6.3.post3}/agent_framework/utils/post_install.py +0 -0
  151. {agent_framework_lib-0.6.3 → agent_framework_lib-0.6.3.post3}/agent_framework/utils/session_title_generator.py +0 -0
  152. {agent_framework_lib-0.6.3 → agent_framework_lib-0.6.3.post3}/agent_framework/utils/source_detector.py +0 -0
  153. {agent_framework_lib-0.6.3 → agent_framework_lib-0.6.3.post3}/agent_framework/utils/special_blocks.py +0 -0
  154. {agent_framework_lib-0.6.3 → agent_framework_lib-0.6.3.post3}/agent_framework/web/__init__.py +0 -0
  155. {agent_framework_lib-0.6.3 → agent_framework_lib-0.6.3.post3}/agent_framework/web/admin_auth.py +0 -0
  156. {agent_framework_lib-0.6.3 → agent_framework_lib-0.6.3.post3}/agent_framework/web/admin_models.py +0 -0
  157. {agent_framework_lib-0.6.3 → agent_framework_lib-0.6.3.post3}/agent_framework/web/admin_router.py +0 -0
  158. {agent_framework_lib-0.6.3 → agent_framework_lib-0.6.3.post3}/agent_framework/web/admin_services.py +0 -0
  159. {agent_framework_lib-0.6.3 → agent_framework_lib-0.6.3.post3}/agent_framework/web/api_timing_middleware.py +0 -0
  160. {agent_framework_lib-0.6.3 → agent_framework_lib-0.6.3.post3}/agent_framework/web/docs/A2A_GUIDE.md +0 -0
  161. {agent_framework_lib-0.6.3 → agent_framework_lib-0.6.3.post3}/agent_framework/web/docs/CREATING_AGENTS.md +0 -0
  162. {agent_framework_lib-0.6.3 → agent_framework_lib-0.6.3.post3}/agent_framework/web/docs/DOCKER_SETUP.md +0 -0
  163. {agent_framework_lib-0.6.3 → agent_framework_lib-0.6.3.post3}/agent_framework/web/docs/Dockerfile +0 -0
  164. {agent_framework_lib-0.6.3 → agent_framework_lib-0.6.3.post3}/agent_framework/web/docs/GETTING_STARTED.md +0 -0
  165. {agent_framework_lib-0.6.3 → agent_framework_lib-0.6.3.post3}/agent_framework/web/docs/MEMORY_INSTALLATION.md +0 -0
  166. {agent_framework_lib-0.6.3 → agent_framework_lib-0.6.3.post3}/agent_framework/web/docs/README.md +0 -0
  167. {agent_framework_lib-0.6.3 → agent_framework_lib-0.6.3.post3}/agent_framework/web/docs/TOOLS_AND_MCP_GUIDE.md +0 -0
  168. {agent_framework_lib-0.6.3 → agent_framework_lib-0.6.3.post3}/agent_framework/web/docs/api-reference.md +0 -0
  169. {agent_framework_lib-0.6.3 → agent_framework_lib-0.6.3.post3}/agent_framework/web/docs/configuration.md +0 -0
  170. {agent_framework_lib-0.6.3 → agent_framework_lib-0.6.3.post3}/agent_framework/web/docs/docker-compose.yml +0 -0
  171. {agent_framework_lib-0.6.3 → agent_framework_lib-0.6.3.post3}/agent_framework/web/docs/examples/agent_example_multi_skills.py +0 -0
  172. {agent_framework_lib-0.6.3 → agent_framework_lib-0.6.3.post3}/agent_framework/web/docs/examples/agent_with_file_storage.py +0 -0
  173. {agent_framework_lib-0.6.3 → agent_framework_lib-0.6.3.post3}/agent_framework/web/docs/examples/agent_with_mcp.py +0 -0
  174. {agent_framework_lib-0.6.3 → agent_framework_lib-0.6.3.post3}/agent_framework/web/docs/examples/agent_with_memory.py +0 -0
  175. {agent_framework_lib-0.6.3 → agent_framework_lib-0.6.3.post3}/agent_framework/web/docs/examples/agent_with_memory_graphiti.py +0 -0
  176. {agent_framework_lib-0.6.3 → agent_framework_lib-0.6.3.post3}/agent_framework/web/docs/examples/agent_with_memory_hybrid.py +0 -0
  177. {agent_framework_lib-0.6.3 → agent_framework_lib-0.6.3.post3}/agent_framework/web/docs/examples/agent_with_memory_simple.py +0 -0
  178. {agent_framework_lib-0.6.3 → agent_framework_lib-0.6.3.post3}/agent_framework/web/docs/examples/custom_framework_agent.py +0 -0
  179. {agent_framework_lib-0.6.3 → agent_framework_lib-0.6.3.post3}/agent_framework/web/docs/examples/simple_agent.py +0 -0
  180. {agent_framework_lib-0.6.3 → agent_framework_lib-0.6.3.post3}/agent_framework/web/docs/examples/skills_demo_agent.py +0 -0
  181. {agent_framework_lib-0.6.3 → agent_framework_lib-0.6.3.post3}/agent_framework/web/docs/installation-guide.md +0 -0
  182. {agent_framework_lib-0.6.3 → agent_framework_lib-0.6.3.post3}/agent_framework/web/documentation_generator.py +0 -0
  183. {agent_framework_lib-0.6.3 → agent_framework_lib-0.6.3.post3}/agent_framework/web/helper_agent.py +0 -0
  184. {agent_framework_lib-0.6.3 → agent_framework_lib-0.6.3.post3}/agent_framework/web/helper_ui.html +0 -0
  185. {agent_framework_lib-0.6.3 → agent_framework_lib-0.6.3.post3}/agent_framework/web/modern_ui.html +0 -0
  186. {agent_framework_lib-0.6.3 → agent_framework_lib-0.6.3.post3}/agent_framework/web/observability/kibana-llm-dashboard-setup.json +0 -0
  187. {agent_framework_lib-0.6.3 → agent_framework_lib-0.6.3.post3}/agent_framework/web/observability/kibana-resource-metrics-dashboard.json +0 -0
  188. {agent_framework_lib-0.6.3 → agent_framework_lib-0.6.3.post3}/agent_framework/web/otel_tracing_middleware.py +0 -0
  189. {agent_framework_lib-0.6.3 → agent_framework_lib-0.6.3.post3}/agent_framework/web/server.py +0 -0
  190. {agent_framework_lib-0.6.3 → agent_framework_lib-0.6.3.post3}/agent_framework/web/test_app.html +0 -0
  191. {agent_framework_lib-0.6.3 → agent_framework_lib-0.6.3.post3}/agent_framework_lib.egg-info/dependency_links.txt +0 -0
  192. {agent_framework_lib-0.6.3 → agent_framework_lib-0.6.3.post3}/agent_framework_lib.egg-info/entry_points.txt +0 -0
  193. {agent_framework_lib-0.6.3 → agent_framework_lib-0.6.3.post3}/agent_framework_lib.egg-info/top_level.txt +0 -0
  194. {agent_framework_lib-0.6.3 → agent_framework_lib-0.6.3.post3}/docs/A2A_GUIDE.md +0 -0
  195. {agent_framework_lib-0.6.3 → agent_framework_lib-0.6.3.post3}/docs/ACTIVITY_OUTPUT_PART.md +0 -0
  196. {agent_framework_lib-0.6.3 → agent_framework_lib-0.6.3.post3}/docs/ARCHITECTURE_DIAGRAM.md +0 -0
  197. {agent_framework_lib-0.6.3 → agent_framework_lib-0.6.3.post3}/docs/CONCURRENCE_VS_PARALLELISME_GUIDE.md +0 -0
  198. {agent_framework_lib-0.6.3 → agent_framework_lib-0.6.3.post3}/docs/CREATING_AGENTS.md +0 -0
  199. {agent_framework_lib-0.6.3 → agent_framework_lib-0.6.3.post3}/docs/DOCKER_SETUP.md +0 -0
  200. {agent_framework_lib-0.6.3 → agent_framework_lib-0.6.3.post3}/docs/ELASTICSEARCH_DATA_STRUCTURES.md +0 -0
  201. {agent_framework_lib-0.6.3 → agent_framework_lib-0.6.3.post3}/docs/FILE_DOWNLOAD_LINKS.md +0 -0
  202. {agent_framework_lib-0.6.3 → agent_framework_lib-0.6.3.post3}/docs/FILE_STORAGE_GUIDE.md +0 -0
  203. {agent_framework_lib-0.6.3 → agent_framework_lib-0.6.3.post3}/docs/GETTING_STARTED.md +0 -0
  204. {agent_framework_lib-0.6.3 → agent_framework_lib-0.6.3.post3}/docs/HISTORY_MESSAGE_FORMAT.md +0 -0
  205. {agent_framework_lib-0.6.3 → agent_framework_lib-0.6.3.post3}/docs/IMPLEMENTATION_GUIDE_NEW_AGENT.md +0 -0
  206. {agent_framework_lib-0.6.3 → agent_framework_lib-0.6.3.post3}/docs/MEMORY_INSTALLATION.md +0 -0
  207. {agent_framework_lib-0.6.3 → agent_framework_lib-0.6.3.post3}/docs/MODIFICATIONS_CONCURRENCE.md +0 -0
  208. {agent_framework_lib-0.6.3 → agent_framework_lib-0.6.3.post3}/docs/MULTIMODAL_TOOLS_GUIDE.md +0 -0
  209. {agent_framework_lib-0.6.3 → agent_framework_lib-0.6.3.post3}/docs/OBSERVABILITY_GUIDE.md +0 -0
  210. {agent_framework_lib-0.6.3 → agent_framework_lib-0.6.3.post3}/docs/PYPI_PUBLISHING.md +0 -0
  211. {agent_framework_lib-0.6.3 → agent_framework_lib-0.6.3.post3}/docs/QA_STREAMING_LATENCY.md +0 -0
  212. {agent_framework_lib-0.6.3 → agent_framework_lib-0.6.3.post3}/docs/SPEC_CROSS_MODEL_HISTORY_CONVERSION.md +0 -0
  213. {agent_framework_lib-0.6.3 → agent_framework_lib-0.6.3.post3}/docs/STREAMING_EVENTS_FRONTEND.md +0 -0
  214. {agent_framework_lib-0.6.3 → agent_framework_lib-0.6.3.post3}/docs/TOOLS_AND_MCP_GUIDE.md +0 -0
  215. {agent_framework_lib-0.6.3 → agent_framework_lib-0.6.3.post3}/docs/api-reference.md +0 -0
  216. {agent_framework_lib-0.6.3 → agent_framework_lib-0.6.3.post3}/docs/configuration.md +0 -0
  217. {agent_framework_lib-0.6.3 → agent_framework_lib-0.6.3.post3}/docs/framework_audit_remarques.md +0 -0
  218. {agent_framework_lib-0.6.3 → agent_framework_lib-0.6.3.post3}/docs/installation-guide.md +0 -0
  219. {agent_framework_lib-0.6.3 → agent_framework_lib-0.6.3.post3}/examples/README.md +0 -0
  220. {agent_framework_lib-0.6.3 → agent_framework_lib-0.6.3.post3}/examples/agent_context_budget_test.py +0 -0
  221. {agent_framework_lib-0.6.3 → agent_framework_lib-0.6.3.post3}/examples/agent_example_multi_skills.py +0 -0
  222. {agent_framework_lib-0.6.3 → agent_framework_lib-0.6.3.post3}/examples/agent_exemple_test.py +0 -0
  223. {agent_framework_lib-0.6.3 → agent_framework_lib-0.6.3.post3}/examples/agent_training_with_apo.py +0 -0
  224. {agent_framework_lib-0.6.3 → agent_framework_lib-0.6.3.post3}/examples/agent_with_custom_tools_file_storage.py +0 -0
  225. {agent_framework_lib-0.6.3 → agent_framework_lib-0.6.3.post3}/examples/agent_with_file_storage.py +0 -0
  226. {agent_framework_lib-0.6.3 → agent_framework_lib-0.6.3.post3}/examples/agent_with_mcp.py +0 -0
  227. {agent_framework_lib-0.6.3 → agent_framework_lib-0.6.3.post3}/examples/agent_with_memory_graphiti.py +0 -0
  228. {agent_framework_lib-0.6.3 → agent_framework_lib-0.6.3.post3}/examples/agent_with_memory_hybrid.py +0 -0
  229. {agent_framework_lib-0.6.3 → agent_framework_lib-0.6.3.post3}/examples/agent_with_memory_simple.py +0 -0
  230. {agent_framework_lib-0.6.3 → agent_framework_lib-0.6.3.post3}/examples/agent_with_personalization.py +0 -0
  231. {agent_framework_lib-0.6.3 → agent_framework_lib-0.6.3.post3}/examples/biagenttest.py +0 -0
  232. {agent_framework_lib-0.6.3 → agent_framework_lib-0.6.3.post3}/examples/custom_framework_agent.py +0 -0
  233. {agent_framework_lib-0.6.3 → agent_framework_lib-0.6.3.post3}/examples/dependencies/docker-compose.yaml +0 -0
  234. {agent_framework_lib-0.6.3 → agent_framework_lib-0.6.3.post3}/examples/pyproject.toml +0 -0
  235. {agent_framework_lib-0.6.3 → agent_framework_lib-0.6.3.post3}/examples/simple_agent.py +0 -0
  236. {agent_framework_lib-0.6.3 → agent_framework_lib-0.6.3.post3}/examples/skills_demo_agent.py +0 -0
  237. {agent_framework_lib-0.6.3 → agent_framework_lib-0.6.3.post3}/setup.cfg +0 -0
  238. {agent_framework_lib-0.6.3 → agent_framework_lib-0.6.3.post3}/setup.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: agent-framework-lib
3
- Version: 0.6.3
3
+ Version: 0.6.3.post3
4
4
  Summary: A comprehensive Python framework for building and serving conversational AI agents with FastAPI
5
5
  Author-email: Sebastian Pavel <sebastian@cinco.ai>, Elliott Girard <elliott.girard@icloud.com>
6
6
  Maintainer-email: Sebastian Pavel <sebastian@cinco.ai>
@@ -53,6 +53,8 @@ Requires-Dist: markdown>=3.5
53
53
  Requires-Dist: playwright>=1.56.0
54
54
  Requires-Dist: elasticsearch<9.0.0,>=8.11.0
55
55
  Requires-Dist: ddgs>=9.9.3
56
+ Requires-Dist: httpx>=0.28.1
57
+ Requires-Dist: beautifulsoup4>=4.12.0
56
58
  Requires-Dist: llama-index>=0.14.16
57
59
  Requires-Dist: llama-index-core>=0.14.16
58
60
  Requires-Dist: llama-index-workflows>=2.16.0
@@ -88,7 +88,7 @@ def _auto_setup_dependencies() -> None:
88
88
  # Track if auto-setup has been done
89
89
  _AUTO_SETUP_DONE = False
90
90
 
91
- __version__ = "0.6.3"
91
+ __version__ = "0.6.3post3"
92
92
  __author__ = "Cinco AI Team"
93
93
  __license__ = "MIT"
94
94
  __email__ = "sebastian@cinco.ai"
@@ -0,0 +1,10 @@
1
+ """Capability resolution for token-based access control."""
2
+
3
+ from .resolver import CapabilityResolverError, CapabilityResolver, CapabilitySet
4
+
5
+
6
+ __all__ = [
7
+ "CapabilitySet",
8
+ "CapabilityResolver",
9
+ "CapabilityResolverError",
10
+ ]
@@ -0,0 +1,175 @@
1
+ """
2
+ CapabilitySet and CapabilityResolver — token-based capability resolution.
3
+ """
4
+
5
+ from __future__ import annotations
6
+
7
+ import contextlib
8
+ import json
9
+ import logging
10
+ import os
11
+ from collections.abc import Iterator
12
+ from dataclasses import dataclass, field
13
+ from pathlib import Path
14
+ from typing import Any
15
+
16
+
17
+ logger = logging.getLogger(__name__)
18
+
19
+
20
+ @dataclass
21
+ class CapabilitySet:
22
+ """Set of capabilities authorized for a session."""
23
+
24
+ skill_ids: list[str] = field(default_factory=list)
25
+ native_tools: list[str] = field(default_factory=list)
26
+ max_subagent_depth: int = 0
27
+ env_overrides: dict[str, str] = field(default_factory=dict)
28
+
29
+ @classmethod
30
+ def empty(cls) -> "CapabilitySet":
31
+ """Return an empty CapabilitySet (no capabilities authorized)."""
32
+ return cls()
33
+
34
+
35
+ class CapabilityResolverError(Exception):
36
+ """Raised when the capability source is unreachable or invalid."""
37
+
38
+
39
+ class CapabilityResolver:
40
+ """Resolves authorized capabilities from a token.
41
+
42
+ Sources are checked in priority order:
43
+ 1. CAPABILITY_MAP environment variable (JSON inline)
44
+ 2. Local JSON file (json_path)
45
+ 3. HTTP endpoint (http_url)
46
+ """
47
+
48
+ def __init__(
49
+ self,
50
+ json_path: Path | None = None,
51
+ http_url: str | None = None,
52
+ ) -> None:
53
+ """Initialize CapabilityResolver.
54
+
55
+ Args:
56
+ json_path: Optional path to a local JSON capability map.
57
+ http_url: Optional HTTP URL to fetch capabilities from.
58
+ """
59
+ self._json_path = json_path
60
+ self._http_url = http_url
61
+
62
+ def resolve(self, api_token: str) -> CapabilitySet:
63
+ """Return the CapabilitySet authorized for api_token.
64
+
65
+ Unknown tokens return CapabilitySet.empty().
66
+
67
+ Args:
68
+ api_token: The API token to resolve.
69
+
70
+ Returns:
71
+ CapabilitySet for the token.
72
+
73
+ Raises:
74
+ CapabilityResolverError: If the configured source is unreachable or invalid.
75
+ """
76
+ capability_map = self._load_map()
77
+ data = capability_map.get(api_token)
78
+ if data is None:
79
+ return CapabilitySet.empty()
80
+ return self._parse_capability_set(data)
81
+
82
+ @contextlib.contextmanager
83
+ def apply_env_overrides(self, capability_set: CapabilitySet) -> Iterator[None]:
84
+ """Context manager that injects env_overrides into os.environ.
85
+
86
+ Keys that were absent before the block are removed on exit.
87
+ Keys that existed before are restored to their original value.
88
+
89
+ Args:
90
+ capability_set: CapabilitySet whose env_overrides to apply.
91
+ """
92
+ overrides = capability_set.env_overrides
93
+ previous: dict[str, str | None] = {}
94
+ for key in overrides:
95
+ previous[key] = os.environ.get(key)
96
+ os.environ[key] = overrides[key]
97
+ try:
98
+ yield
99
+ finally:
100
+ for key, old_value in previous.items():
101
+ if old_value is None:
102
+ os.environ.pop(key, None)
103
+ else:
104
+ os.environ[key] = old_value
105
+
106
+ # ------------------------------------------------------------------
107
+ # Internal helpers
108
+ # ------------------------------------------------------------------
109
+
110
+ def _load_map(self) -> dict[str, Any]:
111
+ """Load the capability map from the highest-priority source."""
112
+ env_json = os.environ.get("CAPABILITY_MAP")
113
+ if env_json:
114
+ try:
115
+ data = json.loads(env_json)
116
+ if not isinstance(data, dict):
117
+ raise CapabilityResolverError(
118
+ "CAPABILITY_MAP must be a JSON object"
119
+ )
120
+ return data
121
+ except json.JSONDecodeError as exc:
122
+ raise CapabilityResolverError(
123
+ f"Invalid JSON in CAPABILITY_MAP: {exc}"
124
+ ) from exc
125
+
126
+ if self._json_path is not None:
127
+ try:
128
+ raw = Path(self._json_path).read_text(encoding="utf-8")
129
+ data = json.loads(raw)
130
+ if not isinstance(data, dict):
131
+ raise CapabilityResolverError(
132
+ f"JSON file must contain an object: {self._json_path}"
133
+ )
134
+ return data
135
+ except (OSError, json.JSONDecodeError) as exc:
136
+ raise CapabilityResolverError(
137
+ f"Cannot load capability map from {self._json_path}: {exc}"
138
+ ) from exc
139
+
140
+ if self._http_url is not None:
141
+ try:
142
+ import httpx
143
+
144
+ resp = httpx.get(self._http_url, timeout=10)
145
+ resp.raise_for_status()
146
+ data = resp.json()
147
+ if not isinstance(data, dict):
148
+ raise CapabilityResolverError(
149
+ f"HTTP response must be a JSON object: {self._http_url}"
150
+ )
151
+ return data
152
+ except Exception as exc:
153
+ raise CapabilityResolverError(
154
+ f"Cannot reach capability source at {self._http_url}: {exc}"
155
+ ) from exc
156
+
157
+ return {}
158
+
159
+ @staticmethod
160
+ def _parse_capability_set(data: Any) -> CapabilitySet:
161
+ if not isinstance(data, dict):
162
+ return CapabilitySet.empty()
163
+ return CapabilitySet(
164
+ skill_ids=list(data.get("skill_ids") or []),
165
+ native_tools=list(data.get("native_tools") or []),
166
+ max_subagent_depth=int(data.get("max_subagent_depth") or 0),
167
+ env_overrides=dict(data.get("env_overrides") or {}),
168
+ )
169
+
170
+
171
+ __all__ = [
172
+ "CapabilitySet",
173
+ "CapabilityResolver",
174
+ "CapabilityResolverError",
175
+ ]
@@ -29,6 +29,11 @@ logger = logging.getLogger(__name__)
29
29
 
30
30
  TRUNCATION_MARKER = "\n\n[... contenu tronqué ...]\n\n"
31
31
 
32
+ # OpenAI enforces a 10 485 760 character limit per message content.
33
+ # We use a conservative 8M limit to leave headroom for JSON serialization
34
+ # overhead, tool_call metadata, and other wrapper text added by the SDK.
35
+ MAX_MESSAGE_CONTENT_CHARS = 8_000_000
36
+
32
37
 
33
38
  class ScratchpadCompressor:
34
39
  """Compresse le scratchpad de la boucle d'outils pour respecter le budget."""
@@ -106,22 +111,100 @@ class ScratchpadCompressor:
106
111
  # Pas de paire trouvée, tout est considéré comme dernière paire
107
112
  return [], list(scratchpad)
108
113
 
114
+ @staticmethod
115
+ def _flatten_message_content(message: ChatMessage) -> str:
116
+ """Aplatit tous les blocks d'un message en une seule chaîne de texte.
117
+
118
+ LlamaIndex stocke le contenu des messages dans des blocks typés
119
+ (TextBlock, ToolCallBlock, ToolResultBlock, etc.). La propriété
120
+ .content ne retourne que le texte des TextBlock. Cette méthode
121
+ sérialise TOUS les blocks pour que la troncature opère sur le
122
+ contenu réel envoyé à l'API.
123
+
124
+ Args:
125
+ message: ChatMessage à aplatir.
126
+
127
+ Returns:
128
+ Texte complet incluant tous les blocks.
129
+ """
130
+ if not hasattr(message, "blocks") or not message.blocks:
131
+ return message.content or ""
132
+
133
+ parts: list[str] = []
134
+ for block in message.blocks:
135
+ block_type = type(block).__name__
136
+ if hasattr(block, "text"):
137
+ parts.append(block.text or "")
138
+ elif hasattr(block, "tool_kwargs"):
139
+ tool_name = getattr(block, "tool_name", "") or ""
140
+ kwargs = getattr(block, "tool_kwargs", None)
141
+ if kwargs:
142
+ import json as _json
143
+ try:
144
+ kwargs_str = _json.dumps(kwargs) if isinstance(kwargs, dict) else str(kwargs)
145
+ except (TypeError, ValueError):
146
+ kwargs_str = str(kwargs)
147
+ parts.append(f"[tool_call: {tool_name}({kwargs_str})]")
148
+ else:
149
+ parts.append(f"[tool_call: {tool_name}()]")
150
+ elif hasattr(block, "tool_output"):
151
+ output = str(getattr(block, "tool_output", "") or "")
152
+ parts.append(output)
153
+ elif block_type == "ImageBlock":
154
+ parts.append("[image]")
155
+ else:
156
+ try:
157
+ block_str = str(block)
158
+ if len(block_str) > 10:
159
+ parts.append(block_str)
160
+ except Exception:
161
+ parts.append("[block non sérialisable]")
162
+
163
+ return "\n".join(parts)
164
+
109
165
  def _truncate_single_result(
110
166
  self, message: ChatMessage, target_tokens: int
111
167
  ) -> ChatMessage:
112
168
  """Tronque le contenu d'un tool_result en gardant début + marqueur + fin.
113
169
 
170
+ Aplatit d'abord tous les blocks en texte brut pour que la troncature
171
+ opère sur le contenu réel (pas seulement .content/TextBlock).
172
+
173
+ Applique deux niveaux de troncature :
174
+ 1. Par tokens (budget du context window)
175
+ 2. Par caractères bruts (limite API OpenAI de 10M chars par message)
176
+
114
177
  Args:
115
178
  message: Message tool_result à tronquer.
116
179
  target_tokens: Nombre cible de tokens pour le message tronqué.
117
180
 
118
181
  Returns:
119
- Nouveau ChatMessage avec le contenu tronqué.
182
+ Nouveau ChatMessage avec le contenu tronqué (blocks nettoyés).
120
183
  """
121
- content = message.content or ""
184
+ content = self._flatten_message_content(message)
185
+
186
+ # Garde-fou caractères bruts : tronquer d'abord si on dépasse la
187
+ # limite API (10M chars) pour éviter un 400 même si le budget
188
+ # tokens semble OK.
189
+ content = self._enforce_char_limit(content)
190
+
122
191
  current_tokens = self._token_counter.count_tokens(content).count
123
192
 
193
+ # Nettoyer additional_kwargs pour ne pas conserver les blocks originaux
194
+ # qui seraient re-sérialisés par LlamaIndex en plus du content tronqué.
195
+ clean_kwargs = {
196
+ k: v
197
+ for k, v in (message.additional_kwargs or {}).items()
198
+ if k not in ("tool_calls",)
199
+ }
200
+
124
201
  if current_tokens <= target_tokens or target_tokens <= 0:
202
+ if content != (message.content or ""):
203
+ return ChatMessage(
204
+ role=message.role,
205
+ content=content,
206
+ additional_kwargs=clean_kwargs,
207
+ )
125
208
  return message
126
209
 
127
210
  # Réserver des tokens pour le marqueur de troncature
@@ -142,6 +225,12 @@ class ScratchpadCompressor:
142
225
  end_chars = max(0, int(end_tokens * chars_per_token))
143
226
 
144
227
  if begin_chars + end_chars >= len(content):
228
+ if content != (message.content or ""):
229
+ return ChatMessage(
230
+ role=message.role,
231
+ content=content,
232
+ additional_kwargs=clean_kwargs,
233
+ )
145
234
  return message
146
235
 
147
236
  begin_part = content[:begin_chars]
@@ -151,9 +240,40 @@ class ScratchpadCompressor:
151
240
  return ChatMessage(
152
241
  role=message.role,
153
242
  content=truncated_content,
154
- additional_kwargs=message.additional_kwargs,
243
+ additional_kwargs=clean_kwargs,
155
244
  )
156
245
 
246
+ @staticmethod
247
+ def _enforce_char_limit(content: str, limit: int = MAX_MESSAGE_CONTENT_CHARS) -> str:
248
+ """Tronque le contenu si sa longueur dépasse la limite en caractères.
249
+
250
+ Garde 60% du début et 40% de la fin pour préserver le contexte.
251
+
252
+ Args:
253
+ content: Texte brut du message.
254
+ limit: Limite en caractères (défaut : MAX_MESSAGE_CONTENT_CHARS).
255
+
256
+ Returns:
257
+ Contenu tronqué si nécessaire, sinon inchangé.
258
+ """
259
+ if len(content) <= limit:
260
+ return content
261
+
262
+ marker_len = len(TRUNCATION_MARKER)
263
+ available = max(0, limit - marker_len)
264
+ begin_chars = int(available * 0.6)
265
+ end_chars = available - begin_chars
266
+
267
+ logger.warning(
268
+ "Message content exceeds char limit (%d > %d), truncating",
269
+ len(content),
270
+ limit,
271
+ )
272
+
273
+ begin_part = content[:begin_chars]
274
+ end_part = content[-end_chars:] if end_chars > 0 else ""
275
+ return begin_part + TRUNCATION_MARKER + end_part
276
+
157
277
  async def compress(
158
278
  self,
159
279
  scratchpad: list[ChatMessage],
@@ -316,7 +436,7 @@ class ScratchpadCompressor:
316
436
  result = list(old_entries)
317
437
  while result and total > target_tokens:
318
438
  removed = result.pop(0)
319
- total -= self._token_counter.count_tokens(removed.content or "").count
439
+ total -= self._count_tokens([removed])
320
440
  return result
321
441
 
322
442
  def _truncate_last_pair(
@@ -326,6 +446,9 @@ class ScratchpadCompressor:
326
446
  ) -> list[ChatMessage]:
327
447
  """Tronque le tool_result dans la dernière paire pour respecter le budget.
328
448
 
449
+ Utilise _count_tokens (blocks-aware) pour compter les tokens du
450
+ message ASSISTANT, pas seulement .content.
451
+
329
452
  Args:
330
453
  last_pair: Dernière paire de messages (tool_call + tool_result).
331
454
  available_tokens: Tokens disponibles pour toute la paire.
@@ -341,7 +464,7 @@ class ScratchpadCompressor:
341
464
  remaining = max(0, available_tokens - tokens_used)
342
465
  result.append(self._truncate_single_result(msg, remaining))
343
466
  else:
344
- tokens_used += self._token_counter.count_tokens(msg.content or "").count
467
+ tokens_used += self._count_tokens([msg])
345
468
  result.append(msg)
346
469
 
347
470
  return result
@@ -20,6 +20,7 @@ from llama_index.core.workflow import Context
20
20
 
21
21
  from agent_framework.core.knowledge_state import ToolCallSignature
22
22
  from agent_framework.core.loop_detector import LoopDetector
23
+ from agent_framework.core.scratchpad_compressor import MAX_MESSAGE_CONTENT_CHARS
23
24
 
24
25
  if TYPE_CHECKING:
25
26
  from agent_framework.core.context_budget import ContextBudgetManager
@@ -176,14 +177,66 @@ class BudgetAwareFunctionAgent(FunctionAgent):
176
177
  "Compressing scratchpad: usage %.1f%% >= 80%% threshold",
177
178
  usage_percent,
178
179
  )
179
- compressed_scratchpad = await self._scratchpad_compressor.compress(
180
- scratchpad=scratchpad,
181
- llm_input=llm_input,
182
- context_window=context_window,
183
- sacred_zone_tokens=sacred_zone_tokens,
184
- model_name=getattr(self.llm, "model", None),
185
- )
186
- await ctx.store.set(self.scratchpad_key, compressed_scratchpad)
180
+
181
+ # Boucle de compression : re-vérifier après chaque passe
182
+ # car la première compression peut ne pas suffire (ex: blocks
183
+ # non aplatis, écart de tokenisation).
184
+ max_compression_passes = 3
185
+ for compression_pass in range(max_compression_passes):
186
+ compressed_scratchpad = await self._scratchpad_compressor.compress(
187
+ scratchpad=scratchpad,
188
+ llm_input=llm_input,
189
+ context_window=context_window,
190
+ sacred_zone_tokens=sacred_zone_tokens,
191
+ model_name=getattr(self.llm, "model", None),
192
+ )
193
+ await ctx.store.set(self.scratchpad_key, compressed_scratchpad)
194
+
195
+ # Recalculer le total après compression
196
+ new_scratchpad_tokens = self._count_total_tokens([], compressed_scratchpad)
197
+ new_total = sacred_zone_tokens + llm_input_tokens + new_scratchpad_tokens
198
+ new_usage = new_total / context_window * 100 if context_window > 0 else 0.0
199
+
200
+ logger.info(
201
+ "Post-compression pass %d: %d/%d tokens (%.1f%%), scratchpad=%d",
202
+ compression_pass + 1,
203
+ new_total,
204
+ context_window,
205
+ new_usage,
206
+ new_scratchpad_tokens,
207
+ )
208
+
209
+ if new_usage < 80:
210
+ break
211
+
212
+ # Préparer la prochaine passe avec le scratchpad compressé
213
+ scratchpad = compressed_scratchpad
214
+
215
+ if compression_pass == max_compression_passes - 1:
216
+ logger.error(
217
+ "Scratchpad still at %.1f%% after %d compression passes",
218
+ new_usage,
219
+ max_compression_passes,
220
+ )
221
+
222
+ # Garde-fou caractères bruts : même après compression en tokens,
223
+ # un message peut dépasser la limite API (10M chars). On tronque
224
+ # chaque message du scratchpad qui dépasse avant l'appel LLM.
225
+ if self._scratchpad_compressor:
226
+ scratchpad = await ctx.store.get(self.scratchpad_key, default=[])
227
+ patched = False
228
+ for i, msg in enumerate(scratchpad):
229
+ content = msg.content or ""
230
+ if len(content) > MAX_MESSAGE_CONTENT_CHARS:
231
+ from agent_framework.core.scratchpad_compressor import ScratchpadCompressor
232
+ scratchpad[i] = ChatMessage(
233
+ role=msg.role,
234
+ content=ScratchpadCompressor._enforce_char_limit(content),
235
+ additional_kwargs=msg.additional_kwargs,
236
+ )
237
+ patched = True
238
+ if patched:
239
+ await ctx.store.set(self.scratchpad_key, scratchpad)
187
240
 
188
241
  return await super().take_step(ctx, llm_input, tools, memory)
189
242
 
@@ -61,9 +61,18 @@ logger = logging.getLogger(__name__)
61
61
 
62
62
 
63
63
  def _detect_is_error(tool_output: str) -> bool:
64
- """Return True if tool_output contains an error marker (case-insensitive)."""
64
+ """Return True if tool_output contains an error marker (case-insensitive).
65
+
66
+ Excludes false positives like 'isError=False' or 'is_error: false' where
67
+ the word 'error' appears only as part of a negative flag.
68
+ """
69
+ import re
70
+
65
71
  lowered = tool_output.lower()
66
- return any(marker in lowered for marker in ("error", "exception", "failed"))
72
+ # Strip patterns like "iserror=false", "is_error=false", "iserror: false"
73
+ # before checking for error markers to avoid false positives from MCP tool output.
74
+ cleaned = re.sub(r"is_?error\s*[=:]\s*false", "", lowered)
75
+ return any(marker in cleaned for marker in ("error", "exception", "failed"))
67
76
 
68
77
 
69
78
  class _StreamingActivityAccumulator:
@@ -1104,8 +1113,12 @@ class LlamaIndexAgent(BaseAgent):
1104
1113
  tool_name = getattr(event, "tool_name", "unknown_tool")
1105
1114
  tool_kwargs = getattr(event, "tool_kwargs", {}) or {}
1106
1115
  call_id = getattr(event, "call_id", None) or "unknown"
1107
- tool_output = str(getattr(event, "tool_output", ""))
1108
- is_error = _detect_is_error(tool_output)
1116
+ raw_tool_output = getattr(event, "tool_output", "")
1117
+ tool_output = str(raw_tool_output)
1118
+ # Prefer the structured is_error flag from ToolOutput when available,
1119
+ # fall back to text-based heuristic detection.
1120
+ native_is_error = getattr(raw_tool_output, "is_error", None)
1121
+ is_error = native_is_error if isinstance(native_is_error, bool) else _detect_is_error(tool_output)
1109
1122
  ts = datetime.now(timezone.utc).isoformat()
1110
1123
 
1111
1124
  self._pending_events.append({
@@ -28,6 +28,7 @@ The mixin provides:
28
28
 
29
29
  import logging
30
30
  from collections.abc import Callable
31
+ from pathlib import Path
31
32
  from typing import TYPE_CHECKING, Any
32
33
 
33
34
 
@@ -126,23 +127,21 @@ class SkillsMixin:
126
127
  self._skill_registry.unload(name)
127
128
  logger.info(f"Unloaded skill '{name}' for agent '{getattr(self, 'agent_id', 'unknown')}'")
128
129
 
129
- def register_builtin_skills(self) -> None:
130
- """
131
- Register all built-in skills with this agent.
130
+ def register_builtin_skills(self, source: str = "python") -> None:
131
+ """Register all built-in skills with this agent.
132
132
 
133
- This method registers all pre-built skills from the builtin module,
134
- making them available for discovery and loading.
135
-
136
- Note: Skills that are already registered will be skipped to avoid
137
- duplicate registration.
133
+ Args:
134
+ source: Loading strategy passed to ``get_all_builtin_skills``.
135
+ Use ``"python"`` (default) for Python factory functions or
136
+ ``"markdown"`` to load from SKILL.md files.
137
+ Skills already registered are skipped.
138
138
  """
139
139
  from .builtin import get_all_builtin_skills
140
140
 
141
- builtin_skills = get_all_builtin_skills()
141
+ builtin_skills = get_all_builtin_skills(source=source) # type: ignore[call-arg]
142
142
  registered_count = 0
143
-
143
+
144
144
  for skill in builtin_skills:
145
- # Skip if already registered
146
145
  if self._skill_registry.get(skill.metadata.name) is not None:
147
146
  continue
148
147
  self.register_skill(skill)
@@ -151,7 +150,7 @@ class SkillsMixin:
151
150
  if registered_count > 0:
152
151
  logger.info(
153
152
  f"Registered {registered_count} built-in skills "
154
- f"for agent '{getattr(self, 'agent_id', 'unknown')}'"
153
+ f"for agent '{getattr(self, 'agent_id', 'unknown')}' (source={source!r})"
155
154
  )
156
155
  elif builtin_skills:
157
156
  logger.debug(
@@ -328,6 +327,59 @@ class SkillsMixin:
328
327
  logger.info(f"🔧 Configured context for {configured_count} skill tools")
329
328
 
330
329
 
330
+ def register_skills_from_dir(self, skills_dir: Path) -> None:
331
+ """Load all SKILL.md files from a directory and register them.
332
+
333
+ Native tools (ShellTool, WebFetchTool) are attached to each loaded skill
334
+ if not already present.
335
+
336
+ Args:
337
+ skills_dir: Directory containing SKILL.md files.
338
+
339
+ Raises:
340
+ ValueError: If the directory does not exist.
341
+ """
342
+ from agent_framework.skills.markdown_loader import MarkdownSkillLoader
343
+ from agent_framework.tools.shell_tool import ShellTool
344
+ from agent_framework.tools.web_fetch_tool import WebFetchTool
345
+
346
+ skills_dir = Path(skills_dir)
347
+ if not skills_dir.exists() or not skills_dir.is_dir():
348
+ raise ValueError(f"Skills directory not found: {skills_dir}")
349
+
350
+ loader = MarkdownSkillLoader()
351
+ skills = loader.load_from_dir(skills_dir)
352
+
353
+ for skill in skills:
354
+ existing_types = {type(t) for t in skill.tools}
355
+ if ShellTool not in existing_types:
356
+ skill.tools.append(ShellTool())
357
+ if WebFetchTool not in existing_types:
358
+ skill.tools.append(WebFetchTool())
359
+ try:
360
+ self._skill_registry.register(skill)
361
+ logger.debug("Registered SKILL.md skill '%s'", skill.metadata.name)
362
+ except Exception as exc:
363
+ logger.warning(
364
+ "Could not register skill '%s': %s", skill.metadata.name, exc
365
+ )
366
+
367
+ def apply_capability_set(self, capability_set: "Any") -> None:
368
+ """Filter the SkillRegistry to only keep skills in capability_set.skill_ids.
369
+
370
+ This method is idempotent: applying the same CapabilitySet twice produces
371
+ the same registry state.
372
+
373
+ Args:
374
+ capability_set: A CapabilitySet instance with a skill_ids attribute.
375
+ """
376
+ allowed = set(capability_set.skill_ids)
377
+ all_names = [m.name for m in self._skill_registry.list_all()]
378
+ for name in all_names:
379
+ if name not in allowed:
380
+ self._skill_registry.unregister(name)
381
+
382
+
331
383
  __all__ = [
332
384
  "SkillsMixin",
333
385
  ]
@@ -52,6 +52,10 @@ class SkillMetadata(BaseModel):
52
52
  default="general", description="Category: document, data, web, visualization, etc."
53
53
  )
54
54
  version: str = Field(default="1.0.0", description="Skill version")
55
+ requires_bins: list[str] = Field(default_factory=list, description="Required system binaries")
56
+ requires_env: list[str] = Field(
57
+ default_factory=list, description="Required environment variables"
58
+ )
55
59
 
56
60
  def matches_query(self, query: str) -> bool:
57
61
  """