mojentic 0.9.0__py3-none-any.whl → 1.0.1__py3-none-any.whl

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 (100) hide show
  1. _examples/async_dispatcher_example.py +12 -4
  2. _examples/async_llm_example.py +1 -2
  3. _examples/broker_as_tool.py +39 -14
  4. _examples/broker_examples.py +4 -6
  5. _examples/characterize_ollama.py +1 -1
  6. _examples/characterize_openai.py +1 -1
  7. _examples/chat_session.py +1 -1
  8. _examples/chat_session_with_tool.py +1 -1
  9. _examples/coding_file_tool.py +1 -3
  10. _examples/current_datetime_tool_example.py +1 -1
  11. _examples/embeddings.py +1 -1
  12. _examples/ephemeral_task_manager_example.py +13 -9
  13. _examples/fetch_openai_models.py +10 -3
  14. _examples/file_deduplication.py +6 -6
  15. _examples/image_analysis.py +2 -3
  16. _examples/image_broker.py +1 -1
  17. _examples/image_broker_splat.py +1 -1
  18. _examples/iterative_solver.py +2 -2
  19. _examples/model_characterization.py +2 -0
  20. _examples/openai_gateway_enhanced_demo.py +15 -5
  21. _examples/raw.py +1 -1
  22. _examples/react/agents/decisioning_agent.py +173 -15
  23. _examples/react/agents/summarization_agent.py +89 -0
  24. _examples/react/agents/thinking_agent.py +84 -14
  25. _examples/react/agents/tool_call_agent.py +83 -0
  26. _examples/react/formatters.py +38 -4
  27. _examples/react/models/base.py +60 -11
  28. _examples/react/models/events.py +76 -8
  29. _examples/react.py +71 -21
  30. _examples/recursive_agent.py +1 -1
  31. _examples/solver_chat_session.py +1 -7
  32. _examples/streaming.py +7 -5
  33. _examples/tell_user_example.py +3 -3
  34. _examples/tracer_demo.py +15 -17
  35. _examples/tracer_qt_viewer.py +49 -46
  36. mojentic/__init__.py +3 -3
  37. mojentic/agents/__init__.py +26 -8
  38. mojentic/agents/{agent_broker.py → agent_event_adapter.py} +3 -3
  39. mojentic/agents/async_aggregator_agent_spec.py +32 -33
  40. mojentic/agents/async_llm_agent.py +9 -5
  41. mojentic/agents/async_llm_agent_spec.py +21 -22
  42. mojentic/agents/base_async_agent.py +2 -2
  43. mojentic/agents/base_llm_agent.py +6 -2
  44. mojentic/agents/iterative_problem_solver.py +11 -5
  45. mojentic/agents/simple_recursive_agent.py +11 -10
  46. mojentic/agents/simple_recursive_agent_spec.py +423 -0
  47. mojentic/async_dispatcher.py +0 -1
  48. mojentic/async_dispatcher_spec.py +1 -1
  49. mojentic/context/__init__.py +0 -2
  50. mojentic/dispatcher.py +7 -8
  51. mojentic/llm/__init__.py +5 -5
  52. mojentic/llm/chat_session.py +24 -1
  53. mojentic/llm/chat_session_spec.py +40 -0
  54. mojentic/llm/gateways/__init__.py +19 -18
  55. mojentic/llm/gateways/anthropic.py +1 -0
  56. mojentic/llm/gateways/anthropic_messages_adapter.py +0 -1
  57. mojentic/llm/gateways/llm_gateway.py +1 -1
  58. mojentic/llm/gateways/ollama.py +2 -0
  59. mojentic/llm/gateways/openai.py +62 -58
  60. mojentic/llm/gateways/openai_message_adapter_spec.py +3 -3
  61. mojentic/llm/gateways/openai_model_registry.py +7 -6
  62. mojentic/llm/gateways/openai_model_registry_spec.py +1 -2
  63. mojentic/llm/gateways/openai_temperature_handling_spec.py +2 -2
  64. mojentic/llm/llm_broker.py +7 -5
  65. mojentic/llm/llm_broker_spec.py +7 -2
  66. mojentic/llm/message_composers.py +6 -3
  67. mojentic/llm/message_composers_spec.py +5 -1
  68. mojentic/llm/registry/__init__.py +0 -3
  69. mojentic/llm/tools/__init__.py +0 -9
  70. mojentic/llm/tools/ask_user_tool.py +11 -5
  71. mojentic/llm/tools/current_datetime.py +9 -6
  72. mojentic/llm/tools/date_resolver.py +10 -4
  73. mojentic/llm/tools/date_resolver_spec.py +0 -1
  74. mojentic/llm/tools/ephemeral_task_manager/append_task_tool.py +4 -1
  75. mojentic/llm/tools/ephemeral_task_manager/ephemeral_task_list.py +1 -1
  76. mojentic/llm/tools/ephemeral_task_manager/insert_task_after_tool.py +4 -1
  77. mojentic/llm/tools/ephemeral_task_manager/prepend_task_tool.py +5 -2
  78. mojentic/llm/tools/file_manager.py +131 -28
  79. mojentic/llm/tools/file_manager_spec.py +0 -3
  80. mojentic/llm/tools/llm_tool.py +1 -1
  81. mojentic/llm/tools/llm_tool_spec.py +0 -2
  82. mojentic/llm/tools/organic_web_search.py +4 -2
  83. mojentic/llm/tools/tell_user_tool.py +6 -2
  84. mojentic/llm/tools/tool_wrapper.py +2 -2
  85. mojentic/tracer/__init__.py +1 -10
  86. mojentic/tracer/event_store.py +7 -8
  87. mojentic/tracer/event_store_spec.py +1 -2
  88. mojentic/tracer/null_tracer.py +37 -43
  89. mojentic/tracer/tracer_events.py +8 -2
  90. mojentic/tracer/tracer_events_spec.py +6 -7
  91. mojentic/tracer/tracer_system.py +37 -36
  92. mojentic/tracer/tracer_system_spec.py +21 -6
  93. mojentic/utils/__init__.py +1 -1
  94. mojentic/utils/formatting.py +1 -0
  95. {mojentic-0.9.0.dist-info → mojentic-1.0.1.dist-info}/METADATA +47 -29
  96. mojentic-1.0.1.dist-info/RECORD +149 -0
  97. {mojentic-0.9.0.dist-info → mojentic-1.0.1.dist-info}/WHEEL +1 -1
  98. mojentic-0.9.0.dist-info/RECORD +0 -146
  99. {mojentic-0.9.0.dist-info → mojentic-1.0.1.dist-info}/licenses/LICENSE.md +0 -0
  100. {mojentic-0.9.0.dist-info → mojentic-1.0.1.dist-info}/top_level.txt +0 -0
