mojentic 0.7.1__tar.gz → 0.7.3__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.
- {mojentic-0.7.1/src/mojentic.egg-info → mojentic-0.7.3}/PKG-INFO +3 -2
- {mojentic-0.7.1 → mojentic-0.7.3}/pyproject.toml +3 -2
- {mojentic-0.7.1 → mojentic-0.7.3}/src/_examples/broker_as_tool.py +13 -10
- mojentic-0.7.3/src/_examples/coding_file_tool.py +181 -0
- {mojentic-0.7.1 → mojentic-0.7.3}/src/_examples/file_tool.py +5 -3
- {mojentic-0.7.1 → mojentic-0.7.3}/src/mojentic/__init__.py +2 -7
- {mojentic-0.7.1 → mojentic-0.7.3}/src/mojentic/agents/__init__.py +11 -2
- mojentic-0.7.3/src/mojentic/context/__init__.py +5 -0
- mojentic-0.7.3/src/mojentic/llm/__init__.py +16 -0
- mojentic-0.7.3/src/mojentic/llm/gateways/__init__.py +25 -0
- {mojentic-0.7.1 → mojentic-0.7.3}/src/mojentic/llm/gateways/anthropic.py +1 -1
- {mojentic-0.7.1 → mojentic-0.7.3}/src/mojentic/llm/gateways/llm_gateway.py +3 -1
- {mojentic-0.7.1 → mojentic-0.7.3}/src/mojentic/llm/gateways/ollama.py +6 -0
- {mojentic-0.7.1 → mojentic-0.7.3}/src/mojentic/llm/gateways/openai.py +5 -0
- {mojentic-0.7.1 → mojentic-0.7.3}/src/mojentic/llm/llm_broker.py +42 -24
- {mojentic-0.7.1 → mojentic-0.7.3}/src/mojentic/llm/message_composers.py +1 -1
- mojentic-0.7.3/src/mojentic/llm/registry/__init__.py +6 -0
- {mojentic-0.7.1 → mojentic-0.7.3}/src/mojentic/llm/registry/populate_registry_from_ollama.py +13 -12
- mojentic-0.7.3/src/mojentic/llm/tools/__init__.py +18 -0
- {mojentic-0.7.1 → mojentic-0.7.3}/src/mojentic/llm/tools/date_resolver.py +5 -2
- mojentic-0.7.3/src/mojentic/llm/tools/ephemeral_task_manager/__init__.py +27 -0
- mojentic-0.7.3/src/mojentic/llm/tools/file_manager.py +685 -0
- mojentic-0.7.3/src/mojentic/llm/tools/file_manager_spec.py +723 -0
- {mojentic-0.7.1 → mojentic-0.7.3}/src/mojentic/llm/tools/tool_wrapper.py +7 -3
- {mojentic-0.7.1 → mojentic-0.7.3}/src/mojentic/tracer/__init__.py +8 -3
- {mojentic-0.7.1 → mojentic-0.7.3/src/mojentic.egg-info}/PKG-INFO +3 -2
- {mojentic-0.7.1 → mojentic-0.7.3}/src/mojentic.egg-info/SOURCES.txt +1 -0
- {mojentic-0.7.1 → mojentic-0.7.3}/src/mojentic.egg-info/requires.txt +2 -1
- mojentic-0.7.1/src/_examples/coding_file_tool.py +0 -88
- mojentic-0.7.1/src/mojentic/context/__init__.py +0 -1
- mojentic-0.7.1/src/mojentic/llm/__init__.py +0 -4
- mojentic-0.7.1/src/mojentic/llm/gateways/__init__.py +0 -3
- mojentic-0.7.1/src/mojentic/llm/registry/__init__.py +0 -0
- mojentic-0.7.1/src/mojentic/llm/tools/__init__.py +0 -0
- mojentic-0.7.1/src/mojentic/llm/tools/ephemeral_task_manager/__init__.py +0 -27
- mojentic-0.7.1/src/mojentic/llm/tools/file_manager.py +0 -124
- {mojentic-0.7.1 → mojentic-0.7.3}/LICENSE.md +0 -0
- {mojentic-0.7.1 → mojentic-0.7.3}/README.md +0 -0
- {mojentic-0.7.1 → mojentic-0.7.3}/setup.cfg +0 -0
- {mojentic-0.7.1 → mojentic-0.7.3}/src/_examples/__init__.py +0 -0
- {mojentic-0.7.1 → mojentic-0.7.3}/src/_examples/async_dispatcher_example.py +0 -0
- {mojentic-0.7.1 → mojentic-0.7.3}/src/_examples/async_llm_example.py +0 -0
- {mojentic-0.7.1 → mojentic-0.7.3}/src/_examples/broker_examples.py +0 -0
- {mojentic-0.7.1 → mojentic-0.7.3}/src/_examples/broker_image_examples.py +0 -0
- {mojentic-0.7.1 → mojentic-0.7.3}/src/_examples/characterize_ollama.py +0 -0
- {mojentic-0.7.1 → mojentic-0.7.3}/src/_examples/characterize_openai.py +0 -0
- {mojentic-0.7.1 → mojentic-0.7.3}/src/_examples/chat_session.py +0 -0
- {mojentic-0.7.1 → mojentic-0.7.3}/src/_examples/chat_session_with_tool.py +0 -0
- {mojentic-0.7.1 → mojentic-0.7.3}/src/_examples/current_datetime_tool_example.py +0 -0
- {mojentic-0.7.1 → mojentic-0.7.3}/src/_examples/design_analysis.py +0 -0
- {mojentic-0.7.1 → mojentic-0.7.3}/src/_examples/embeddings.py +0 -0
- {mojentic-0.7.1 → mojentic-0.7.3}/src/_examples/ensures_files_exist.py +0 -0
- {mojentic-0.7.1 → mojentic-0.7.3}/src/_examples/ephemeral_task_manager_example.py +0 -0
- {mojentic-0.7.1 → mojentic-0.7.3}/src/_examples/file_deduplication.py +0 -0
- {mojentic-0.7.1 → mojentic-0.7.3}/src/_examples/image_analysis.py +0 -0
- {mojentic-0.7.1 → mojentic-0.7.3}/src/_examples/image_broker.py +0 -0
- {mojentic-0.7.1 → mojentic-0.7.3}/src/_examples/image_broker_splat.py +0 -0
- {mojentic-0.7.1 → mojentic-0.7.3}/src/_examples/iterative_solver.py +0 -0
- {mojentic-0.7.1 → mojentic-0.7.3}/src/_examples/list_models.py +0 -0
- {mojentic-0.7.1 → mojentic-0.7.3}/src/_examples/oversized_embeddings.py +0 -0
- {mojentic-0.7.1 → mojentic-0.7.3}/src/_examples/raw.py +0 -0
- {mojentic-0.7.1 → mojentic-0.7.3}/src/_examples/react/__init__.py +0 -0
- {mojentic-0.7.1 → mojentic-0.7.3}/src/_examples/react/agents/__init__.py +0 -0
- {mojentic-0.7.1 → mojentic-0.7.3}/src/_examples/react/agents/decisioning_agent.py +0 -0
- {mojentic-0.7.1 → mojentic-0.7.3}/src/_examples/react/agents/thinking_agent.py +0 -0
- {mojentic-0.7.1 → mojentic-0.7.3}/src/_examples/react/formatters.py +0 -0
- {mojentic-0.7.1 → mojentic-0.7.3}/src/_examples/react/models/__init__.py +0 -0
- {mojentic-0.7.1 → mojentic-0.7.3}/src/_examples/react/models/base.py +0 -0
- {mojentic-0.7.1 → mojentic-0.7.3}/src/_examples/react/models/events.py +0 -0
- {mojentic-0.7.1 → mojentic-0.7.3}/src/_examples/react.py +0 -0
- {mojentic-0.7.1 → mojentic-0.7.3}/src/_examples/recursive_agent.py +0 -0
- {mojentic-0.7.1 → mojentic-0.7.3}/src/_examples/routed_send_response.py +0 -0
- {mojentic-0.7.1 → mojentic-0.7.3}/src/_examples/simple_llm.py +0 -0
- {mojentic-0.7.1 → mojentic-0.7.3}/src/_examples/simple_llm_repl.py +0 -0
- {mojentic-0.7.1 → mojentic-0.7.3}/src/_examples/simple_structured.py +0 -0
- {mojentic-0.7.1 → mojentic-0.7.3}/src/_examples/simple_tool.py +0 -0
- {mojentic-0.7.1 → mojentic-0.7.3}/src/_examples/solver_chat_session.py +0 -0
- {mojentic-0.7.1 → mojentic-0.7.3}/src/_examples/streaming.py +0 -0
- {mojentic-0.7.1 → mojentic-0.7.3}/src/_examples/tell_user_example.py +0 -0
- {mojentic-0.7.1 → mojentic-0.7.3}/src/_examples/tracer_demo.py +0 -0
- {mojentic-0.7.1 → mojentic-0.7.3}/src/_examples/working_memory.py +0 -0
- {mojentic-0.7.1 → mojentic-0.7.3}/src/mojentic/agents/agent_broker.py +0 -0
- {mojentic-0.7.1 → mojentic-0.7.3}/src/mojentic/agents/async_aggregator_agent.py +0 -0
- {mojentic-0.7.1 → mojentic-0.7.3}/src/mojentic/agents/async_aggregator_agent_spec.py +0 -0
- {mojentic-0.7.1 → mojentic-0.7.3}/src/mojentic/agents/async_llm_agent.py +0 -0
- {mojentic-0.7.1 → mojentic-0.7.3}/src/mojentic/agents/async_llm_agent_spec.py +0 -0
- {mojentic-0.7.1 → mojentic-0.7.3}/src/mojentic/agents/base_agent.py +0 -0
- {mojentic-0.7.1 → mojentic-0.7.3}/src/mojentic/agents/base_async_agent.py +0 -0
- {mojentic-0.7.1 → mojentic-0.7.3}/src/mojentic/agents/base_llm_agent.py +0 -0
- {mojentic-0.7.1 → mojentic-0.7.3}/src/mojentic/agents/base_llm_agent_spec.py +0 -0
- {mojentic-0.7.1 → mojentic-0.7.3}/src/mojentic/agents/correlation_aggregator_agent.py +0 -0
- {mojentic-0.7.1 → mojentic-0.7.3}/src/mojentic/agents/iterative_problem_solver.py +0 -0
- {mojentic-0.7.1 → mojentic-0.7.3}/src/mojentic/agents/output_agent.py +0 -0
- {mojentic-0.7.1 → mojentic-0.7.3}/src/mojentic/agents/simple_recursive_agent.py +0 -0
- {mojentic-0.7.1 → mojentic-0.7.3}/src/mojentic/async_dispatcher.py +0 -0
- {mojentic-0.7.1 → mojentic-0.7.3}/src/mojentic/async_dispatcher_spec.py +0 -0
- {mojentic-0.7.1 → mojentic-0.7.3}/src/mojentic/context/shared_working_memory.py +0 -0
- {mojentic-0.7.1 → mojentic-0.7.3}/src/mojentic/dispatcher.py +0 -0
- {mojentic-0.7.1 → mojentic-0.7.3}/src/mojentic/event.py +0 -0
- {mojentic-0.7.1 → mojentic-0.7.3}/src/mojentic/llm/chat_session.py +0 -0
- {mojentic-0.7.1 → mojentic-0.7.3}/src/mojentic/llm/chat_session_spec.py +0 -0
- {mojentic-0.7.1 → mojentic-0.7.3}/src/mojentic/llm/gateways/anthropic_messages_adapter.py +0 -0
- {mojentic-0.7.1 → mojentic-0.7.3}/src/mojentic/llm/gateways/embeddings_gateway.py +0 -0
- {mojentic-0.7.1 → mojentic-0.7.3}/src/mojentic/llm/gateways/file_gateway.py +0 -0
- {mojentic-0.7.1 → mojentic-0.7.3}/src/mojentic/llm/gateways/models.py +0 -0
- {mojentic-0.7.1 → mojentic-0.7.3}/src/mojentic/llm/gateways/ollama_messages_adapter.py +0 -0
- {mojentic-0.7.1 → mojentic-0.7.3}/src/mojentic/llm/gateways/ollama_messages_adapter_spec.py +0 -0
- {mojentic-0.7.1 → mojentic-0.7.3}/src/mojentic/llm/gateways/openai_message_adapter_spec.py +0 -0
- {mojentic-0.7.1 → mojentic-0.7.3}/src/mojentic/llm/gateways/openai_messages_adapter.py +0 -0
- {mojentic-0.7.1 → mojentic-0.7.3}/src/mojentic/llm/gateways/tokenizer_gateway.py +0 -0
- {mojentic-0.7.1 → mojentic-0.7.3}/src/mojentic/llm/llm_broker_spec.py +0 -0
- {mojentic-0.7.1 → mojentic-0.7.3}/src/mojentic/llm/message_composers_spec.py +0 -0
- {mojentic-0.7.1 → mojentic-0.7.3}/src/mojentic/llm/registry/llm_registry.py +0 -0
- {mojentic-0.7.1 → mojentic-0.7.3}/src/mojentic/llm/registry/models.py +0 -0
- {mojentic-0.7.1 → mojentic-0.7.3}/src/mojentic/llm/tools/ask_user_tool.py +0 -0
- {mojentic-0.7.1 → mojentic-0.7.3}/src/mojentic/llm/tools/current_datetime.py +0 -0
- {mojentic-0.7.1 → mojentic-0.7.3}/src/mojentic/llm/tools/date_resolver_spec.py +0 -0
- {mojentic-0.7.1 → mojentic-0.7.3}/src/mojentic/llm/tools/ephemeral_task_manager/append_task_tool.py +0 -0
- {mojentic-0.7.1 → mojentic-0.7.3}/src/mojentic/llm/tools/ephemeral_task_manager/append_task_tool_spec.py +0 -0
- {mojentic-0.7.1 → mojentic-0.7.3}/src/mojentic/llm/tools/ephemeral_task_manager/clear_tasks_tool.py +0 -0
- {mojentic-0.7.1 → mojentic-0.7.3}/src/mojentic/llm/tools/ephemeral_task_manager/clear_tasks_tool_spec.py +0 -0
- {mojentic-0.7.1 → mojentic-0.7.3}/src/mojentic/llm/tools/ephemeral_task_manager/complete_task_tool.py +0 -0
- {mojentic-0.7.1 → mojentic-0.7.3}/src/mojentic/llm/tools/ephemeral_task_manager/complete_task_tool_spec.py +0 -0
- {mojentic-0.7.1 → mojentic-0.7.3}/src/mojentic/llm/tools/ephemeral_task_manager/ephemeral_task_list.py +0 -0
- {mojentic-0.7.1 → mojentic-0.7.3}/src/mojentic/llm/tools/ephemeral_task_manager/ephemeral_task_list_spec.py +0 -0
- {mojentic-0.7.1 → mojentic-0.7.3}/src/mojentic/llm/tools/ephemeral_task_manager/insert_task_after_tool.py +0 -0
- {mojentic-0.7.1 → mojentic-0.7.3}/src/mojentic/llm/tools/ephemeral_task_manager/insert_task_after_tool_spec.py +0 -0
- {mojentic-0.7.1 → mojentic-0.7.3}/src/mojentic/llm/tools/ephemeral_task_manager/list_tasks_tool.py +0 -0
- {mojentic-0.7.1 → mojentic-0.7.3}/src/mojentic/llm/tools/ephemeral_task_manager/list_tasks_tool_spec.py +0 -0
- {mojentic-0.7.1 → mojentic-0.7.3}/src/mojentic/llm/tools/ephemeral_task_manager/prepend_task_tool.py +0 -0
- {mojentic-0.7.1 → mojentic-0.7.3}/src/mojentic/llm/tools/ephemeral_task_manager/prepend_task_tool_spec.py +0 -0
- {mojentic-0.7.1 → mojentic-0.7.3}/src/mojentic/llm/tools/ephemeral_task_manager/start_task_tool.py +0 -0
- {mojentic-0.7.1 → mojentic-0.7.3}/src/mojentic/llm/tools/ephemeral_task_manager/start_task_tool_spec.py +0 -0
- {mojentic-0.7.1 → mojentic-0.7.3}/src/mojentic/llm/tools/llm_tool.py +0 -0
- {mojentic-0.7.1 → mojentic-0.7.3}/src/mojentic/llm/tools/llm_tool_spec.py +0 -0
- {mojentic-0.7.1 → mojentic-0.7.3}/src/mojentic/llm/tools/organic_web_search.py +0 -0
- {mojentic-0.7.1 → mojentic-0.7.3}/src/mojentic/llm/tools/tell_user_tool.py +0 -0
- {mojentic-0.7.1 → mojentic-0.7.3}/src/mojentic/llm/tools/tool_wrapper_spec.py +0 -0
- {mojentic-0.7.1 → mojentic-0.7.3}/src/mojentic/router.py +0 -0
- {mojentic-0.7.1 → mojentic-0.7.3}/src/mojentic/router_spec.py +0 -0
- {mojentic-0.7.1 → mojentic-0.7.3}/src/mojentic/tracer/event_store.py +0 -0
- {mojentic-0.7.1 → mojentic-0.7.3}/src/mojentic/tracer/event_store_spec.py +0 -0
- {mojentic-0.7.1 → mojentic-0.7.3}/src/mojentic/tracer/null_tracer.py +0 -0
- {mojentic-0.7.1 → mojentic-0.7.3}/src/mojentic/tracer/tracer_events.py +0 -0
- {mojentic-0.7.1 → mojentic-0.7.3}/src/mojentic/tracer/tracer_events_spec.py +0 -0
- {mojentic-0.7.1 → mojentic-0.7.3}/src/mojentic/tracer/tracer_system.py +0 -0
- {mojentic-0.7.1 → mojentic-0.7.3}/src/mojentic/tracer/tracer_system_spec.py +0 -0
- {mojentic-0.7.1 → mojentic-0.7.3}/src/mojentic/utils/__init__.py +0 -0
- {mojentic-0.7.1 → mojentic-0.7.3}/src/mojentic/utils/formatting.py +0 -0
- {mojentic-0.7.1 → mojentic-0.7.3}/src/mojentic.egg-info/dependency_links.txt +0 -0
- {mojentic-0.7.1 → mojentic-0.7.3}/src/mojentic.egg-info/top_level.txt +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: mojentic
|
|
3
|
-
Version: 0.7.
|
|
3
|
+
Version: 0.7.3
|
|
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/svetzal/mojentic
|
|
@@ -30,9 +30,10 @@ Requires-Dist: pytest-cov; extra == "dev"
|
|
|
30
30
|
Requires-Dist: pytest-mock>=3.10.0; extra == "dev"
|
|
31
31
|
Requires-Dist: flake8>=6.0.0; extra == "dev"
|
|
32
32
|
Requires-Dist: mkdocs; extra == "dev"
|
|
33
|
+
Requires-Dist: mkdocs-material; extra == "dev"
|
|
34
|
+
Requires-Dist: mkdocs-llmstxt; extra == "dev"
|
|
33
35
|
Requires-Dist: mkdocstrings[python]; extra == "dev"
|
|
34
36
|
Requires-Dist: griffe-fieldz; extra == "dev"
|
|
35
|
-
Requires-Dist: mkdocs-material; extra == "dev"
|
|
36
37
|
Requires-Dist: pymdown-extensions; extra == "dev"
|
|
37
38
|
Dynamic: license-file
|
|
38
39
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "mojentic"
|
|
3
|
-
version = "0.7.
|
|
3
|
+
version = "0.7.3"
|
|
4
4
|
authors = [
|
|
5
5
|
{ name = "Stacey Vetzal", email = "stacey@vetzal.com" },
|
|
6
6
|
]
|
|
@@ -36,9 +36,10 @@ dev = [
|
|
|
36
36
|
"pytest-mock>=3.10.0",
|
|
37
37
|
"flake8>=6.0.0",
|
|
38
38
|
"mkdocs",
|
|
39
|
+
"mkdocs-material",
|
|
40
|
+
"mkdocs-llmstxt",
|
|
39
41
|
"mkdocstrings[python]",
|
|
40
42
|
"griffe-fieldz",
|
|
41
|
-
"mkdocs-material",
|
|
42
43
|
"pymdown-extensions",
|
|
43
44
|
]
|
|
44
45
|
|
|
@@ -2,7 +2,7 @@ import os
|
|
|
2
2
|
from mojentic.agents.base_llm_agent import BaseLLMAgent
|
|
3
3
|
from mojentic.llm.llm_broker import LLMBroker
|
|
4
4
|
from mojentic.llm.tools.date_resolver import ResolveDateTool
|
|
5
|
-
from mojentic.llm.tools.file_manager import FileManager, ListFilesTool, ReadFileTool, WriteFileTool
|
|
5
|
+
from mojentic.llm.tools.file_manager import FileManager, ListFilesTool, ReadFileTool, WriteFileTool, FilesystemGateway
|
|
6
6
|
from mojentic.llm.tools.tool_wrapper import ToolWrapper
|
|
7
7
|
|
|
8
8
|
#
|
|
@@ -15,19 +15,22 @@ temporal_specialist = BaseLLMAgent(
|
|
|
15
15
|
behaviour="You are a historian and sociologist who focuses on sorting out temporal events, determining what happened or will happen when."
|
|
16
16
|
)
|
|
17
17
|
|
|
18
|
+
if not os.path.exists("local"):
|
|
19
|
+
os.mkdir("local")
|
|
20
|
+
|
|
21
|
+
# Create a filesystem gateway for the local directory
|
|
22
|
+
fs = FilesystemGateway(base_path="local")
|
|
23
|
+
|
|
18
24
|
knowledge_specialist = BaseLLMAgent(
|
|
19
25
|
llm=LLMBroker(model="llama3.3-70b-32k"),
|
|
20
26
|
tools=[
|
|
21
|
-
ListFilesTool(
|
|
22
|
-
ReadFileTool(
|
|
23
|
-
WriteFileTool(
|
|
27
|
+
ListFilesTool(fs),
|
|
28
|
+
ReadFileTool(fs),
|
|
29
|
+
WriteFileTool(fs),
|
|
24
30
|
],
|
|
25
31
|
behaviour="You are a knowledge management agent who focuses on sorting out facts and information, able to organize elemental ideas and make connections between them. You can list files to find out where you stored information, read files to review that information, and write files to store that information for later retrieval."
|
|
26
32
|
)
|
|
27
33
|
|
|
28
|
-
if not os.path.exists("local"):
|
|
29
|
-
os.mkdir("local")
|
|
30
|
-
|
|
31
34
|
|
|
32
35
|
|
|
33
36
|
coordinator = BaseLLMAgent(
|
|
@@ -42,14 +45,14 @@ coordinator = BaseLLMAgent(
|
|
|
42
45
|
result = coordinator.generate_response("""
|
|
43
46
|
|
|
44
47
|
I have several things I need to do this week:
|
|
45
|
-
|
|
48
|
+
|
|
46
49
|
- On Monday, I need to ensure that I have called Scotiabank and ordered replacement cards for my current, credit, and line of credit accounts.
|
|
47
50
|
- On Wednesday, I need to drive into Toronto for work. While in Toronto I need to pick up razors. I need to make sure I see Gregg, Britney and Vikram.
|
|
48
51
|
- On Thursday, I need to ensure I'm up by 7am so that I can be showered and ready for work by 9.
|
|
49
52
|
- On Friday, I need to ensure that I have my laundry done and my bags packed for my trip to Ottawa.
|
|
50
|
-
|
|
53
|
+
|
|
51
54
|
Create me a markdown file for each day of the week, named "YYYY-MM-DD-ToDo.md" where the date is the date of that day.
|
|
52
55
|
Make a list of to-do items in the markdown file, and add a section for the day's daily notes that I can fill out each day.
|
|
53
56
|
""")
|
|
54
57
|
|
|
55
|
-
print(result)
|
|
58
|
+
print(result)
|
|
@@ -0,0 +1,181 @@
|
|
|
1
|
+
"""
|
|
2
|
+
This example demonstrates the use of all the file management tools available in mojentic.
|
|
3
|
+
|
|
4
|
+
It creates an IterativeProblemSolver with access to a comprehensive set of file management tools:
|
|
5
|
+
- ListFilesTool: List files in the top-level directory
|
|
6
|
+
- ListAllFilesTool: List all files recursively
|
|
7
|
+
- ReadFileTool: Read the content of a file
|
|
8
|
+
- WriteFileTool: Write content to a file
|
|
9
|
+
- CreateDirectoryTool: Create a new directory
|
|
10
|
+
- FindFilesByGlobTool: Find files matching a glob pattern
|
|
11
|
+
- FindFilesContainingTool: Find files containing text matching a regex pattern
|
|
12
|
+
- FindLinesMatchingTool: Find lines in a file matching a regex pattern
|
|
13
|
+
- EditFileWithDiffTool: Edit a file by applying a diff
|
|
14
|
+
|
|
15
|
+
The solver is then given a task that requires using all of these tools.
|
|
16
|
+
"""
|
|
17
|
+
|
|
18
|
+
import os
|
|
19
|
+
from pathlib import Path
|
|
20
|
+
|
|
21
|
+
from mojentic.agents.iterative_problem_solver import IterativeProblemSolver
|
|
22
|
+
from mojentic.llm.gateways import OpenAIGateway
|
|
23
|
+
from mojentic.llm.llm_broker import LLMBroker
|
|
24
|
+
from mojentic.llm.tools.ephemeral_task_manager import EphemeralTaskList, AppendTaskTool, \
|
|
25
|
+
ClearTasksTool, CompleteTaskTool, InsertTaskAfterTool, ListTasksTool, PrependTaskTool, \
|
|
26
|
+
StartTaskTool
|
|
27
|
+
from mojentic.llm.tools.file_manager import (
|
|
28
|
+
ReadFileTool, WriteFileTool, ListFilesTool, ListAllFilesTool,
|
|
29
|
+
FindFilesByGlobTool, FindFilesContainingTool, FindLinesMatchingTool,
|
|
30
|
+
EditFileWithDiffTool, CreateDirectoryTool, FilesystemGateway
|
|
31
|
+
)
|
|
32
|
+
|
|
33
|
+
base_dir = Path(__file__).parent.parent.parent.parent / "code-playground2"
|
|
34
|
+
|
|
35
|
+
# Initialize the LLM broker
|
|
36
|
+
|
|
37
|
+
api_key = os.getenv("OPENAI_API_KEY")
|
|
38
|
+
gateway = OpenAIGateway(api_key)
|
|
39
|
+
llm = LLMBroker(model="o4-mini", gateway=gateway)
|
|
40
|
+
|
|
41
|
+
# llm = LLMBroker("qwen2.5-coder:32b")
|
|
42
|
+
# llm = LLMBroker("llama3.3")
|
|
43
|
+
# llm = LLMBroker(model="qwen3-128k:32b")
|
|
44
|
+
|
|
45
|
+
# Create a filesystem gateway for the sandbox
|
|
46
|
+
fs = FilesystemGateway(base_path=str(base_dir))
|
|
47
|
+
|
|
48
|
+
task_manager = EphemeralTaskList()
|
|
49
|
+
|
|
50
|
+
# Create a list of all file management tools
|
|
51
|
+
tools = [
|
|
52
|
+
ReadFileTool(fs),
|
|
53
|
+
WriteFileTool(fs),
|
|
54
|
+
ListFilesTool(fs),
|
|
55
|
+
ListAllFilesTool(fs),
|
|
56
|
+
CreateDirectoryTool(fs),
|
|
57
|
+
FindFilesByGlobTool(fs),
|
|
58
|
+
FindFilesContainingTool(fs),
|
|
59
|
+
FindLinesMatchingTool(fs),
|
|
60
|
+
EditFileWithDiffTool(fs),
|
|
61
|
+
AppendTaskTool(task_manager),
|
|
62
|
+
ClearTasksTool(task_manager),
|
|
63
|
+
CompleteTaskTool(task_manager),
|
|
64
|
+
InsertTaskAfterTool(task_manager),
|
|
65
|
+
ListTasksTool(task_manager),
|
|
66
|
+
PrependTaskTool(task_manager),
|
|
67
|
+
StartTaskTool(task_manager),
|
|
68
|
+
]
|
|
69
|
+
|
|
70
|
+
# Create the iterative problem solver with the tools
|
|
71
|
+
solver = IterativeProblemSolver(
|
|
72
|
+
llm=llm,
|
|
73
|
+
available_tools=tools,
|
|
74
|
+
max_iterations=5,
|
|
75
|
+
system_prompt="""
|
|
76
|
+
# 0 - Project Identity & Context
|
|
77
|
+
|
|
78
|
+
You are an expert and principled software engineer, well versed in writing Python games. You work
|
|
79
|
+
carefully and purposefully and always check your work with an eye to testability and correctness.
|
|
80
|
+
You know that every line of code you write is a liability, and you take care that every line
|
|
81
|
+
matters.
|
|
82
|
+
|
|
83
|
+
# 1 - Universal Engineering Principles
|
|
84
|
+
|
|
85
|
+
* **Code is communication** — optimise for the next human reader.
|
|
86
|
+
* **Simple Design Heuristics** — guiding principles, not iron laws; consult the user when you
|
|
87
|
+
need to break them.
|
|
88
|
+
1. **All tests pass** — correctness is non‑negotiable.
|
|
89
|
+
2. **Reveals intent** — code should read like an explanation.
|
|
90
|
+
3. **No *****knowledge***** duplication** — avoid multiple spots that must change together;
|
|
91
|
+
identical code is only a smell when it hides duplicate *decisions*.
|
|
92
|
+
4. **Minimal entities** — remove unnecessary indirection, classes, or parameters.
|
|
93
|
+
* **Small, safe increments** — single‑reason commits; avoid speculative work (**YAGNI**).
|
|
94
|
+
* **Tests are the executable spec** — red first, green always; test behaviour not implementation.
|
|
95
|
+
* **Compose over inherit**; favour pure functions where practical, avoid side-effects.
|
|
96
|
+
* **Functional core, imperative shell** — isolate pure business logic from I/O and side effects;
|
|
97
|
+
push mutations to the system boundaries, build mockable gateways at those boundaries.
|
|
98
|
+
* **Psychological safety** — review code, not colleagues; critique ideas, not authors.
|
|
99
|
+
* **Version‑control etiquette** — descriptive commit messages, branch from `main`, PRs require
|
|
100
|
+
green CI.
|
|
101
|
+
|
|
102
|
+
# 2 - Python‑Specific Conventions
|
|
103
|
+
|
|
104
|
+
## 2.1 Runtime & Environment
|
|
105
|
+
|
|
106
|
+
* **Python ≥ 3.12** (support the two most recent LTS releases).
|
|
107
|
+
* Create an isolated environment:
|
|
108
|
+
|
|
109
|
+
```bash
|
|
110
|
+
python -m venv .venv
|
|
111
|
+
source .venv/bin/activate
|
|
112
|
+
pip install -e ".[dev]"
|
|
113
|
+
```
|
|
114
|
+
* Enforce `pre‑commit` hooks (flake8, mypy, black, pytest).
|
|
115
|
+
|
|
116
|
+
## 2.2 Core Libraries
|
|
117
|
+
|
|
118
|
+
Mandatory: pydantic, structlog, pytest, pytest-spec, pytest-cov, pytest-mock, flake8, black,
|
|
119
|
+
pre‑commit, mkdocs‑material. Add new libs only when they eliminate **significant** boilerplate or
|
|
120
|
+
risk.
|
|
121
|
+
|
|
122
|
+
## 2.3 Project Structure & Imports
|
|
123
|
+
|
|
124
|
+
* **src‑layout**: code in `src/<package_name>/`; tests live beside code as `*_spec.py`.
|
|
125
|
+
* Import order: 1) stdlib, 2) third‑party, 3) first‑party — each group alphabetised with a blank
|
|
126
|
+
line.
|
|
127
|
+
|
|
128
|
+
## 2.4 Naming & Style
|
|
129
|
+
|
|
130
|
+
* `snake_case` for functions & vars, `PascalCase` for classes, `UPPER_SNAKE` for constants.
|
|
131
|
+
* Prefix intentionally unused vars/args with `_`.
|
|
132
|
+
* **flake8** (with plugins) handles linting, and **black** auto‑formats code. Max line length
|
|
133
|
+
**100**.
|
|
134
|
+
* Cyclomatic complexity cap: **10** (flake8 `C901`).
|
|
135
|
+
* Use **f‑strings**; avoid magic numbers.
|
|
136
|
+
|
|
137
|
+
## 2.5 Type Hints & Docstrings
|
|
138
|
+
|
|
139
|
+
* **100% type coverage**; code must pass `mypy --strict`.
|
|
140
|
+
* Use `pydantic.BaseModel` for data models; don't use bare `@dataclass` if validation is needed.
|
|
141
|
+
* Docstrings in Google format; omit the obvious.
|
|
142
|
+
|
|
143
|
+
## 2.6 Logging & Observability
|
|
144
|
+
|
|
145
|
+
* Configure **structlog** for JSON output by default.
|
|
146
|
+
* Never use `print` for diagnostics; reserve for user‑facing CLI UX.
|
|
147
|
+
* Log levels: `DEBUG` (dev detail) → `INFO` (lifecycle) → `WARNING` (recoverable) → `ERROR` (user
|
|
148
|
+
visible).
|
|
149
|
+
|
|
150
|
+
## 2.7 Testing Strategy
|
|
151
|
+
|
|
152
|
+
* **pytest** with **pytest-spec** for specification-style output.
|
|
153
|
+
* Test files end with `_spec.py` and live in the same folder as the code under test.
|
|
154
|
+
* Use **Arrange / Act / Assert** blocks separated by a blank line (no comments) **or** BDD
|
|
155
|
+
`describe/should` classes.
|
|
156
|
+
* Function names: use `should_*` and BDD-style specifications.
|
|
157
|
+
* Class names: use `Describe*` and BDD-style test suites.
|
|
158
|
+
* **Mocking**: Use `pytest-mock`'s `mocker` fixture; don't use `unittest.mock.MagicMock` directly.
|
|
159
|
+
* One behavioural expectation per test. Fixtures are helpers, not magic.
|
|
160
|
+
* Tests should fail for one reason, avoid multiple assert statements, split the test cases
|
|
161
|
+
|
|
162
|
+
# 3 - Planning and Goal Tracking
|
|
163
|
+
|
|
164
|
+
- Use the provided task manager tools to create your plans and work through them step by step.
|
|
165
|
+
- Before declaring yourself finished list all tasks, ensure they are all complete, and that you
|
|
166
|
+
have not missed any steps
|
|
167
|
+
- If you've missed or forgotten some steps, add them to the task list and continue
|
|
168
|
+
- When all tasks are complete, and you can think of no more to add, declare yourself finished.
|
|
169
|
+
"""
|
|
170
|
+
)
|
|
171
|
+
|
|
172
|
+
# Define the task
|
|
173
|
+
task = """
|
|
174
|
+
Create a new Python project that is a graphical clone of Windows MineSweeper.
|
|
175
|
+
|
|
176
|
+
Ensure it is well tested.
|
|
177
|
+
"""
|
|
178
|
+
|
|
179
|
+
# Solve the task and print the result
|
|
180
|
+
result = solver.solve(task)
|
|
181
|
+
print(result)
|
|
@@ -5,7 +5,7 @@ from mojentic.agents.output_agent import OutputAgent
|
|
|
5
5
|
from mojentic.dispatcher import Dispatcher
|
|
6
6
|
from mojentic.event import Event
|
|
7
7
|
from mojentic.llm.llm_broker import LLMBroker
|
|
8
|
-
from mojentic.llm.tools.file_manager import ReadFileTool, WriteFileTool
|
|
8
|
+
from mojentic.llm.tools.file_manager import ReadFileTool, WriteFileTool, FilesystemGateway
|
|
9
9
|
from mojentic.router import Router
|
|
10
10
|
|
|
11
11
|
|
|
@@ -25,8 +25,10 @@ class RequestAgent(BaseLLMAgent):
|
|
|
25
25
|
def __init__(self, llm: LLMBroker):
|
|
26
26
|
super().__init__(llm,
|
|
27
27
|
"You are a helpful assistant.")
|
|
28
|
-
|
|
29
|
-
|
|
28
|
+
# Create a filesystem gateway for the /tmp directory
|
|
29
|
+
fs = FilesystemGateway(base_path="/tmp")
|
|
30
|
+
self.add_tool(ReadFileTool(fs))
|
|
31
|
+
self.add_tool(WriteFileTool(fs))
|
|
30
32
|
|
|
31
33
|
def receive_event(self, event):
|
|
32
34
|
response = self.generate_response(event.text)
|
|
@@ -7,10 +7,12 @@ import logging
|
|
|
7
7
|
|
|
8
8
|
import structlog
|
|
9
9
|
|
|
10
|
+
# Core components
|
|
10
11
|
from .dispatcher import Dispatcher
|
|
11
12
|
from .event import Event
|
|
12
13
|
from .router import Router
|
|
13
14
|
|
|
15
|
+
# Initialize logging
|
|
14
16
|
logging.basicConfig(level=logging.INFO)
|
|
15
17
|
structlog.configure(logger_factory=structlog.stdlib.LoggerFactory(), processors=[
|
|
16
18
|
structlog.stdlib.filter_by_level,
|
|
@@ -22,13 +24,6 @@ structlog.configure(logger_factory=structlog.stdlib.LoggerFactory(), processors=
|
|
|
22
24
|
logger = structlog.get_logger()
|
|
23
25
|
logger.info("Starting logger")
|
|
24
26
|
|
|
25
|
-
# logger = logging.getLogger("mojentic")
|
|
26
|
-
# path = Path().cwd()
|
|
27
|
-
# log_filename = path / 'output.log'
|
|
28
|
-
# print(f"Logging to {log_filename}")
|
|
29
|
-
# logging.basicConfig(filename=log_filename, level=logging.DEBUG)
|
|
30
|
-
# logger.info("Starting logger")
|
|
31
|
-
|
|
32
27
|
|
|
33
28
|
__version__: str
|
|
34
29
|
try:
|
|
@@ -1,7 +1,16 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Mojentic agents module for creating and working with various agent types.
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
# Base agent types
|
|
1
6
|
from .base_agent import BaseAgent
|
|
2
|
-
from .base_llm_agent import BaseLLMAgent
|
|
3
|
-
|
|
7
|
+
from .base_llm_agent import BaseLLMAgent, BaseLLMAgentWithMemory
|
|
8
|
+
|
|
9
|
+
# Special purpose agents
|
|
4
10
|
from .correlation_aggregator_agent import BaseAggregatingAgent
|
|
5
11
|
from .output_agent import OutputAgent
|
|
6
12
|
from .iterative_problem_solver import IterativeProblemSolver
|
|
7
13
|
from .simple_recursive_agent import SimpleRecursiveAgent
|
|
14
|
+
|
|
15
|
+
# Agent brokering
|
|
16
|
+
from .agent_broker import AgentBroker
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Mojentic LLM module for interacting with Large Language Models.
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
# Main LLM components
|
|
6
|
+
from .llm_broker import LLMBroker
|
|
7
|
+
from .chat_session import ChatSession
|
|
8
|
+
from .message_composers import MessageBuilder, FileTypeSensor
|
|
9
|
+
from .registry.llm_registry import LLMRegistry
|
|
10
|
+
|
|
11
|
+
# Re-export gateway components at the LLM level
|
|
12
|
+
from .gateways.models import (
|
|
13
|
+
LLMMessage,
|
|
14
|
+
LLMGatewayResponse,
|
|
15
|
+
MessageRole
|
|
16
|
+
)
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Mojentic LLM gateways module for connecting to various LLM providers.
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
# Gateway implementations
|
|
6
|
+
from .llm_gateway import LLMGateway
|
|
7
|
+
from .ollama import OllamaGateway
|
|
8
|
+
from .openai import OpenAIGateway
|
|
9
|
+
from .anthropic import AnthropicGateway
|
|
10
|
+
from .file_gateway import FileGateway
|
|
11
|
+
from .embeddings_gateway import EmbeddingsGateway
|
|
12
|
+
from .tokenizer_gateway import TokenizerGateway
|
|
13
|
+
|
|
14
|
+
# Message adapters
|
|
15
|
+
from .anthropic_messages_adapter import adapt_messages_to_anthropic
|
|
16
|
+
from .ollama_messages_adapter import adapt_messages_to_ollama
|
|
17
|
+
from .openai_messages_adapter import adapt_messages_to_openai
|
|
18
|
+
|
|
19
|
+
# Common models
|
|
20
|
+
from .models import (
|
|
21
|
+
LLMMessage,
|
|
22
|
+
MessageRole,
|
|
23
|
+
LLMGatewayResponse,
|
|
24
|
+
LLMToolCall
|
|
25
|
+
)
|
|
@@ -29,7 +29,7 @@ class AnthropicGateway(LLMGateway):
|
|
|
29
29
|
response = self.client.messages.create(
|
|
30
30
|
**anthropic_args,
|
|
31
31
|
temperature=args.get('temperature', 1.0),
|
|
32
|
-
max_tokens=args.get('num_predict', 2000),
|
|
32
|
+
max_tokens=args.get('max_tokens', args.get('num_predict', 2000)),
|
|
33
33
|
# thinking={
|
|
34
34
|
# "type": "enabled",
|
|
35
35
|
# "budget_tokens": 32768,
|
|
@@ -19,7 +19,7 @@ class LLMGateway:
|
|
|
19
19
|
object_model: Optional[Type[BaseModel]] = None,
|
|
20
20
|
tools: Optional[List[LLMTool]] = None,
|
|
21
21
|
temperature: float = 1.0,
|
|
22
|
-
num_ctx: int = 32768,
|
|
22
|
+
num_ctx: int = 32768, max_tokens: int = 16384,
|
|
23
23
|
num_predict: int = -1) -> LLMGatewayResponse:
|
|
24
24
|
"""
|
|
25
25
|
Complete the LLM request.
|
|
@@ -39,6 +39,8 @@ class LLMGateway:
|
|
|
39
39
|
The temperature to use for the response. Defaults to 1.0.
|
|
40
40
|
num_ctx : int
|
|
41
41
|
The number of context tokens to use. Defaults to 32768.
|
|
42
|
+
max_tokens : int
|
|
43
|
+
The maximum number of tokens to generate. Defaults to 16384.
|
|
42
44
|
num_predict : int
|
|
43
45
|
The number of tokens to predict. Defaults to no limit.
|
|
44
46
|
|
|
@@ -35,6 +35,8 @@ class OllamaGateway(LLMGateway):
|
|
|
35
35
|
)
|
|
36
36
|
if args.get('num_predict', 0) > 0:
|
|
37
37
|
options.num_predict = args['num_predict']
|
|
38
|
+
if 'max_tokens' in args:
|
|
39
|
+
options.num_predict = args['max_tokens']
|
|
38
40
|
return options
|
|
39
41
|
|
|
40
42
|
def complete(self, **args) -> LLMGatewayResponse:
|
|
@@ -56,6 +58,8 @@ class OllamaGateway(LLMGateway):
|
|
|
56
58
|
The temperature to use for the response. Defaults to 1.0.
|
|
57
59
|
num_ctx : int, optional
|
|
58
60
|
The number of context tokens to use. Defaults to 32768.
|
|
61
|
+
max_tokens : int, optional
|
|
62
|
+
The maximum number of tokens to generate. Defaults to 16384.
|
|
59
63
|
num_predict : int, optional
|
|
60
64
|
The number of tokens to predict. Defaults to no limit.
|
|
61
65
|
|
|
@@ -120,6 +124,8 @@ class OllamaGateway(LLMGateway):
|
|
|
120
124
|
The temperature to use for the response. Defaults to 1.0.
|
|
121
125
|
num_ctx : int, optional
|
|
122
126
|
The number of context tokens to use. Defaults to 32768.
|
|
127
|
+
max_tokens : int, optional
|
|
128
|
+
The maximum number of tokens to generate. Defaults to 16384.
|
|
123
129
|
num_predict : int, optional
|
|
124
130
|
The number of tokens to predict. Defaults to no limit.
|
|
125
131
|
|
|
@@ -46,6 +46,8 @@ class OpenAIGateway(LLMGateway):
|
|
|
46
46
|
The temperature to use for the response. Defaults to 1.0.
|
|
47
47
|
num_ctx : int, optional
|
|
48
48
|
The number of context tokens to use. Defaults to 32768.
|
|
49
|
+
max_tokens : int, optional
|
|
50
|
+
The maximum number of tokens to generate. Defaults to 16384.
|
|
49
51
|
num_predict : int, optional
|
|
50
52
|
The number of tokens to predict. Defaults to no limit.
|
|
51
53
|
|
|
@@ -68,6 +70,9 @@ class OpenAIGateway(LLMGateway):
|
|
|
68
70
|
if 'tools' in args and args['tools'] is not None:
|
|
69
71
|
openai_args['tools'] = [t.descriptor for t in args['tools']]
|
|
70
72
|
|
|
73
|
+
if 'max_tokens' in args:
|
|
74
|
+
openai_args['max_tokens'] = args['max_tokens']
|
|
75
|
+
|
|
71
76
|
response = completion(**openai_args)
|
|
72
77
|
|
|
73
78
|
object = None
|
|
@@ -5,18 +5,19 @@ from typing import List, Optional, Type
|
|
|
5
5
|
import structlog
|
|
6
6
|
from pydantic import BaseModel
|
|
7
7
|
|
|
8
|
-
from mojentic.tracer.tracer_system import TracerSystem
|
|
9
8
|
from mojentic.llm.gateways.llm_gateway import LLMGateway
|
|
10
9
|
from mojentic.llm.gateways.models import MessageRole, LLMMessage, LLMGatewayResponse
|
|
11
10
|
from mojentic.llm.gateways.ollama import OllamaGateway
|
|
12
11
|
from mojentic.llm.gateways.tokenizer_gateway import TokenizerGateway
|
|
12
|
+
from mojentic.tracer.tracer_system import TracerSystem
|
|
13
13
|
|
|
14
14
|
logger = structlog.get_logger()
|
|
15
15
|
|
|
16
16
|
|
|
17
17
|
class LLMBroker():
|
|
18
18
|
"""
|
|
19
|
-
This class is responsible for managing interaction with a Large Language Model. It abstracts
|
|
19
|
+
This class is responsible for managing interaction with a Large Language Model. It abstracts
|
|
20
|
+
the user
|
|
20
21
|
from the specific mechanics of the LLM and provides a common interface for generating responses.
|
|
21
22
|
"""
|
|
22
23
|
|
|
@@ -25,7 +26,8 @@ class LLMBroker():
|
|
|
25
26
|
model: str
|
|
26
27
|
tracer: Optional[TracerSystem]
|
|
27
28
|
|
|
28
|
-
def __init__(self, model: str, gateway: Optional[LLMGateway] = None,
|
|
29
|
+
def __init__(self, model: str, gateway: Optional[LLMGateway] = None,
|
|
30
|
+
tokenizer: Optional[TokenizerGateway] = None,
|
|
29
31
|
tracer: Optional[TracerSystem] = None):
|
|
30
32
|
"""
|
|
31
33
|
Create an instance of the LLMBroker.
|
|
@@ -35,10 +37,12 @@ class LLMBroker():
|
|
|
35
37
|
model
|
|
36
38
|
The name of the model to use.
|
|
37
39
|
gateway
|
|
38
|
-
The gateway to use for communication with the LLM. If None, a gateway is created that
|
|
40
|
+
The gateway to use for communication with the LLM. If None, a gateway is created that
|
|
41
|
+
will utilize a local
|
|
39
42
|
Ollama server.
|
|
40
43
|
tokenizer
|
|
41
|
-
The gateway to use for tokenization. This is used to log approximate token counts for
|
|
44
|
+
The gateway to use for tokenization. This is used to log approximate token counts for
|
|
45
|
+
the LLM calls. If
|
|
42
46
|
None, `mxbai-embed-large` is used on a local Ollama server.
|
|
43
47
|
tracer
|
|
44
48
|
Optional tracer system to record LLM calls and responses.
|
|
@@ -58,8 +62,9 @@ class LLMBroker():
|
|
|
58
62
|
else:
|
|
59
63
|
self.adapter = gateway
|
|
60
64
|
|
|
61
|
-
def generate(self, messages: List[LLMMessage], tools=None, temperature=1.0, num_ctx=32768,
|
|
62
|
-
|
|
65
|
+
def generate(self, messages: List[LLMMessage], tools=None, temperature=1.0, num_ctx=32768,
|
|
66
|
+
num_predict=-1, max_tokens=16384,
|
|
67
|
+
correlation_id: str = None) -> str:
|
|
63
68
|
"""
|
|
64
69
|
Generate a text response from the LLM.
|
|
65
70
|
|
|
@@ -68,7 +73,8 @@ class LLMBroker():
|
|
|
68
73
|
messages : LLMMessage
|
|
69
74
|
A list of messages to send to the LLM.
|
|
70
75
|
tools : List[Tool]
|
|
71
|
-
A list of tools to use with the LLM. If a tool call is requested, the tool will be
|
|
76
|
+
A list of tools to use with the LLM. If a tool call is requested, the tool will be
|
|
77
|
+
called and the output
|
|
72
78
|
will be included in the response.
|
|
73
79
|
temperature : float
|
|
74
80
|
The temperature to use for the response. Defaults to 1.0
|
|
@@ -91,10 +97,11 @@ class LLMBroker():
|
|
|
91
97
|
messages_for_tracer = [m.model_dump() for m in messages]
|
|
92
98
|
|
|
93
99
|
# Record LLM call in tracer
|
|
94
|
-
tools_for_tracer = [{"name": t.name, "description": t.description} for t in
|
|
100
|
+
tools_for_tracer = [{"name": t.name, "description": t.description} for t in
|
|
101
|
+
tools] if tools else None
|
|
95
102
|
self.tracer.record_llm_call(
|
|
96
|
-
self.model,
|
|
97
|
-
messages_for_tracer,
|
|
103
|
+
self.model,
|
|
104
|
+
messages_for_tracer,
|
|
98
105
|
temperature,
|
|
99
106
|
tools=tools_for_tracer,
|
|
100
107
|
source=type(self),
|
|
@@ -110,12 +117,14 @@ class LLMBroker():
|
|
|
110
117
|
tools=tools,
|
|
111
118
|
temperature=temperature,
|
|
112
119
|
num_ctx=num_ctx,
|
|
113
|
-
num_predict=num_predict
|
|
120
|
+
num_predict=num_predict,
|
|
121
|
+
max_tokens=max_tokens)
|
|
114
122
|
|
|
115
123
|
call_duration_ms = (time.time() - start_time) * 1000
|
|
116
124
|
|
|
117
125
|
# Record LLM response in tracer
|
|
118
|
-
tool_calls_for_tracer = [tc.model_dump() for tc in
|
|
126
|
+
tool_calls_for_tracer = [tc.model_dump() for tc in
|
|
127
|
+
result.tool_calls] if result.tool_calls else None
|
|
119
128
|
self.tracer.record_llm_response(
|
|
120
129
|
self.model,
|
|
121
130
|
result.content,
|
|
@@ -153,13 +162,17 @@ class LLMBroker():
|
|
|
153
162
|
logger.info('Function output', output=output)
|
|
154
163
|
messages.append(LLMMessage(role=MessageRole.Assistant, tool_calls=[tool_call]))
|
|
155
164
|
messages.append(
|
|
156
|
-
LLMMessage(role=MessageRole.Tool, content=json.dumps(output),
|
|
157
|
-
|
|
158
|
-
|
|
165
|
+
LLMMessage(role=MessageRole.Tool, content=json.dumps(output),
|
|
166
|
+
tool_calls=[tool_call]))
|
|
167
|
+
# {'role': 'tool', 'content': str(output), 'name': tool_call.name,
|
|
168
|
+
# 'tool_call_id': tool_call.id})
|
|
169
|
+
return self.generate(messages, tools, temperature, num_ctx, num_predict,
|
|
170
|
+
correlation_id=correlation_id)
|
|
159
171
|
else:
|
|
160
172
|
logger.warn('Function not found', function=tool_call.name)
|
|
161
173
|
logger.info('Expected usage of missing function', expected_usage=tool_call)
|
|
162
|
-
# raise Exception('Unknown tool function requested:',
|
|
174
|
+
# raise Exception('Unknown tool function requested:',
|
|
175
|
+
# requested_tool.function.name)
|
|
163
176
|
|
|
164
177
|
return result.content
|
|
165
178
|
|
|
@@ -170,8 +183,9 @@ class LLMBroker():
|
|
|
170
183
|
content += message.content
|
|
171
184
|
return content
|
|
172
185
|
|
|
173
|
-
def generate_object(self, messages: List[LLMMessage], object_model: Type[BaseModel],
|
|
174
|
-
num_predict=-1,
|
|
186
|
+
def generate_object(self, messages: List[LLMMessage], object_model: Type[BaseModel],
|
|
187
|
+
temperature=1.0, num_ctx=32768, num_predict=-1, max_tokens=16384,
|
|
188
|
+
correlation_id: str = None) -> BaseModel:
|
|
175
189
|
"""
|
|
176
190
|
Generate a structured response from the LLM and return it as an object.
|
|
177
191
|
|
|
@@ -203,8 +217,8 @@ class LLMBroker():
|
|
|
203
217
|
|
|
204
218
|
# Record LLM call in tracer
|
|
205
219
|
self.tracer.record_llm_call(
|
|
206
|
-
self.model,
|
|
207
|
-
messages_for_tracer,
|
|
220
|
+
self.model,
|
|
221
|
+
messages_for_tracer,
|
|
208
222
|
temperature,
|
|
209
223
|
tools=None,
|
|
210
224
|
source=type(self),
|
|
@@ -214,14 +228,18 @@ class LLMBroker():
|
|
|
214
228
|
# Measure call duration for audit
|
|
215
229
|
start_time = time.time()
|
|
216
230
|
|
|
217
|
-
result = self.adapter.complete(model=self.model, messages=messages,
|
|
218
|
-
|
|
231
|
+
result = self.adapter.complete(model=self.model, messages=messages,
|
|
232
|
+
object_model=object_model,
|
|
233
|
+
temperature=temperature, num_ctx=num_ctx,
|
|
234
|
+
num_predict=num_predict, max_tokens=max_tokens)
|
|
219
235
|
|
|
220
236
|
call_duration_ms = (time.time() - start_time) * 1000
|
|
221
237
|
|
|
222
238
|
# Record LLM response in tracer with object representation
|
|
223
239
|
# Convert object to string for tracer
|
|
224
|
-
object_str = str(result.object.model_dump()) if hasattr(result.object,
|
|
240
|
+
object_str = str(result.object.model_dump()) if hasattr(result.object,
|
|
241
|
+
"model_dump") else str(
|
|
242
|
+
result.object)
|
|
225
243
|
self.tracer.record_llm_response(
|
|
226
244
|
self.model,
|
|
227
245
|
f"Structured response: {object_str}",
|
|
@@ -216,7 +216,7 @@ class MessageBuilder():
|
|
|
216
216
|
|
|
217
217
|
return self
|
|
218
218
|
|
|
219
|
-
def add_files(self, *file_paths: Union[str, Path]) -> "MessageBuilder":
|
|
219
|
+
def add_files(self, *file_paths: List[Union[str, Path]]) -> "MessageBuilder":
|
|
220
220
|
"""
|
|
221
221
|
Add multiple text files to the message, ignoring binary files.
|
|
222
222
|
|