mojentic 0.5.7__tar.gz → 0.6.1__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 (137) hide show
  1. {mojentic-0.5.7 → mojentic-0.6.1}/PKG-INFO +20 -10
  2. {mojentic-0.5.7 → mojentic-0.6.1}/README.md +6 -7
  3. {mojentic-0.5.7 → mojentic-0.6.1}/pyproject.toml +17 -3
  4. {mojentic-0.5.7 → mojentic-0.6.1}/src/_examples/recursive_agent.py +4 -3
  5. mojentic-0.6.1/src/_examples/tracer_demo.py +170 -0
  6. {mojentic-0.5.7 → mojentic-0.6.1}/src/mojentic/agents/simple_recursive_agent.py +20 -21
  7. {mojentic-0.5.7 → mojentic-0.6.1}/src/mojentic/dispatcher.py +17 -1
  8. {mojentic-0.5.7 → mojentic-0.6.1}/src/mojentic/llm/gateways/openai_message_adapter_spec.py +7 -7
  9. {mojentic-0.5.7 → mojentic-0.6.1}/src/mojentic/llm/gateways/openai_messages_adapter.py +58 -15
  10. {mojentic-0.5.7 → mojentic-0.6.1}/src/mojentic/llm/llm_broker.py +97 -4
  11. {mojentic-0.5.7 → mojentic-0.6.1}/src/mojentic/llm/message_composers_spec.py +43 -25
  12. mojentic-0.6.1/src/mojentic/llm/tools/llm_tool.py +60 -0
  13. {mojentic-0.5.7 → mojentic-0.6.1}/src/mojentic/llm/tools/llm_tool_spec.py +2 -1
  14. mojentic-0.6.1/src/mojentic/tracer/__init__.py +16 -0
  15. mojentic-0.6.1/src/mojentic/tracer/event_store.py +111 -0
  16. mojentic-0.6.1/src/mojentic/tracer/event_store_spec.py +210 -0
  17. mojentic-0.6.1/src/mojentic/tracer/null_tracer.py +203 -0
  18. mojentic-0.6.1/src/mojentic/tracer/tracer_events.py +138 -0
  19. mojentic-0.6.1/src/mojentic/tracer/tracer_events_spec.py +116 -0
  20. mojentic-0.6.1/src/mojentic/tracer/tracer_system.py +301 -0
  21. mojentic-0.6.1/src/mojentic/tracer/tracer_system_spec.py +266 -0
  22. {mojentic-0.5.7 → mojentic-0.6.1}/src/mojentic.egg-info/PKG-INFO +20 -10
  23. {mojentic-0.5.7 → mojentic-0.6.1}/src/mojentic.egg-info/SOURCES.txt +9 -2
  24. mojentic-0.6.1/src/mojentic.egg-info/requires.txt +23 -0
  25. mojentic-0.5.7/src/mojentic/audit/event_store.py +0 -6
  26. mojentic-0.5.7/src/mojentic/audit/event_store_spec.py +0 -26
  27. mojentic-0.5.7/src/mojentic/llm/tools/llm_tool.py +0 -33
  28. mojentic-0.5.7/src/mojentic.egg-info/requires.txt +0 -11
  29. {mojentic-0.5.7 → mojentic-0.6.1}/LICENSE.md +0 -0
  30. {mojentic-0.5.7 → mojentic-0.6.1}/setup.cfg +0 -0
  31. {mojentic-0.5.7 → mojentic-0.6.1}/src/_examples/__init__.py +0 -0
  32. {mojentic-0.5.7 → mojentic-0.6.1}/src/_examples/broker_as_tool.py +0 -0
  33. {mojentic-0.5.7 → mojentic-0.6.1}/src/_examples/broker_examples.py +0 -0
  34. {mojentic-0.5.7 → mojentic-0.6.1}/src/_examples/broker_image_examples.py +0 -0
  35. {mojentic-0.5.7 → mojentic-0.6.1}/src/_examples/characterize_ollama.py +0 -0
  36. {mojentic-0.5.7 → mojentic-0.6.1}/src/_examples/characterize_openai.py +0 -0
  37. {mojentic-0.5.7 → mojentic-0.6.1}/src/_examples/chat_session.py +0 -0
  38. {mojentic-0.5.7 → mojentic-0.6.1}/src/_examples/chat_session_with_tool.py +0 -0
  39. {mojentic-0.5.7 → mojentic-0.6.1}/src/_examples/coding_file_tool.py +0 -0
  40. {mojentic-0.5.7 → mojentic-0.6.1}/src/_examples/current_datetime_tool_example.py +0 -0
  41. {mojentic-0.5.7 → mojentic-0.6.1}/src/_examples/design_analysis.py +0 -0
  42. {mojentic-0.5.7 → mojentic-0.6.1}/src/_examples/embeddings.py +0 -0
  43. {mojentic-0.5.7 → mojentic-0.6.1}/src/_examples/ensures_files_exist.py +0 -0
  44. {mojentic-0.5.7 → mojentic-0.6.1}/src/_examples/ephemeral_task_manager_example.py +0 -0
  45. {mojentic-0.5.7 → mojentic-0.6.1}/src/_examples/file_deduplication.py +0 -0
  46. {mojentic-0.5.7 → mojentic-0.6.1}/src/_examples/file_tool.py +0 -0
  47. {mojentic-0.5.7 → mojentic-0.6.1}/src/_examples/image_analysis.py +0 -0
  48. {mojentic-0.5.7 → mojentic-0.6.1}/src/_examples/image_broker.py +0 -0
  49. {mojentic-0.5.7 → mojentic-0.6.1}/src/_examples/image_broker_splat.py +0 -0
  50. {mojentic-0.5.7 → mojentic-0.6.1}/src/_examples/iterative_solver.py +0 -0
  51. {mojentic-0.5.7 → mojentic-0.6.1}/src/_examples/list_models.py +0 -0
  52. {mojentic-0.5.7 → mojentic-0.6.1}/src/_examples/oversized_embeddings.py +0 -0
  53. {mojentic-0.5.7 → mojentic-0.6.1}/src/_examples/raw.py +0 -0
  54. {mojentic-0.5.7 → mojentic-0.6.1}/src/_examples/react/__init__.py +0 -0
  55. {mojentic-0.5.7 → mojentic-0.6.1}/src/_examples/react/agents/__init__.py +0 -0
  56. {mojentic-0.5.7 → mojentic-0.6.1}/src/_examples/react/agents/decisioning_agent.py +0 -0
  57. {mojentic-0.5.7 → mojentic-0.6.1}/src/_examples/react/agents/thinking_agent.py +0 -0
  58. {mojentic-0.5.7 → mojentic-0.6.1}/src/_examples/react/formatters.py +0 -0
  59. {mojentic-0.5.7 → mojentic-0.6.1}/src/_examples/react/models/__init__.py +0 -0
  60. {mojentic-0.5.7 → mojentic-0.6.1}/src/_examples/react/models/base.py +0 -0
  61. {mojentic-0.5.7 → mojentic-0.6.1}/src/_examples/react/models/events.py +0 -0
  62. {mojentic-0.5.7 → mojentic-0.6.1}/src/_examples/react.py +0 -0
  63. {mojentic-0.5.7 → mojentic-0.6.1}/src/_examples/routed_send_response.py +0 -0
  64. {mojentic-0.5.7 → mojentic-0.6.1}/src/_examples/simple_llm.py +0 -0
  65. {mojentic-0.5.7 → mojentic-0.6.1}/src/_examples/simple_llm_repl.py +0 -0
  66. {mojentic-0.5.7 → mojentic-0.6.1}/src/_examples/simple_structured.py +0 -0
  67. {mojentic-0.5.7 → mojentic-0.6.1}/src/_examples/simple_tool.py +0 -0
  68. {mojentic-0.5.7 → mojentic-0.6.1}/src/_examples/solver_chat_session.py +0 -0
  69. {mojentic-0.5.7 → mojentic-0.6.1}/src/_examples/streaming.py +0 -0
  70. {mojentic-0.5.7 → mojentic-0.6.1}/src/_examples/tell_user_example.py +0 -0
  71. {mojentic-0.5.7 → mojentic-0.6.1}/src/_examples/working_memory.py +0 -0
  72. {mojentic-0.5.7 → mojentic-0.6.1}/src/mojentic/__init__.py +0 -0
  73. {mojentic-0.5.7 → mojentic-0.6.1}/src/mojentic/agents/__init__.py +0 -0
  74. {mojentic-0.5.7 → mojentic-0.6.1}/src/mojentic/agents/agent_broker.py +0 -0
  75. {mojentic-0.5.7 → mojentic-0.6.1}/src/mojentic/agents/base_agent.py +0 -0
  76. {mojentic-0.5.7 → mojentic-0.6.1}/src/mojentic/agents/base_llm_agent.py +0 -0
  77. {mojentic-0.5.7 → mojentic-0.6.1}/src/mojentic/agents/base_llm_agent_spec.py +0 -0
  78. {mojentic-0.5.7 → mojentic-0.6.1}/src/mojentic/agents/correlation_aggregator_agent.py +0 -0
  79. {mojentic-0.5.7 → mojentic-0.6.1}/src/mojentic/agents/iterative_problem_solver.py +0 -0
  80. {mojentic-0.5.7 → mojentic-0.6.1}/src/mojentic/agents/output_agent.py +0 -0
  81. {mojentic-0.5.7 → mojentic-0.6.1}/src/mojentic/context/__init__.py +0 -0
  82. {mojentic-0.5.7 → mojentic-0.6.1}/src/mojentic/context/shared_working_memory.py +0 -0
  83. {mojentic-0.5.7 → mojentic-0.6.1}/src/mojentic/event.py +0 -0
  84. {mojentic-0.5.7 → mojentic-0.6.1}/src/mojentic/llm/__init__.py +0 -0
  85. {mojentic-0.5.7 → mojentic-0.6.1}/src/mojentic/llm/chat_session.py +0 -0
  86. {mojentic-0.5.7 → mojentic-0.6.1}/src/mojentic/llm/chat_session_spec.py +0 -0
  87. {mojentic-0.5.7 → mojentic-0.6.1}/src/mojentic/llm/gateways/__init__.py +0 -0
  88. {mojentic-0.5.7 → mojentic-0.6.1}/src/mojentic/llm/gateways/anthropic.py +0 -0
  89. {mojentic-0.5.7 → mojentic-0.6.1}/src/mojentic/llm/gateways/anthropic_messages_adapter.py +0 -0
  90. {mojentic-0.5.7 → mojentic-0.6.1}/src/mojentic/llm/gateways/embeddings_gateway.py +0 -0
  91. {mojentic-0.5.7 → mojentic-0.6.1}/src/mojentic/llm/gateways/file_gateway.py +0 -0
  92. {mojentic-0.5.7 → mojentic-0.6.1}/src/mojentic/llm/gateways/llm_gateway.py +0 -0
  93. {mojentic-0.5.7 → mojentic-0.6.1}/src/mojentic/llm/gateways/models.py +0 -0
  94. {mojentic-0.5.7 → mojentic-0.6.1}/src/mojentic/llm/gateways/ollama.py +0 -0
  95. {mojentic-0.5.7 → mojentic-0.6.1}/src/mojentic/llm/gateways/ollama_messages_adapter.py +0 -0
  96. {mojentic-0.5.7 → mojentic-0.6.1}/src/mojentic/llm/gateways/ollama_messages_adapter_spec.py +0 -0
  97. {mojentic-0.5.7 → mojentic-0.6.1}/src/mojentic/llm/gateways/openai.py +0 -0
  98. {mojentic-0.5.7 → mojentic-0.6.1}/src/mojentic/llm/gateways/tokenizer_gateway.py +0 -0
  99. {mojentic-0.5.7 → mojentic-0.6.1}/src/mojentic/llm/llm_broker_spec.py +0 -0
  100. {mojentic-0.5.7 → mojentic-0.6.1}/src/mojentic/llm/message_composers.py +0 -0
  101. {mojentic-0.5.7 → mojentic-0.6.1}/src/mojentic/llm/registry/__init__.py +0 -0
  102. {mojentic-0.5.7 → mojentic-0.6.1}/src/mojentic/llm/registry/llm_registry.py +0 -0
  103. {mojentic-0.5.7 → mojentic-0.6.1}/src/mojentic/llm/registry/models.py +0 -0
  104. {mojentic-0.5.7 → mojentic-0.6.1}/src/mojentic/llm/registry/populate_registry_from_ollama.py +0 -0
  105. {mojentic-0.5.7 → mojentic-0.6.1}/src/mojentic/llm/tools/__init__.py +0 -0
  106. {mojentic-0.5.7 → mojentic-0.6.1}/src/mojentic/llm/tools/ask_user_tool.py +0 -0
  107. {mojentic-0.5.7 → mojentic-0.6.1}/src/mojentic/llm/tools/current_datetime.py +0 -0
  108. {mojentic-0.5.7 → mojentic-0.6.1}/src/mojentic/llm/tools/date_resolver.py +0 -0
  109. {mojentic-0.5.7 → mojentic-0.6.1}/src/mojentic/llm/tools/date_resolver_spec.py +0 -0
  110. {mojentic-0.5.7 → mojentic-0.6.1}/src/mojentic/llm/tools/ephemeral_task_manager/__init__.py +0 -0
  111. {mojentic-0.5.7 → mojentic-0.6.1}/src/mojentic/llm/tools/ephemeral_task_manager/append_task_tool.py +0 -0
  112. {mojentic-0.5.7 → mojentic-0.6.1}/src/mojentic/llm/tools/ephemeral_task_manager/append_task_tool_spec.py +0 -0
  113. {mojentic-0.5.7 → mojentic-0.6.1}/src/mojentic/llm/tools/ephemeral_task_manager/clear_tasks_tool.py +0 -0
  114. {mojentic-0.5.7 → mojentic-0.6.1}/src/mojentic/llm/tools/ephemeral_task_manager/clear_tasks_tool_spec.py +0 -0
  115. {mojentic-0.5.7 → mojentic-0.6.1}/src/mojentic/llm/tools/ephemeral_task_manager/complete_task_tool.py +0 -0
  116. {mojentic-0.5.7 → mojentic-0.6.1}/src/mojentic/llm/tools/ephemeral_task_manager/complete_task_tool_spec.py +0 -0
  117. {mojentic-0.5.7 → mojentic-0.6.1}/src/mojentic/llm/tools/ephemeral_task_manager/ephemeral_task_list.py +0 -0
  118. {mojentic-0.5.7 → mojentic-0.6.1}/src/mojentic/llm/tools/ephemeral_task_manager/ephemeral_task_list_spec.py +0 -0
  119. {mojentic-0.5.7 → mojentic-0.6.1}/src/mojentic/llm/tools/ephemeral_task_manager/insert_task_after_tool.py +0 -0
  120. {mojentic-0.5.7 → mojentic-0.6.1}/src/mojentic/llm/tools/ephemeral_task_manager/insert_task_after_tool_spec.py +0 -0
  121. {mojentic-0.5.7 → mojentic-0.6.1}/src/mojentic/llm/tools/ephemeral_task_manager/list_tasks_tool.py +0 -0
  122. {mojentic-0.5.7 → mojentic-0.6.1}/src/mojentic/llm/tools/ephemeral_task_manager/list_tasks_tool_spec.py +0 -0
  123. {mojentic-0.5.7 → mojentic-0.6.1}/src/mojentic/llm/tools/ephemeral_task_manager/prepend_task_tool.py +0 -0
  124. {mojentic-0.5.7 → mojentic-0.6.1}/src/mojentic/llm/tools/ephemeral_task_manager/prepend_task_tool_spec.py +0 -0
  125. {mojentic-0.5.7 → mojentic-0.6.1}/src/mojentic/llm/tools/ephemeral_task_manager/start_task_tool.py +0 -0
  126. {mojentic-0.5.7 → mojentic-0.6.1}/src/mojentic/llm/tools/ephemeral_task_manager/start_task_tool_spec.py +0 -0
  127. {mojentic-0.5.7 → mojentic-0.6.1}/src/mojentic/llm/tools/file_manager.py +0 -0
  128. {mojentic-0.5.7 → mojentic-0.6.1}/src/mojentic/llm/tools/organic_web_search.py +0 -0
  129. {mojentic-0.5.7 → mojentic-0.6.1}/src/mojentic/llm/tools/tell_user_tool.py +0 -0
  130. {mojentic-0.5.7 → mojentic-0.6.1}/src/mojentic/llm/tools/tool_wrapper.py +0 -0
  131. {mojentic-0.5.7 → mojentic-0.6.1}/src/mojentic/llm/tools/tool_wrapper_spec.py +0 -0
  132. {mojentic-0.5.7 → mojentic-0.6.1}/src/mojentic/router.py +0 -0
  133. {mojentic-0.5.7 → mojentic-0.6.1}/src/mojentic/router_spec.py +0 -0
  134. {mojentic-0.5.7 → mojentic-0.6.1}/src/mojentic/utils/__init__.py +0 -0
  135. {mojentic-0.5.7 → mojentic-0.6.1}/src/mojentic/utils/formatting.py +0 -0
  136. {mojentic-0.5.7 → mojentic-0.6.1}/src/mojentic.egg-info/dependency_links.txt +0 -0
  137. {mojentic-0.5.7 → mojentic-0.6.1}/src/mojentic.egg-info/top_level.txt +0 -0