@@ -1,28 +1,96 @@
1
+ """Event definitions for the ReAct pattern.
2
+
3
+ This module defines all event types used to coordinate the ReAct loop,
4
+ including thinking, decisioning, tool calls, completion, and failure events.
5
+ """
1
6
  from pydantic import Field
2
7
 
3
8
  from mojentic import Event
9
+
4
10
  from .base import CurrentContext, NextAction
5
11
 
6
12
 
7
13
  class InvokeThinking(Event):
8
- context: CurrentContext = Field(..., description="The current context as we work through our response.")
14
+ """Event to trigger the thinking/planning phase.
15
+
16
+ This event initiates the planning process where the agent creates
17
+ or refines a plan for answering the user's query.
18
+ """
19
+
20
+ context: CurrentContext = Field(
21
+ ...,
22
+ description="The current context as we work through our response."
23
+ )
9
24
 
10
25
 
11
26
  class InvokeDecisioning(Event):
12
- context: CurrentContext = Field(..., description="The current context as we work through our response.")
27
+ """Event to trigger the decision-making phase.
28
+
29
+ This event initiates the decision process where the agent evaluates
30
+ the current plan and history to decide on the next action.
31
+ """
32
+
33
+ context: CurrentContext = Field(
34
+ ...,
35
+ description="The current context as we work through our response."
36
+ )
13
37
 
