mojentic 0.5.3__py3-none-any.whl → 0.5.5__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.
- _examples/broker_examples.py +12 -22
- _examples/broker_image_examples.py +41 -0
- _examples/ephemeral_task_manager_example.py +48 -0
- _examples/oversized_embeddings.py +9 -0
- _examples/streaming.py +1 -1
- _examples/tell_user_example.py +43 -0
- mojentic/agents/iterative_problem_solver.py +5 -2
- mojentic/agents/simple_recursive_agent.py +2 -2
- mojentic/llm/gateways/openai.py +29 -6
- mojentic/llm/llm_broker_spec.py +0 -49
- mojentic/llm/message_composers_spec.py +0 -80
- mojentic/llm/tools/ask_user_tool.py +1 -1
- mojentic/llm/tools/ephemeral_task_manager/__init__.py +27 -0
- mojentic/llm/tools/ephemeral_task_manager/append_task_tool.py +77 -0
- mojentic/llm/tools/ephemeral_task_manager/append_task_tool_spec.py +34 -0
- mojentic/llm/tools/ephemeral_task_manager/clear_tasks_tool.py +57 -0
- mojentic/llm/tools/ephemeral_task_manager/clear_tasks_tool_spec.py +32 -0
- mojentic/llm/tools/ephemeral_task_manager/complete_task_tool.py +81 -0
- mojentic/llm/tools/ephemeral_task_manager/complete_task_tool_spec.py +43 -0
- mojentic/llm/tools/ephemeral_task_manager/ephemeral_task_list.py +202 -0
- mojentic/llm/tools/ephemeral_task_manager/ephemeral_task_list_spec.py +137 -0
- mojentic/llm/tools/ephemeral_task_manager/insert_task_after_tool.py +84 -0
- mojentic/llm/tools/ephemeral_task_manager/insert_task_after_tool_spec.py +42 -0
- mojentic/llm/tools/ephemeral_task_manager/list_tasks_tool.py +80 -0
- mojentic/llm/tools/ephemeral_task_manager/list_tasks_tool_spec.py +38 -0
- mojentic/llm/tools/ephemeral_task_manager/prepend_task_tool.py +77 -0
- mojentic/llm/tools/ephemeral_task_manager/prepend_task_tool_spec.py +34 -0
- mojentic/llm/tools/ephemeral_task_manager/start_task_tool.py +81 -0
- mojentic/llm/tools/ephemeral_task_manager/start_task_tool_spec.py +43 -0
- mojentic/llm/tools/organic_web_search.py +37 -0
- mojentic/llm/tools/tell_user_tool.py +27 -0
- {mojentic-0.5.3.dist-info → mojentic-0.5.5.dist-info}/METADATA +2 -1
- {mojentic-0.5.3.dist-info → mojentic-0.5.5.dist-info}/RECORD +36 -14
- {mojentic-0.5.3.dist-info → mojentic-0.5.5.dist-info}/WHEEL +1 -1
- mojentic/llm/tools/web_search.py +0 -35
- {mojentic-0.5.3.dist-info → mojentic-0.5.5.dist-info}/licenses/LICENSE.md +0 -0
- {mojentic-0.5.3.dist-info → mojentic-0.5.5.dist-info}/top_level.txt +0 -0
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Tool for inserting a new task after an existing task in the ephemeral task manager list.
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
from typing import Dict
|
|
6
|
+
|
|
7
|
+
from mojentic.llm.tools.llm_tool import LLMTool
|
|
8
|
+
from mojentic.llm.tools.ephemeral_task_manager.ephemeral_task_list import EphemeralTaskList
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class InsertTaskAfterTool(LLMTool):
|
|
12
|
+
"""
|
|
13
|
+
Tool for inserting a new task after an existing task in the ephemeral task manager list.
|
|
14
|
+
"""
|
|
15
|
+
|
|
16
|
+
def __init__(self, task_list: EphemeralTaskList):
|
|
17
|
+
"""
|
|
18
|
+
Initialize the tool with a shared task list.
|
|
19
|
+
|
|
20
|
+
Args:
|
|
21
|
+
task_list: The shared task list to use
|
|
22
|
+
"""
|
|
23
|
+
self._task_list = task_list
|
|
24
|
+
|
|
25
|
+
def run(self, existing_task_id: int, description: str) -> Dict[str, str]:
|
|
26
|
+
"""
|
|
27
|
+
Insert a new task after an existing task.
|
|
28
|
+
|
|
29
|
+
Args:
|
|
30
|
+
existing_task_id: The ID of the existing task after which to insert the new task
|
|
31
|
+
description: The description of the new task
|
|
32
|
+
|
|
33
|
+
Returns:
|
|
34
|
+
A dictionary with the result of the operation
|
|
35
|
+
|
|
36
|
+
Raises:
|
|
37
|
+
ValueError: If no task with the given ID exists
|
|
38
|
+
"""
|
|
39
|
+
try:
|
|
40
|
+
# Convert existing_task_id to int if it's a string
|
|
41
|
+
task_id = int(existing_task_id) if isinstance(existing_task_id, str) else existing_task_id
|
|
42
|
+
task = self._task_list.insert_task_after(existing_task_id=task_id, description=description)
|
|
43
|
+
return {
|
|
44
|
+
"id": task.id,
|
|
45
|
+
"description": task.description,
|
|
46
|
+
"status": task.status.value,
|
|
47
|
+
"summary": f"Task '{task.id}' inserted after task '{existing_task_id}' successfully"
|
|
48
|
+
}
|
|
49
|
+
except ValueError as e:
|
|
50
|
+
return {
|
|
51
|
+
"error": str(e),
|
|
52
|
+
"summary": f"Failed to insert task: {str(e)}"
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
@property
|
|
56
|
+
def descriptor(self):
|
|
57
|
+
"""
|
|
58
|
+
Get the descriptor for the tool.
|
|
59
|
+
|
|
60
|
+
Returns:
|
|
61
|
+
The descriptor dictionary
|
|
62
|
+
"""
|
|
63
|
+
return {
|
|
64
|
+
"type": "function",
|
|
65
|
+
"function": {
|
|
66
|
+
"name": "insert_task_after",
|
|
67
|
+
"description": "Insert a new task after an existing task in the task list. The task will start with 'pending' status.",
|
|
68
|
+
"parameters": {
|
|
69
|
+
"type": "object",
|
|
70
|
+
"properties": {
|
|
71
|
+
"existing_task_id": {
|
|
72
|
+
"type": "integer",
|
|
73
|
+
"description": "The ID of the existing task after which to insert the new task"
|
|
74
|
+
},
|
|
75
|
+
"description": {
|
|
76
|
+
"type": "string",
|
|
77
|
+
"description": "The description of the new task"
|
|
78
|
+
}
|
|
79
|
+
},
|
|
80
|
+
"required": ["existing_task_id", "description"],
|
|
81
|
+
"additionalProperties": False
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
from unittest.mock import Mock
|
|
2
|
+
|
|
3
|
+
import pytest
|
|
4
|
+
|
|
5
|
+
from mojentic.llm.tools.ephemeral_task_manager.ephemeral_task_list import EphemeralTaskList, Task, TaskStatus
|
|
6
|
+
from mojentic.llm.tools.ephemeral_task_manager.insert_task_after_tool import InsertTaskAfterTool
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
@pytest.fixture
|
|
10
|
+
def mock_task_list():
|
|
11
|
+
mock = Mock(spec=EphemeralTaskList)
|
|
12
|
+
return mock
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
@pytest.fixture
|
|
16
|
+
def insert_task_after_tool(mock_task_list):
|
|
17
|
+
return InsertTaskAfterTool(task_list=mock_task_list)
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
class DescribeInsertTaskAfterTool:
|
|
21
|
+
def should_call_insert_task_after_with_correct_parameters(self, insert_task_after_tool, mock_task_list):
|
|
22
|
+
mock_task = Task(id=2, description="Test task", status=TaskStatus.PENDING)
|
|
23
|
+
mock_task_list.insert_task_after.return_value = mock_task
|
|
24
|
+
|
|
25
|
+
insert_task_after_tool.run(existing_task_id=1, description="Test task")
|
|
26
|
+
|
|
27
|
+
mock_task_list.insert_task_after.assert_called_once_with(existing_task_id=1, description="Test task")
|
|
28
|
+
|
|
29
|
+
def should_convert_string_task_id_to_int(self, insert_task_after_tool, mock_task_list):
|
|
30
|
+
mock_task = Task(id=2, description="Test task", status=TaskStatus.PENDING)
|
|
31
|
+
mock_task_list.insert_task_after.return_value = mock_task
|
|
32
|
+
|
|
33
|
+
insert_task_after_tool.run(existing_task_id="1", description="Test task")
|
|
34
|
+
|
|
35
|
+
mock_task_list.insert_task_after.assert_called_once_with(existing_task_id=1, description="Test task")
|
|
36
|
+
|
|
37
|
+
def should_handle_error_when_insert_task_after_fails(self, insert_task_after_tool, mock_task_list):
|
|
38
|
+
mock_task_list.insert_task_after.side_effect = ValueError("No task with ID '999' exists")
|
|
39
|
+
|
|
40
|
+
insert_task_after_tool.run(existing_task_id=999, description="Test task")
|
|
41
|
+
|
|
42
|
+
mock_task_list.insert_task_after.assert_called_once_with(existing_task_id=999, description="Test task")
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Tool for listing all tasks in the ephemeral task manager.
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
from typing import Dict, List
|
|
6
|
+
|
|
7
|
+
from mojentic.llm.tools.ephemeral_task_manager.ephemeral_task_list import EphemeralTaskList, Task
|
|
8
|
+
from mojentic.llm.tools.llm_tool import LLMTool
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class ListTasksTool(LLMTool):
|
|
12
|
+
"""
|
|
13
|
+
Tool for listing all tasks in the ephemeral task manager.
|
|
14
|
+
"""
|
|
15
|
+
|
|
16
|
+
def __init__(self, task_list: EphemeralTaskList):
|
|
17
|
+
"""
|
|
18
|
+
Initialize the tool with a shared task list.
|
|
19
|
+
|
|
20
|
+
Args:
|
|
21
|
+
task_list: The shared task list to use
|
|
22
|
+
"""
|
|
23
|
+
self._task_list = task_list
|
|
24
|
+
|
|
25
|
+
def run(self) -> Dict[str, str]:
|
|
26
|
+
"""
|
|
27
|
+
Get all tasks in the list.
|
|
28
|
+
|
|
29
|
+
Returns:
|
|
30
|
+
A dictionary with the result of the operation
|
|
31
|
+
"""
|
|
32
|
+
tasks = self._task_list.list_tasks()
|
|
33
|
+
|
|
34
|
+
# Format tasks as a string for the summary
|
|
35
|
+
task_list_str = self._format_tasks(tasks)
|
|
36
|
+
|
|
37
|
+
return {
|
|
38
|
+
"count": str(len(tasks)),
|
|
39
|
+
"tasks": task_list_str,
|
|
40
|
+
"summary": f"Found {len(tasks)} tasks\n\n{task_list_str}"
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
def _format_tasks(self, tasks: List[Task]) -> str:
|
|
44
|
+
"""
|
|
45
|
+
Format a list of tasks as a string.
|
|
46
|
+
|
|
47
|
+
Args:
|
|
48
|
+
tasks: The list of tasks to format
|
|
49
|
+
|
|
50
|
+
Returns:
|
|
51
|
+
A formatted string representation of the tasks
|
|
52
|
+
"""
|
|
53
|
+
if not tasks:
|
|
54
|
+
return "No tasks found."
|
|
55
|
+
|
|
56
|
+
result = [f"{task.id}. {task.description} ({task.status.value})"
|
|
57
|
+
for i, task in enumerate(tasks)]
|
|
58
|
+
|
|
59
|
+
return "\n".join(result)
|
|
60
|
+
|
|
61
|
+
@property
|
|
62
|
+
def descriptor(self):
|
|
63
|
+
"""
|
|
64
|
+
Get the descriptor for the tool.
|
|
65
|
+
|
|
66
|
+
Returns:
|
|
67
|
+
The descriptor dictionary
|
|
68
|
+
"""
|
|
69
|
+
return {
|
|
70
|
+
"type": "function",
|
|
71
|
+
"function": {
|
|
72
|
+
"name": "list_tasks",
|
|
73
|
+
"description": "List all tasks in the task list.",
|
|
74
|
+
"parameters": {
|
|
75
|
+
"type": "object",
|
|
76
|
+
"properties": {},
|
|
77
|
+
"additionalProperties": False
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
from unittest.mock import Mock
|
|
2
|
+
|
|
3
|
+
import pytest
|
|
4
|
+
|
|
5
|
+
from mojentic.llm.tools.ephemeral_task_manager.ephemeral_task_list import EphemeralTaskList, Task, TaskStatus
|
|
6
|
+
from mojentic.llm.tools.ephemeral_task_manager.list_tasks_tool import ListTasksTool
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
@pytest.fixture
|
|
10
|
+
def mock_task_list():
|
|
11
|
+
mock = Mock(spec=EphemeralTaskList)
|
|
12
|
+
return mock
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
@pytest.fixture
|
|
16
|
+
def list_tasks_tool(mock_task_list):
|
|
17
|
+
return ListTasksTool(task_list=mock_task_list)
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
class DescribeListTasksTool:
|
|
21
|
+
def should_call_list_tasks(self, list_tasks_tool, mock_task_list):
|
|
22
|
+
mock_tasks = [
|
|
23
|
+
Task(id=1, description="Task 1", status=TaskStatus.PENDING),
|
|
24
|
+
Task(id=2, description="Task 2", status=TaskStatus.IN_PROGRESS),
|
|
25
|
+
Task(id=3, description="Task 3", status=TaskStatus.COMPLETED)
|
|
26
|
+
]
|
|
27
|
+
mock_task_list.list_tasks.return_value = mock_tasks
|
|
28
|
+
|
|
29
|
+
list_tasks_tool.run()
|
|
30
|
+
|
|
31
|
+
mock_task_list.list_tasks.assert_called_once()
|
|
32
|
+
|
|
33
|
+
def should_handle_empty_list(self, list_tasks_tool, mock_task_list):
|
|
34
|
+
mock_task_list.list_tasks.return_value = []
|
|
35
|
+
|
|
36
|
+
list_tasks_tool.run()
|
|
37
|
+
|
|
38
|
+
mock_task_list.list_tasks.assert_called_once()
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Tool for prepending a new task to the beginning of the ephemeral task manager list.
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
from typing import Dict
|
|
6
|
+
|
|
7
|
+
from mojentic.llm.tools.llm_tool import LLMTool
|
|
8
|
+
from mojentic.llm.tools.ephemeral_task_manager.ephemeral_task_list import EphemeralTaskList
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class PrependTaskTool(LLMTool):
|
|
12
|
+
"""
|
|
13
|
+
Tool for prepending a new task to the beginning of the ephemeral task manager list.
|
|
14
|
+
"""
|
|
15
|
+
|
|
16
|
+
def __init__(self, task_list: EphemeralTaskList):
|
|
17
|
+
"""
|
|
18
|
+
Initialize the tool with a shared task list.
|
|
19
|
+
|
|
20
|
+
Args:
|
|
21
|
+
task_list: The shared task list to use
|
|
22
|
+
"""
|
|
23
|
+
self._task_list = task_list
|
|
24
|
+
|
|
25
|
+
def run(self, description: str) -> Dict[str, str]:
|
|
26
|
+
"""
|
|
27
|
+
Prepend a new task to the beginning of the list.
|
|
28
|
+
|
|
29
|
+
Args:
|
|
30
|
+
description: The description of the task
|
|
31
|
+
|
|
32
|
+
Returns:
|
|
33
|
+
A dictionary with the result of the operation
|
|
34
|
+
|
|
35
|
+
Raises:
|
|
36
|
+
ValueError: If there's an error adding the task
|
|
37
|
+
"""
|
|
38
|
+
try:
|
|
39
|
+
task = self._task_list.prepend_task(description=description)
|
|
40
|
+
return {
|
|
41
|
+
"id": task.id,
|
|
42
|
+
"description": task.description,
|
|
43
|
+
"status": task.status.value,
|
|
44
|
+
"summary": f"Task '{task.id}' prepended successfully"
|
|
45
|
+
}
|
|
46
|
+
except ValueError as e:
|
|
47
|
+
return {
|
|
48
|
+
"error": str(e),
|
|
49
|
+
"summary": f"Failed to prepend task: {str(e)}"
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
@property
|
|
53
|
+
def descriptor(self):
|
|
54
|
+
"""
|
|
55
|
+
Get the descriptor for the tool.
|
|
56
|
+
|
|
57
|
+
Returns:
|
|
58
|
+
The descriptor dictionary
|
|
59
|
+
"""
|
|
60
|
+
return {
|
|
61
|
+
"type": "function",
|
|
62
|
+
"function": {
|
|
63
|
+
"name": "prepend_task",
|
|
64
|
+
"description": "Prepend a new task to the beginning of the task list with a description. The task will start with 'pending' status.",
|
|
65
|
+
"parameters": {
|
|
66
|
+
"type": "object",
|
|
67
|
+
"properties": {
|
|
68
|
+
"description": {
|
|
69
|
+
"type": "string",
|
|
70
|
+
"description": "The description of the task"
|
|
71
|
+
}
|
|
72
|
+
},
|
|
73
|
+
"required": ["description"],
|
|
74
|
+
"additionalProperties": False
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
from unittest.mock import Mock
|
|
2
|
+
|
|
3
|
+
import pytest
|
|
4
|
+
|
|
5
|
+
from mojentic.llm.tools.ephemeral_task_manager.ephemeral_task_list import EphemeralTaskList, Task, TaskStatus
|
|
6
|
+
from mojentic.llm.tools.ephemeral_task_manager.prepend_task_tool import PrependTaskTool
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
@pytest.fixture
|
|
10
|
+
def mock_task_list():
|
|
11
|
+
mock = Mock(spec=EphemeralTaskList)
|
|
12
|
+
return mock
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
@pytest.fixture
|
|
16
|
+
def prepend_task_tool(mock_task_list):
|
|
17
|
+
return PrependTaskTool(task_list=mock_task_list)
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
class DescribePrependTaskTool:
|
|
21
|
+
def should_call_prepend_task_with_description(self, prepend_task_tool, mock_task_list):
|
|
22
|
+
mock_task = Task(id=1, description="Test task", status=TaskStatus.PENDING)
|
|
23
|
+
mock_task_list.prepend_task.return_value = mock_task
|
|
24
|
+
|
|
25
|
+
prepend_task_tool.run(description="Test task")
|
|
26
|
+
|
|
27
|
+
mock_task_list.prepend_task.assert_called_once_with(description="Test task")
|
|
28
|
+
|
|
29
|
+
def should_handle_error_when_prepend_task_fails(self, prepend_task_tool, mock_task_list):
|
|
30
|
+
mock_task_list.prepend_task.side_effect = ValueError("Test error")
|
|
31
|
+
|
|
32
|
+
prepend_task_tool.run(description="Test task")
|
|
33
|
+
|
|
34
|
+
mock_task_list.prepend_task.assert_called_once_with(description="Test task")
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Tool for starting a task in the ephemeral task manager.
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
from typing import Dict
|
|
6
|
+
|
|
7
|
+
from mojentic.llm.tools.llm_tool import LLMTool
|
|
8
|
+
from mojentic.llm.tools.ephemeral_task_manager.ephemeral_task_list import EphemeralTaskList
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class StartTaskTool(LLMTool):
|
|
12
|
+
"""
|
|
13
|
+
Tool for starting a task in the ephemeral task manager.
|
|
14
|
+
|
|
15
|
+
This tool changes a task's status from PENDING to IN_PROGRESS.
|
|
16
|
+
"""
|
|
17
|
+
|
|
18
|
+
def __init__(self, task_list: EphemeralTaskList):
|
|
19
|
+
"""
|
|
20
|
+
Initialize the tool with a shared task list.
|
|
21
|
+
|
|
22
|
+
Args:
|
|
23
|
+
task_list: The shared task list to use
|
|
24
|
+
"""
|
|
25
|
+
self._task_list = task_list
|
|
26
|
+
|
|
27
|
+
def run(self, id: int) -> Dict[str, str]:
|
|
28
|
+
"""
|
|
29
|
+
Start a task by changing its status from PENDING to IN_PROGRESS.
|
|
30
|
+
|
|
31
|
+
Args:
|
|
32
|
+
id: The ID of the task to start
|
|
33
|
+
|
|
34
|
+
Returns:
|
|
35
|
+
A dictionary with the result of the operation
|
|
36
|
+
|
|
37
|
+
Raises:
|
|
38
|
+
ValueError: If no task with the given ID exists or if the task is not in PENDING status
|
|
39
|
+
"""
|
|
40
|
+
try:
|
|
41
|
+
# Convert id to int if it's a string
|
|
42
|
+
task_id = int(id) if isinstance(id, str) else id
|
|
43
|
+
task = self._task_list.start_task(id=task_id)
|
|
44
|
+
return {
|
|
45
|
+
"id": task.id,
|
|
46
|
+
"description": task.description,
|
|
47
|
+
"status": task.status.value,
|
|
48
|
+
"summary": f"Task '{id}' started successfully"
|
|
49
|
+
}
|
|
50
|
+
except ValueError as e:
|
|
51
|
+
return {
|
|
52
|
+
"error": str(e),
|
|
53
|
+
"summary": f"Failed to start task: {str(e)}"
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
@property
|
|
57
|
+
def descriptor(self):
|
|
58
|
+
"""
|
|
59
|
+
Get the descriptor for the tool.
|
|
60
|
+
|
|
61
|
+
Returns:
|
|
62
|
+
The descriptor dictionary
|
|
63
|
+
"""
|
|
64
|
+
return {
|
|
65
|
+
"type": "function",
|
|
66
|
+
"function": {
|
|
67
|
+
"name": "start_task",
|
|
68
|
+
"description": "Start a task by changing its status from PENDING to IN_PROGRESS.",
|
|
69
|
+
"parameters": {
|
|
70
|
+
"type": "object",
|
|
71
|
+
"properties": {
|
|
72
|
+
"id": {
|
|
73
|
+
"type": "integer",
|
|
74
|
+
"description": "The ID of the task to start"
|
|
75
|
+
}
|
|
76
|
+
},
|
|
77
|
+
"required": ["id"],
|
|
78
|
+
"additionalProperties": False
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
from unittest.mock import Mock
|
|
2
|
+
|
|
3
|
+
import pytest
|
|
4
|
+
|
|
5
|
+
from mojentic.llm.tools.ephemeral_task_manager.ephemeral_task_list import EphemeralTaskList, Task, TaskStatus
|
|
6
|
+
from mojentic.llm.tools.ephemeral_task_manager.start_task_tool import StartTaskTool
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
@pytest.fixture
|
|
10
|
+
def mock_task_list():
|
|
11
|
+
mock = Mock(spec=EphemeralTaskList)
|
|
12
|
+
return mock
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
@pytest.fixture
|
|
16
|
+
def start_task_tool(mock_task_list):
|
|
17
|
+
return StartTaskTool(task_list=mock_task_list)
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
class DescribeStartTaskTool:
|
|
21
|
+
def should_call_start_task_with_correct_id(self, start_task_tool, mock_task_list):
|
|
22
|
+
mock_task = Task(id=1, description="Test task", status=TaskStatus.IN_PROGRESS)
|
|
23
|
+
mock_task_list.start_task.return_value = mock_task
|
|
24
|
+
|
|
25
|
+
start_task_tool.run(id=1)
|
|
26
|
+
|
|
27
|
+
mock_task_list.start_task.assert_called_once_with(id=1)
|
|
28
|
+
|
|
29
|
+
def should_convert_string_id_to_int(self, start_task_tool, mock_task_list):
|
|
30
|
+
mock_task = Task(id=1, description="Test task", status=TaskStatus.IN_PROGRESS)
|
|
31
|
+
mock_task_list.start_task.return_value = mock_task
|
|
32
|
+
|
|
33
|
+
start_task_tool.run(id="1")
|
|
34
|
+
|
|
35
|
+
mock_task_list.start_task.assert_called_once_with(id=1)
|
|
36
|
+
|
|
37
|
+
def should_handle_error_when_start_task_fails(self, start_task_tool, mock_task_list):
|
|
38
|
+
error_message = "Task '1' cannot be started because it is not in PENDING status"
|
|
39
|
+
mock_task_list.start_task.side_effect = ValueError(error_message)
|
|
40
|
+
|
|
41
|
+
start_task_tool.run(id=1)
|
|
42
|
+
|
|
43
|
+
mock_task_list.start_task.assert_called_once_with(id=1)
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import json
|
|
2
|
+
import os
|
|
3
|
+
|
|
4
|
+
import serpapi
|
|
5
|
+
|
|
6
|
+
from mojentic.llm.tools.llm_tool import LLMTool
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class OrganicWebSearchTool(LLMTool):
|
|
10
|
+
def __init__(self, api_key: str = None):
|
|
11
|
+
self.api_key = api_key or os.getenv("SERPAPI_API_KEY")
|
|
12
|
+
|
|
13
|
+
def run(self, query: str, engine: str = "google", location: str = None, hl: str = "en", gl="ca") -> str:
|
|
14
|
+
results = serpapi.search(q=query, engine=engine, location=location, hl=hl, gl=gl, api_key=self.api_key)
|
|
15
|
+
# Limiting this to the organic results cuts this from 60K to 3K tokens
|
|
16
|
+
return json.dumps(results['organic_results'], indent=2)
|
|
17
|
+
|
|
18
|
+
@property
|
|
19
|
+
def descriptor(self):
|
|
20
|
+
return {
|
|
21
|
+
"type": "function",
|
|
22
|
+
"function": {
|
|
23
|
+
"name": "organic_web_search",
|
|
24
|
+
"description":
|
|
25
|
+
"Search the Internet for information matching the given query and return the organic search results.",
|
|
26
|
+
"parameters": {
|
|
27
|
+
"type": "object",
|
|
28
|
+
"properties": {
|
|
29
|
+
"query": {
|
|
30
|
+
"type": "string",
|
|
31
|
+
"description": "The search query."
|
|
32
|
+
},
|
|
33
|
+
},
|
|
34
|
+
"required": ["query"]
|
|
35
|
+
},
|
|
36
|
+
},
|
|
37
|
+
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
from mojentic.llm.tools.llm_tool import LLMTool
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
class TellUserTool(LLMTool):
|
|
5
|
+
def run(self, message: str) -> str:
|
|
6
|
+
print(f"\n\n\nMESSAGE FROM ASSISTANT:\n{message}")
|
|
7
|
+
return "Message delivered to user."
|
|
8
|
+
|
|
9
|
+
@property
|
|
10
|
+
def descriptor(self):
|
|
11
|
+
return {
|
|
12
|
+
"type": "function",
|
|
13
|
+
"function": {
|
|
14
|
+
"name": "tell_user",
|
|
15
|
+
"description": "Display a message to the user without expecting a response. Use this to send important intermediate information to the user as you work on completing their request.",
|
|
16
|
+
"parameters": {
|
|
17
|
+
"type": "object",
|
|
18
|
+
"properties": {
|
|
19
|
+
"message": {
|
|
20
|
+
"type": "string",
|
|
21
|
+
"description": "The important message you want to display to the user."
|
|
22
|
+
}
|
|
23
|
+
},
|
|
24
|
+
"required": ["message"]
|
|
25
|
+
},
|
|
26
|
+
},
|
|
27
|
+
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: mojentic
|
|
3
|
-
Version: 0.5.
|
|
3
|
+
Version: 0.5.5
|
|
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
6
|
Project-URL: Homepage, https://github.com/mojility/mojentic
|
|
@@ -13,6 +13,7 @@ Description-Content-Type: text/markdown
|
|
|
13
13
|
License-File: LICENSE.md
|
|
14
14
|
Requires-Dist: pydantic
|
|
15
15
|
Requires-Dist: structlog
|
|
16
|
+
Requires-Dist: numpy
|
|
16
17
|
Requires-Dist: ollama
|
|
17
18
|
Requires-Dist: openai
|
|
18
19
|
Requires-Dist: anthropic
|