@@ -1,10 +1,10 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: mojentic
3
- Version: 0.5.7
3
+ Version: 0.6.1
4
4
  Summary: Mojentic is an agentic framework that aims to provide a simple and flexible way to assemble teams of agents to solve complex problems.
5
5
  Author-email: Stacey Vetzal <stacey@vetzal.com>
6
- Project-URL: Homepage, https://github.com/mojility/mojentic
7
- Project-URL: Issues, https://github.com/mojility/mojentic/issues
6
+ Project-URL: Homepage, https://github.com/svetzal/mojentic
7
+ Project-URL: Issues, https://github.com/svetzal/mojentic/issues
8
8
  Classifier: Programming Language :: Python :: 3
9
9
  Classifier: License :: OSI Approved :: MIT License
10
10
  Classifier: Operating System :: OS Independent
@@ -22,15 +22,26 @@ Requires-Dist: parsedatetime
22
22
  Requires-Dist: pytz
23
23
  Requires-Dist: serpapi
24
24
  Requires-Dist: colorama
25
+ Provides-Extra: dev
26
+ Requires-Dist: pytest>=7.0.0; extra == "dev"
27
+ Requires-Dist: pytest-spec; extra == "dev"
28
+ Requires-Dist: pytest-cov; extra == "dev"
29
+ Requires-Dist: pytest-mock>=3.10.0; extra == "dev"
30
+ Requires-Dist: flake8>=6.0.0; extra == "dev"
31
+ Requires-Dist: mkdocs; extra == "dev"
32
+ Requires-Dist: mkdocstrings[python]; extra == "dev"
33
+ Requires-Dist: griffe-fieldz; extra == "dev"
34
+ Requires-Dist: mkdocs-material; extra == "dev"
35
+ Requires-Dist: pymdown-extensions; extra == "dev"
25
36
  Dynamic: license-file