14
38
 
15
39
  class InvokeToolCall(Event):
16
- context: CurrentContext = Field(..., description="The current context as we work through our response.")
17
- thought: str = Field(..., description="The reasoning behind the decision.")
40
+ """Event to trigger a tool invocation.
41
+
42
+ This event carries the information needed to execute a specific tool
43
+ with given arguments, along with the reasoning behind the decision.
44
+ """
45
+
46
+ context: CurrentContext = Field(
47
+ ...,
48
+ description="The current context as we work through our response."
49
+ )
50
+ thought: str = Field(
51
+ ...,
52
+ description="The reasoning behind the decision."
53
+ )
18
54
  action: NextAction
55
+ tool: object = Field(
56
+ ...,
57
+ description="The tool instance to invoke."
58
+ )
59
+ tool_arguments: dict = Field(
60
+ default_factory=dict,
61
+ description="Arguments to pass to the tool."
62
+ )
19
63
 
20
64
 
21
65
  class FinishAndSummarize(Event):
22
- context: CurrentContext = Field(..., description="The current context as we work through our response.")
23
- thought: str = Field(..., description="The reasoning behind the decision.")
66
+ """Event to trigger the completion and summarization phase.
67
+
68
+ This event indicates that the agent has gathered sufficient information
69
+ to answer the user's query and should generate a final response.
70
+ """
71
+
72
+ context: CurrentContext = Field(
73
+ ...,
74
+ description="The current context as we work through our response."
75
+ )
76
+ thought: str = Field(
77
+ ...,
78
+ description="The reasoning behind the decision."
79
+ )
24
80
 
25
81
 
26
82
  class FailureOccurred(Event):
27
- context: CurrentContext = Field(..., description="The current context as we work through our response.")
28
- reason: str = Field(..., description="The reason for the failure.")
83
+ """Event to signal a failure in the ReAct loop.
84
+
85
+ This event captures errors or unrecoverable situations that prevent
86
+ the agent from continuing to process the user's query.
87
+ """
88
+
89
+ context: CurrentContext = Field(
90
+ ...,
91
+ description="The current context as we work through our response."
92
+ )
93
+ reason: str = Field(
94
+ ...,
95
+ description="The reason for the failure."
96
+ )
_examples/react.py CHANGED
@@ -1,32 +1,82 @@
1
+ """ReAct Pattern Example.
2
+
3
+ This example demonstrates a Reasoning and Acting (ReAct) loop where agents
4
+ iteratively plan, decide, act, and summarize to answer user queries.
5
+
6
+ The ReAct pattern consists of:
7
+ 1. Thinking Agent - Creates plans
8
+ 2. Decisioning Agent - Decides next actions
9
+ 3. Tool Call Agent - Executes tools
10
+ 4. Summarization Agent - Generates final answers
11
+ """
1
12
  from _examples.react.agents.decisioning_agent import DecisioningAgent
13
+ from _examples.react.agents.summarization_agent import SummarizationAgent
2
14
  from _examples.react.agents.thinking_agent import ThinkingAgent
15
+ from _examples.react.agents.tool_call_agent import ToolCallAgent
3
16
  from _examples.react.models.base import CurrentContext
4
- from _examples.react.models.events import InvokeThinking, InvokeDecisioning
5
- from mojentic import Router, Dispatcher
6
- from mojentic.agents import OutputAgent
17
+ from _examples.react.models.events import (
18
+ FailureOccurred,
19
+ FinishAndSummarize,
20
+ InvokeDecisioning,
21
+ InvokeThinking,
22
+ InvokeToolCall,
23
+ )
24
+ from mojentic import Dispatcher, Router
25
+ from mojentic.agents.output_agent import OutputAgent
7
26
  from mojentic.llm import LLMBroker
8
27
 
9
- # llm = LLMBroker("qwen3:32b")
10
- llm = LLMBroker("deepseek-r1:70b")
11
- thinking_agent = ThinkingAgent(llm)
12
- decisioning_agent = DecisioningAgent(llm)
13
28
 
14
- output_agent = OutputAgent()
29
+ def main():
30
+ """Run the ReAct pattern example."""
31
+ # Initialize LLM broker - using qwen3:32b as it's widely available
32
+ llm = LLMBroker("qwen3:32b")
33
+ # Alternative models (uncomment if available):
34
+ # llm = LLMBroker("deepseek-r1:70b")
35
+ # llm = LLMBroker("qwen3:8b")
15
36
 
16
- router = Router({
17
- InvokeThinking: [thinking_agent, output_agent],
18
- InvokeDecisioning: [decisioning_agent, output_agent],
19
- })
37
+ # Create agents
38
+ thinking_agent = ThinkingAgent(llm)
39
+ decisioning_agent = DecisioningAgent(llm)
40
+ tool_call_agent = ToolCallAgent()
41
+ summarization_agent = SummarizationAgent(llm)
42
+ output_agent = OutputAgent()
20
43
 
21
- dispatcher = Dispatcher(router)
44
+ # Configure router - maps event types to agent handlers
45
+ router = Router({
46
+ InvokeThinking: [thinking_agent, output_agent],
47
+ InvokeDecisioning: [decisioning_agent, output_agent],
48
+ InvokeToolCall: [tool_call_agent, output_agent],
49
+ FinishAndSummarize: [summarization_agent, output_agent],
50
+ FailureOccurred: [output_agent],
51
+ })
22
52
 