26
37
 
27
38
  # Mojentic
28
39
 
29
40
  Mojentic is a framework that provides a simple and flexible way to interact with Large Language Models (LLMs). It offers integration with various LLM providers and includes tools for structured output generation, task automation, and more. The future direction is to facilitate a team of agents, but the current focus is on robust LLM interaction capabilities.
30
41
 
31
- [![GitHub](https://img.shields.io/github/license/mojility/mojentic)](LICENSE.md)
42
+ [![GitHub](https://img.shields.io/github/license/svetzal/mojentic)](LICENSE.md)
32
43
  [![Python Version](https://img.shields.io/badge/python-3.11%2B-blue)](https://www.python.org/downloads/)
33
- [![Documentation](https://img.shields.io/badge/docs-latest-brightgreen)](https://mojility.github.io/mojentic/)
44
+ [![Documentation](https://img.shields.io/badge/docs-latest-brightgreen)](https://svetzal.github.io/mojentic/)
34
45
 
35
46
  ## 🚀 Features
36
47
 
@@ -57,7 +68,7 @@ pip install mojentic
57
68
  Or install from source
58
69
 
59
70
  ```bash
60
- git clone https://github.com/mojility/mojentic.git
71
+ git clone https://github.com/svetzal/mojentic.git
61
72
  cd mojentic
62
73
  pip install -e .
63
74
  ```
@@ -123,18 +134,17 @@ The primary focus is currently on the `llm` module, which provides robust capabi
123
134
 
124
135
  ## 📚 Documentation
125
136
 
126
- Visit [the documentation](https://mojility.github.io/mojentic/) for comprehensive guides, API reference, and examples.
137
+ Visit [the documentation](https://svetzal.github.io/mojentic/) for comprehensive guides, API reference, and examples.
127
138
 
128
139
  ## 🧪 Development
129
140
 
130
141
  ```bash
131
142
  # Clone the repository
132
- git clone https://github.com/mojility/mojentic.git
143
+ git clone https://github.com/svetzal/mojentic.git
133
144
  cd mojentic
134
145
 
135
146
  # Install dependencies
136
- pip install -r requirements.txt
137
- pip install -r dev-requirements.txt
147
+ pip install -e ".[dev]"
138
148
 
139
149
  # Run tests
140
150
  pytest
@@ -2,9 +2,9 @@
2
2
 
3
3
  Mojentic is a framework that provides a simple and flexible way to interact with Large Language Models (LLMs). It offers integration with various LLM providers and includes tools for structured output generation, task automation, and more. The future direction is to facilitate a team of agents, but the current focus is on robust LLM interaction capabilities.
4
4
 
5
- [![GitHub](https://img.shields.io/github/license/mojility/mojentic)](LICENSE.md)
5
+ [![GitHub](https://img.shields.io/github/license/svetzal/mojentic)](LICENSE.md)
6
6
  [![Python Version](https://img.shields.io/badge/python-3.11%2B-blue)](https://www.python.org/downloads/)
7
- [![Documentation](https://img.shields.io/badge/docs-latest-brightgreen)](https://mojility.github.io/mojentic/)
7
+ [![Documentation](https://img.shields.io/badge/docs-latest-brightgreen)](https://svetzal.github.io/mojentic/)
8
8
 
9
9
  ## 🚀 Features
10
10
 
@@ -31,7 +31,7 @@ pip install mojentic
31
31
  Or install from source
32
32
 
33
33
  ```bash
34
- git clone https://github.com/mojility/mojentic.git
34
+ git clone https://github.com/svetzal/mojentic.git
35
35
  cd mojentic
36
36
  pip install -e .
37
37
  ```
@@ -97,18 +97,17 @@ The primary focus is currently on the `llm` module, which provides robust capabi
97
97
 
98
98
  ## 📚 Documentation
99
99
 
100
- Visit [the documentation](https://mojility.github.io/mojentic/) for comprehensive guides, API reference, and examples.
100
+ Visit [the documentation](https://svetzal.github.io/mojentic/) for comprehensive guides, API reference, and examples.
101
101
 
102
102
  ## 🧪 Development
103
103
 
104
104
  ```bash
105
105
  # Clone the repository
106
- git clone https://github.com/mojility/mojentic.git
106
+ git clone https://github.com/svetzal/mojentic.git
107
107
  cd mojentic
108
108
 
109
109
  # Install dependencies
110
- pip install -r requirements.txt
111
- pip install -r dev-requirements.txt
110
+ pip install -e ".[dev]"
112
111
 
113
112
  # Run tests
114
113
  pytest
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "mojentic"
3
- version = "0.5.7"
3
+ version = "0.6.1"
4
4
  authors = [
5
5
  { name = "Stacey Vetzal", email = "stacey@vetzal.com" },
6
6
  ]
@@ -27,6 +27,20 @@ dependencies = [
27
27
  "colorama",
28
28
  ]
29
29
 
30
+ [project.optional-dependencies]
31
+ dev = [
32
+ "pytest>=7.0.0",
33
+ "pytest-spec",
34
+ "pytest-cov",
35
+ "pytest-mock>=3.10.0",
36
+ "flake8>=6.0.0",
37
+ "mkdocs",
38
+ "mkdocstrings[python]",
39
+ "griffe-fieldz",
40
+ "mkdocs-material",
41
+ "pymdown-extensions",
42
+ ]
43
+
30
44
  [build-system]
31
45
  requires = ["setuptools", "wheel"]
32
46
  build-backend = "setuptools.build_meta"
@@ -35,5 +49,5 @@ build-backend = "setuptools.build_meta"
35
49
  where = ["src"]
36
50
 
37
51
  [project.urls]
38
- Homepage = "https://github.com/mojility/mojentic"
39
- Issues = "https://github.com/mojility/mojentic/issues"
52
+ Homepage = "https://github.com/svetzal/mojentic"
53
+ Issues = "https://github.com/svetzal/mojentic/issues"
@@ -21,7 +21,8 @@ async def demonstrate_async():
21
21
  3. Running multiple problem-solving tasks concurrently
22
22
  """
23
23
  # Initialize the LLM broker with your preferred model
24
- llm = LLMBroker(model="llama3.3-70b-32k")
24
+ # llm = LLMBroker(model="llama3.3-70b-32k")
25
+ llm = LLMBroker(model="qwen3:30b-a3b-q4_K_M")
25
26
 
26
27
  # Create the agent with a maximum of 3 iterations
27
28
  agent = SimpleRecursiveAgent(llm=llm, max_iterations=3)
@@ -41,7 +42,7 @@ async def demonstrate_async():
41
42
  print(f"\nProblem (With Event Handling): {problem2}")
42
43
 
43
44
  # Set up event handlers for monitoring the solution process
44
- from mojentic.agents.simple_recursive_agent import ProblemSolvedEvent, IterationCompletedEvent
45
+ from mojentic.agents.simple_recursive_agent import GoalAchievedEvent, IterationCompletedEvent
45
46
 
46
47
  # Define event handlers
47
48
  def on_iteration_completed(event):
@@ -52,7 +53,7 @@ async def demonstrate_async():
52
53
 
53
54
  # Subscribe to events
54
55
  unsubscribe_iteration = agent.emitter.subscribe(IterationCompletedEvent, on_iteration_completed)
55
- unsubscribe_solved = agent.emitter.subscribe(ProblemSolvedEvent, on_problem_solved)
56
+ unsubscribe_solved = agent.emitter.subscribe(GoalAchievedEvent, on_problem_solved)
56
57
 
57
58
  # Solve the problem
58
59
  solution2 = await agent.solve(problem2)
@@ -0,0 +1,170 @@
1
+ """
2
+ Example script demonstrating the tracer system with ChatSession and tools.
3
+
4
+ This example shows how to use the tracer system to monitor an interactive
5
+ chat session with LLMBroker and tools. When the user exits the session,
6
+ the script displays a summary of all traced events.
7
+
8
+ It also demonstrates how correlation_id is used to trace related events
9
+ across the system, allowing you to track the flow of a request from start to finish.
10
+ """
11
+ import uuid
12
+ from datetime import datetime
13
+
14
+ from mojentic.tracer import TracerSystem
15
+ from mojentic.tracer.tracer_events import LLMCallTracerEvent, LLMResponseTracerEvent, ToolCallTracerEvent
16
+ from mojentic.llm import ChatSession, LLMBroker
17
+ from mojentic.llm.gateways.models import LLMMessage, MessageRole
18
+ from mojentic.llm.tools.date_resolver import ResolveDateTool
19
+
20
+
21
+ def print_tracer_events(events):
22
+ """Print tracer events using their printable_summary method."""
23
+ print(f"\n{'-'*80}")
24
+ print("Tracer Events:")
25
+ print(f"{'-'*80}")
26
+
27
+ for i, event in enumerate(events, 1):
28
+ print(f"{i}. {event.printable_summary()}")
29
+ print()
30
+
31
+
32
+ def main():
33
+ """Run a chat session with tracer system to monitor interactions."""
34
+ # Create a tracer system to monitor all interactions
35
+ tracer = TracerSystem()
36
+
37
+ # Create an LLM broker with the tracer
38
+ llm_broker = LLMBroker(model="llama3.3-70b-32k", tracer=tracer)
39
+
40
+ # Create a date resolver tool that will also use the tracer
41
+ date_tool = ResolveDateTool(llm_broker=llm_broker, tracer=tracer)
42
+
43
+ # Create a chat session with the broker and tool
44
+ chat_session = ChatSession(llm_broker, tools=[date_tool])
45
+
46
+ # Dictionary to store correlation_ids for each conversation turn
47
+ # This allows us to track related events across the system
48
+ conversation_correlation_ids = {}
49
+
50
+ print("Welcome to the chat session with tracer demonstration!")
51
+ print("Ask questions about dates (e.g., 'What day is next Friday?') or anything else.")
52
+ print("Behind the scenes, the tracer system is recording all interactions.")
53
+ print("Each interaction is assigned a unique correlation_id to trace related events.")
54
+ print("Press Enter with no input to exit and see the trace summary.")
55
+ print("-" * 80)
56
+
57
+ # Interactive chat session
58
+ turn_counter = 0
59
+ while True:
60
+ query = input("You: ")
61
+ if not query:
62
+ print("Exiting chat session...")
63
+ break
64
+ else:
65
+ # Generate a unique correlation_id for this conversation turn
66
+ # In a real system, this would be passed from the initiating event
67
+ # to all downstream events to maintain the causal chain
68
+ correlation_id = str(uuid.uuid4())
69
+ turn_counter += 1
70
+ conversation_correlation_ids[turn_counter] = correlation_id
71
+
72
+ print(f"[Turn {turn_counter}, correlation_id: {correlation_id[:8]}...]")
73
+ print("Assistant: ", end="")
74
+
75
+ # For demonstration purposes, we'll use the chat_session normally
76
+ # In a production system, you would modify ChatSession to accept and use correlation_id
77
+ response = chat_session.send(query)
78
+
79
+ # Alternatively, you could use the LLMBroker directly with correlation_id:
80
+ # messages = [LLMMessage(role=MessageRole.User, content=query)]
81
+ # response = llm_broker.generate(messages, tools=[date_tool], correlation_id=correlation_id)
82
+
83
+ print(response)
84
+
85
+ # After the user exits, display tracer event summary
86
+ print("\nTracer System Summary")
87
+ print("=" * 80)
88
+ print(f"You just had a conversation with an LLM, and the tracer recorded everything!")
89
+
90
+ # Get all events
91
+ all_events = tracer.get_events()
92
+ print(f"Total events recorded: {len(all_events)}")
93
+ print_tracer_events(all_events)
94
+
95
+ # Show how to filter events by type
96
+ print("\nYou can filter events by type:")
97
+
98
+ llm_calls = tracer.get_events(event_type=LLMCallTracerEvent)
99
+ print(f"LLM Call Events: {len(llm_calls)}")
100
+ if llm_calls:
101
+ print(f"Example: {llm_calls[0].printable_summary()}")
102
+
103
+ llm_responses = tracer.get_events(event_type=LLMResponseTracerEvent)
104
+ print(f"LLM Response Events: {len(llm_responses)}")
105
+ if llm_responses:
106
+ print(f"Example: {llm_responses[0].printable_summary()}")
107
+
108
+ tool_calls = tracer.get_events(event_type=ToolCallTracerEvent)
109
+ print(f"Tool Call Events: {len(tool_calls)}")
110
+ if tool_calls:
111
+ print(f"Example: {tool_calls[0].printable_summary()}")
112
+
113
+ # Show the last few events
114
+ print("\nThe last few events:")
115
+ last_events = tracer.get_last_n_tracer_events(3)
116
+ print_tracer_events(last_events)
117
+
118
+ # Show how to use time-based filtering
119
+ print("\nYou can also filter events by time range:")
120
+ print("Example: tracer.get_events(start_time=start_timestamp, end_time=end_timestamp)")
121
+
122
+ # Demonstrate filtering events by correlation_id
123
+ print("\nFiltering events by correlation_id:")
124
+ print("This is a powerful feature that allows you to trace all events related to a specific request")
125
+
126
+ # If we have any conversation turns, show events for the first turn
127
+ if conversation_correlation_ids:
128
+ # Get the correlation_id for the first turn
129
+ first_turn_id = 1
130
+ first_correlation_id = conversation_correlation_ids.get(first_turn_id)
131
+
132
+ if first_correlation_id:
133
+ print(f"\nEvents for conversation turn {first_turn_id} (correlation_id: {first_correlation_id[:8]}...):")
134
+
135
+ # Define a filter function that checks the correlation_id
136
+ def filter_by_correlation_id(event):
137
+ return event.correlation_id == first_correlation_id
138
+
139
+ # Get all events with this correlation_id
140
+ related_events = tracer.get_events(filter_func=filter_by_correlation_id)
141
+
142
+ if related_events:
143
+ print(f"Found {len(related_events)} related events")
144
+ print_tracer_events(related_events)
145
+
146
+ # Show how this helps trace the flow of a request
147
+ print("\nThe correlation_id allows you to trace the complete flow of a request:")
148
+ print("1. From the initial LLM call")
149
+ print("2. To the LLM response")
150
+ print("3. To any tool calls triggered by the LLM")
151
+ print("4. And any subsequent LLM calls with the tool results")
152
+ print("\nThis creates a complete audit trail for debugging and observability.")
153
+ else:
154
+ print("No events found with this correlation_id. This is unexpected and may indicate an issue.")
155
+
156
+ # Show how to extract specific information from events
157
+ if tool_calls:
158
+ print("\nDetailed analysis example - Tool usage stats:")
159
+ tool_names = {}
160
+ for event in tool_calls:
161
+ tool_name = event.tool_name
162
+ tool_names[tool_name] = tool_names.get(tool_name, 0) + 1
163
+
164
+ print("Tool usage frequency:")
165
+ for tool_name, count in tool_names.items():
166
+ print(f" - {tool_name}: {count} calls")
167
+
168
+
169
+ if __name__ == "__main__":
170
+ main()
@@ -8,17 +8,16 @@ from typing import List, Optional
8
8
 
9
9
  from pydantic import BaseModel
10
10
 
11
- from mojentic.llm.gateways.models import LLMMessage
11
+ from mojentic.llm.chat_session import ChatSession
12
12
  from mojentic.llm.llm_broker import LLMBroker
13
13
  from mojentic.llm.tools.llm_tool import LLMTool
14
- from mojentic.llm.chat_session import ChatSession
15
14
 
16
15
 
17
- class ProblemState(BaseModel):
16
+ class GoalState(BaseModel):
18
17
  """
19
18
  Represents the state of a problem-solving process.
20
19
  """
21
- problem: str
20
+ goal: str
22
21
  iteration: int = 0
23
22
  max_iterations: int = 5
24
23
  solution: Optional[str] = None
@@ -29,10 +28,10 @@ class SolverEvent(BaseModel):
29
28
  """
30
29
  Base class for solver events.
31
30
  """
32
- state: ProblemState
31
+ state: GoalState
33
32
 
34
33
 
35
- class ProblemSubmittedEvent(SolverEvent):
34
+ class GoalSubmittedEvent(SolverEvent):
36
35
  """
37
36
  Event triggered when a problem is submitted for solving.
38
37
  """
@@ -46,14 +45,14 @@ class IterationCompletedEvent(SolverEvent):
46
45
  response: str
47
46
 
48
47
 
49
- class ProblemSolvedEvent(SolverEvent):
48
+ class GoalAchievedEvent(SolverEvent):
50
49
  """
51
50
  Event triggered when a problem is solved.
52
51
  """
53
52
  pass
54
53
 
55
54
 
56
- class ProblemFailedEvent(SolverEvent):
55
+ class GoalFailedEvent(SolverEvent):
57
56
  """
58
57
  Event triggered when a problem cannot be solved.
59
58
  """
@@ -168,7 +167,7 @@ class SimpleRecursiveAgent:
168
167
  )
169
168
 
170
169
  # Set up event handlers
171
- self.emitter.subscribe(ProblemSubmittedEvent, self._handle_problem_submitted)
170
+ self.emitter.subscribe(GoalSubmittedEvent, self._handle_problem_submitted)
172
171
  self.emitter.subscribe(IterationCompletedEvent, self._handle_iteration_completed)
173
172
 
174
173
  async def solve(self, problem: str) -> str:
@@ -189,7 +188,7 @@ class SimpleRecursiveAgent:
189
188
  solution_future = asyncio.Future()
190
189
 
191
190
  # Create the initial problem state
192
- state = ProblemState(problem=problem, max_iterations=self.max_iterations)
191
+ state = GoalState(goal=problem, max_iterations=self.max_iterations)
193
192
 
194
193
  # Define handlers for completion events
195
194
  async def handle_solution_event(event):
@@ -197,12 +196,12 @@ class SimpleRecursiveAgent:
197
196
  solution_future.set_result(event.state.solution)
198
197
 
199
198
  # Subscribe to completion events
200
- self.emitter.subscribe(ProblemSolvedEvent, handle_solution_event)
201
- self.emitter.subscribe(ProblemFailedEvent, handle_solution_event)
199
+ self.emitter.subscribe(GoalAchievedEvent, handle_solution_event)
200
+ self.emitter.subscribe(GoalFailedEvent, handle_solution_event)
202
201
  self.emitter.subscribe(TimeoutEvent, handle_solution_event)
203
202
 
204
203
  # Start the solving process
205
- self.emitter.emit(ProblemSubmittedEvent(state=state))
204
+ self.emitter.emit(GoalSubmittedEvent(state=state))
206
205
 
207
206
  # Wait for the solution or timeout
208
207
  try:
@@ -215,13 +214,13 @@ class SimpleRecursiveAgent:
215
214
  self.emitter.emit(TimeoutEvent(state=state))
216
215
  return timeout_message
217
216
 
218
- async def _handle_problem_submitted(self, event: ProblemSubmittedEvent):
217
+ async def _handle_problem_submitted(self, event: GoalSubmittedEvent):
219
218
  """
220
219
  Handle a problem submitted event.
221
220
 
222
221
  Parameters
223
222
  ----------
224
- event : ProblemSubmittedEvent
223
+ event : GoalSubmittedEvent
225
224
  The problem submitted event to handle
226
225
  """
227
226
  # Start the first iteration
@@ -243,31 +242,31 @@ class SimpleRecursiveAgent:
243
242
  if "FAIL".lower() in response.lower():
244
243
  state.solution = f"Failed to solve after {state.iteration} iterations:\n{response}"
245
244
  state.is_complete = True
246
- self.emitter.emit(ProblemFailedEvent(state=state))
245
+ self.emitter.emit(GoalFailedEvent(state=state))
247
246
  return
248
247
  elif "DONE".lower() in response.lower():
249
248
  state.solution = response
250
249
  state.is_complete = True
251
- self.emitter.emit(ProblemSolvedEvent(state=state))
250
+ self.emitter.emit(GoalAchievedEvent(state=state))
252
251
  return
253
252
 
254
253
  # Check if we've reached the maximum number of iterations
255
254
  if state.iteration >= state.max_iterations:
256
255
  state.solution = f"Best solution after {state.max_iterations} iterations:\n{response}"
257
256
  state.is_complete = True
258
- self.emitter.emit(ProblemSolvedEvent(state=state))
257
+ self.emitter.emit(GoalAchievedEvent(state=state))
259
258
  return
260
259
 
261
260
  # If the problem is not solved and we haven't reached max_iterations, continue with next iteration
262
261
  await self._process_iteration(state)
263
262
 
264
- async def _process_iteration(self, state: ProblemState):
263
+ async def _process_iteration(self, state: GoalState):
265
264
  """
266
265
  Process a single iteration of the problem-solving process.
267
266
 
268
267
  Parameters
269
268
  ----------
270
- state : ProblemState
269
+ state : GoalState
271
270
  The current state of the problem-solving process
272
271
  """
273
272
  # Increment the iteration counter
@@ -276,7 +275,7 @@ class SimpleRecursiveAgent:
276
275
  # Create a prompt for the LLM
277
276
  prompt = f"""
278
277
  Given the user request:
279
- {state.problem}
278
+ {state.goal}
280
279
 
281
280
  Use the tools at your disposal to act on their request. You may wish to create a step-by-step plan for more complicated requests.
282
281
 
@@ -1,6 +1,7 @@
1
1
  import logging
2
2
  import threading
3
3
  from time import sleep
4
+ from typing import Optional, Type
4
5
  from uuid import uuid4
5
6
 
6
7
  import structlog
@@ -11,12 +12,16 @@ logger = structlog.get_logger()
11
12
 
12
13
 
13
14
  class Dispatcher:
14
- def __init__(self, router, shared_working_memory=None, batch_size=5):
15
+ def __init__(self, router, shared_working_memory=None, batch_size=5, tracer=None):
15
16
  self.router = router
16
17
  self.batch_size = batch_size
17
18
  self.event_queue = []
18
19
  self._stop_event = threading.Event()
19
20
  self._thread = threading.Thread(target=self._dispatch_events)
21
+
22
+ # Use null_tracer if no tracer is provided
23
+ from mojentic.tracer import null_tracer
24
+ self.tracer = tracer or null_tracer
20
25
 
21
26
  logger.debug("Starting event dispatch thread")
22
27
  self._thread.start()
@@ -44,6 +49,17 @@ class Dispatcher:
44
49
  events = []
45
50
  for agent in agents:
46
51
  logger.debug(f"Sending event to agent {agent}")
52
+
53
+ # Record agent interaction in tracer system
54
+ self.tracer.record_agent_interaction(
55
+ from_agent=str(event.source),
56
+ to_agent=str(type(agent)),
57
+ event_type=str(type(event).__name__),
58
+ event_id=event.correlation_id,
59
+ source=type(self)
60
+ )
61
+
62
+ # Process the event through the agent
47
63
  received_events = agent.receive_event(event)
48
64
  logger.debug(f"Agent {agent} returned {len(events)} events")
49
65
  events.extend(received_events)
@@ -92,13 +92,13 @@ class DescribeOpenAIMessagesAdapter:
92
92
  When adapting to OpenAI format
93
93
  Then it should convert to the correct format with structured content array
94
94
  """
95
- # Mock the open function to avoid reading actual files
96
- mock_file = mocker.mock_open(read_data=b'fake_image_data')
97
- mocker.patch('builtins.open', mock_file)
98
-
99
- # Mock base64 encoding to return a predictable value
100
- mock_b64encode = mocker.patch('base64.b64encode')
101
- mock_b64encode.return_value = b'ZmFrZV9pbWFnZV9kYXRhX2VuY29kZWQ=' # 'fake_image_data_encoded' in base64
95
+ # Patch our own methods that encapsulate external library calls
96
+ mocker.patch('mojentic.llm.gateways.openai_messages_adapter.read_file_as_binary',
97
+ return_value=b'fake_image_data')
98
+ mocker.patch('mojentic.llm.gateways.openai_messages_adapter.encode_base64',
99
+ return_value='ZmFrZV9pbWFnZV9kYXRhX2VuY29kZWQ=')
100
+ mocker.patch('mojentic.llm.gateways.openai_messages_adapter.get_image_type',
101
+ side_effect=lambda path: 'jpg' if path.endswith('.jpg') else 'png')
102
102
 
103
103
  image_paths = ["/path/to/image1.jpg", "/path/to/image2.png"]
104
104
  messages = [LLMMessage(role=MessageRole.User, content="What's in these images?", image_paths=image_paths)]
@@ -10,6 +10,53 @@ from mojentic.llm.gateways.models import LLMMessage, MessageRole
10
10
  logger = structlog.get_logger()
11
11
 
12
12
 
13
+ def read_file_as_binary(file_path: str) -> bytes:
14
+ """Read a file as binary data.
15
+
16
+ This function encapsulates the external library call to open() so it can be mocked in tests.
17
+
18
+ Args:
19
+ file_path: Path to the file to read
20
+
21
+ Returns:
22
+ Binary content of the file
23
+ """
24
+ with open(file_path, "rb") as file:
25
+ return file.read()
26
+
27
+
28
+ def encode_base64(data: bytes) -> str:
29
+ """Encode binary data as base64 string.
30
+
31
+ This function encapsulates the external library call to base64.b64encode() so it can be mocked in tests.
32
+
33
+ Args:
34
+ data: Binary data to encode
35
+
36
+ Returns:
37
+ Base64-encoded string
38
+ """
39
+ return base64.b64encode(data).decode('utf-8')
40
+
41
+
42
+ def get_image_type(file_path: str) -> str:
43
+ """Determine image type from file extension.
44
+
45
+ This function encapsulates the external library call to os.path.splitext() so it can be mocked in tests.
46
+
47
+ Args:
48
+ file_path: Path to the image file
49
+
50
+ Returns:
51
+ Image type (e.g., 'jpg', 'png')
52
+ """
53
+ _, ext = os.path.splitext(file_path)
54
+ image_type = ext.lstrip('.').lower()
55
+ if image_type not in ['jpeg', 'jpg', 'png', 'gif', 'webp']:
56
+ image_type = 'jpeg' # Default to jpeg if unknown extension
57
+ return image_type
58
+
59
+
13
60
  def adapt_messages_to_openai(messages: List[LLMMessage]):
14
61
  new_messages: List[dict[str, Any]] = []
15
62
  for m in messages:
@@ -25,21 +72,17 @@ def adapt_messages_to_openai(messages: List[LLMMessage]):
25
72
  # Add each image as a base64-encoded URL
26
73
  for image_path in m.image_paths:
27
74
  try:
28
- with open(image_path, "rb") as image_file:
29
- base64_image = base64.b64encode(image_file.read()).decode('utf-8')
30
-
31
- # Determine image type from file extension
32
- _, ext = os.path.splitext(image_path)
33
- image_type = ext.lstrip('.').lower()
34
- if image_type not in ['jpeg', 'jpg', 'png', 'gif', 'webp']:
35
- image_type = 'jpeg' # Default to jpeg if unknown extension
36
-
37
- content.append({
38
- "type": "image_url",
39
- "image_url": {
40
- "url": f"data:image/{image_type};base64,{base64_image}"
41
- }
42
- })
75
+ # Use our encapsulated methods instead of direct library calls
76
+ binary_data = read_file_as_binary(image_path)
77
+ base64_image = encode_base64(binary_data)
78
+ image_type = get_image_type(image_path)
79
+
80
+ content.append({
81
+ "type": "image_url",
82
+ "image_url": {
83
+ "url": f"data:image/{image_type};base64,{base64_image}"
84
+ }
85
+ })
43
86
  except Exception as e:
44
87
  logger.error("Failed to encode image", error=str(e), image_path=image_path)
45
88