23
- event = thinking_agent.receive_event(
24
- InvokeThinking(
25
- source=str,
26
- context=CurrentContext(
27
- user_query="What is the date next Friday?"
28
- )
53
+ # Create dispatcher
54
+ dispatcher = Dispatcher(router)
55
+
56
+ # Create initial context
57
+ initial_context = CurrentContext(
58
+ user_query="What is the date next Friday?"
29
59
  )
30
- )
31
60
 
32
- dispatcher.dispatch(event)
61
+ # Start the ReAct loop
62
+ print("\n" + "=" * 80)
63
+ print("Starting ReAct Pattern Example")
64
+ print("=" * 80)
65
+ print(f"User Query: {initial_context.user_query}")
66
+ print("=" * 80 + "\n")
67
+
68
+ # Create and dispatch initial thinking event
69
+ initial_event = InvokeThinking(
70
+ source=type(main),
71
+ context=initial_context
72
+ )
73
+
74
+ dispatcher.dispatch(initial_event)
75
+
76
+ print("\n" + "=" * 80)
77
+ print("ReAct Pattern Example Complete")
78
+ print("=" * 80 + "\n")
79
+
80
+
81
+ if __name__ == "__main__":
82
+ main()
@@ -78,7 +78,7 @@ async def demonstrate_async():
78
78
 
79
79
  # Create tasks for all problems and run them concurrently
80
80
  tasks = [solve_and_print(problem) for problem in problems]
81
- solutions = await asyncio.gather(*tasks)
81
+ await asyncio.gather(*tasks)
82
82
 
83
83
  print("\nAll concurrent problems have been solved!")
84
84
 
@@ -4,11 +4,10 @@ from typing import List
4
4
  from mojentic.agents import IterativeProblemSolver
5
5
  from mojentic.llm.tools.date_resolver import ResolveDateTool
6
6
  from mojentic.llm.tools.llm_tool import LLMTool
7
+ from mojentic.llm import LLMBroker, ChatSession
7
8
 
8
9
  logging.basicConfig(level=logging.WARN)
9
10
 
10
- from mojentic.llm import LLMBroker, ChatSession
11
-
12
11
 
13
12
  class IterativeProblemSolverTool(LLMTool):
14
13
  def __init__(self, llm: LLMBroker, tools: List[LLMTool]):
@@ -50,11 +49,6 @@ def main():
50
49
  # llm = LLMBroker(model="qwq:32b-fp16")
51
50
  # llm = LLMBroker(model="qwen3:32b")
52
51
 
53
- tools = [
54
- ResolveDateTool(),
55
- IterativeProblemSolverTool(llm=llm, tools=[ResolveDateTool()])
56
- ]
57
-
58
52
  chat_session = ChatSession(llm, tools=[IterativeProblemSolverTool(llm=llm, tools=[ResolveDateTool()])])
59
53
 
60
54
  while True:
_examples/streaming.py CHANGED
@@ -1,8 +1,6 @@
1
- import os
2
1
  from mojentic.llm.llm_broker import LLMBroker
3
2
  from mojentic.llm.gateways.models import LLMMessage
4
3
  from mojentic.llm.gateways.ollama import OllamaGateway
5
- from mojentic.llm.gateways.openai import OpenAIGateway
6
4
  from mojentic.llm.tools.date_resolver import ResolveDateTool
7
5
 
8
6
 
@@ -30,15 +28,19 @@ def main():
30
28
 
31
29
  stream = broker.generate_stream(
32
30
  messages=[
33
- LLMMessage(content="Tell me a short story about a dragon. In your story, reference several dates relative to today, "
34
- "like 'three days from now' or 'last week'.")
31
+ LLMMessage(
32
+ content=(
33
+ "Tell me a short story about a dragon. "
34
+ "In your story, reference several dates relative to today, "
35
+ "like 'three days from now' or 'last week'."
36
+ )
37
+ )
35
38
  ],
36
39
  tools=[date_tool],
37
40
  temperature=0.7,
38
41
  num_ctx=32768,
39
42
  num_predict=-1
40
43
  )
41
-
42
44
  for chunk in stream:
43
45
  print(chunk, end='', flush=True)
44
46
 
@@ -7,12 +7,12 @@ to display messages to the user without expecting a response.
7
7
 
8
8
  import logging
9
9
 
10
- logging.basicConfig(level=logging.WARN)
11
-
12
10
  from mojentic.agents.iterative_problem_solver import IterativeProblemSolver
13
11
  from mojentic.llm.tools.tell_user_tool import TellUserTool
14
12
  from mojentic.llm import LLMBroker
15
13
 
14
+ logging.basicConfig(level=logging.WARN)
15
+
16
16
 
17
17
  def main():
18
18
  # Initialize the LLM broker with your preferred model
@@ -40,4 +40,4 @@ def main():
40
40
 
41
41
 
42
42
  if __name__ == "__main__":
43
- main()
43
+ main()
_examples/tracer_demo.py CHANGED
@@ -9,23 +9,21 @@ It also demonstrates how correlation_id is used to trace related events
9
9
  across the system, allowing you to track the flow of a request from start to finish.
10
10
  """
11
11
  import uuid
12
- from datetime import datetime
13
12
 
14
13
  from mojentic.tracer import TracerSystem
15
14
  from mojentic.tracer.tracer_events import LLMCallTracerEvent, LLMResponseTracerEvent, ToolCallTracerEvent
16
15
  from mojentic.llm import ChatSession, LLMBroker
17
- from mojentic.llm.gateways.models import LLMMessage, MessageRole
18
16
  from mojentic.llm.tools.date_resolver import ResolveDateTool
19
17
 
20
18
 
21
19
  def print_tracer_events(events):
22
20
  """Print tracer events using their printable_summary method."""
23
- print(f"\n{'-'*80}")
21
+ print("\n{'-'*80}")
24
22
  print("Tracer Events:")
25
- print(f"{'-'*80}")
23
+ print("{'-'*80}")
26
24
 
27
25
  for i, event in enumerate(events, 1):
28
- print(f"{i}. {event.printable_summary()}")
26
+ print("{i}. {event.printable_summary()}")
29
27
  print()
30
28
 
31
29
 
@@ -69,7 +67,7 @@ def main():
69
67
  turn_counter += 1
70
68
  conversation_correlation_ids[turn_counter] = correlation_id
71
69
 
72
- print(f"[Turn {turn_counter}, correlation_id: {correlation_id[:8]}...]")
70
+ print("[Turn {turn_counter}, correlation_id: {correlation_id[:8]}...]")
73
71
  print("Assistant: ", end="")
74
72
 
75
73
  # For demonstration purposes, we'll use the chat_session normally
@@ -85,30 +83,30 @@ def main():
85
83
  # After the user exits, display tracer event summary
86
84
  print("\nTracer System Summary")
87
85
  print("=" * 80)
88
- print(f"You just had a conversation with an LLM, and the tracer recorded everything!")
86
+ print("You just had a conversation with an LLM, and the tracer recorded everything!")
89
87
 
90
88
  # Get all events
91
89
  all_events = tracer.get_events()
92
- print(f"Total events recorded: {len(all_events)}")
90
+ print("Total events recorded: {len(all_events)}")
93
91
  print_tracer_events(all_events)
94
92
 
95
93
  # Show how to filter events by type
96
94
  print("\nYou can filter events by type:")
97
95
 
98
96
  llm_calls = tracer.get_events(event_type=LLMCallTracerEvent)
99
- print(f"LLM Call Events: {len(llm_calls)}")
97
+ print("LLM Call Events: {len(llm_calls)}")
100
98
  if llm_calls:
101
- print(f"Example: {llm_calls[0].printable_summary()}")
99
+ print("Example: {llm_calls[0].printable_summary()}")
102
100
 
103
101
  llm_responses = tracer.get_events(event_type=LLMResponseTracerEvent)
104
- print(f"LLM Response Events: {len(llm_responses)}")
102
+ print("LLM Response Events: {len(llm_responses)}")
105
103
  if llm_responses:
106
- print(f"Example: {llm_responses[0].printable_summary()}")
104
+ print("Example: {llm_responses[0].printable_summary()}")
107
105
 
108
106
  tool_calls = tracer.get_events(event_type=ToolCallTracerEvent)
109
- print(f"Tool Call Events: {len(tool_calls)}")
107
+ print("Tool Call Events: {len(tool_calls)}")
110
108
  if tool_calls:
111
- print(f"Example: {tool_calls[0].printable_summary()}")
109
+ print("Example: {tool_calls[0].printable_summary()}")
112
110
 
113
111
  # Show the last few events
114
112
  print("\nThe last few events:")
@@ -130,7 +128,7 @@ def main():
130
128
  first_correlation_id = conversation_correlation_ids.get(first_turn_id)
131
129
 
132
130
  if first_correlation_id:
133
- print(f"\nEvents for conversation turn {first_turn_id} (correlation_id: {first_correlation_id[:8]}...):")
131
+ print("\nEvents for conversation turn {first_turn_id} (correlation_id: {first_correlation_id[:8]}...):")
134
132
 
135
133
  # Define a filter function that checks the correlation_id
136
134
  def filter_by_correlation_id(event):
@@ -140,7 +138,7 @@ def main():
140
138
  related_events = tracer.get_events(filter_func=filter_by_correlation_id)
141
139
 
142
140
  if related_events:
143
- print(f"Found {len(related_events)} related events")
141
+ print("Found {len(related_events)} related events")
144
142
  print_tracer_events(related_events)
145
143
 
146
144
  # Show how this helps trace the flow of a request
@@ -163,7 +161,7 @@ def main():
163
161
 
164
162
  print("Tool usage frequency:")
165
163
  for tool_name, count in tool_names.items():
166
- print(f" - {tool_name}: {count} calls")
164
+ print(" - {tool_name}: {count} calls")
167
165
 
168
166
 
169
167
  if __name__ == "__main__":