diff --git a/trustgraph-base/trustgraph/base/__init__.py b/trustgraph-base/trustgraph/base/__init__.py index 24b10390..1687f794 100644 --- a/trustgraph-base/trustgraph/base/__init__.py +++ b/trustgraph-base/trustgraph/base/__init__.py @@ -29,4 +29,5 @@ from . document_embeddings_client import DocumentEmbeddingsClientSpec from . agent_service import AgentService from . graph_rag_client import GraphRagClientSpec from . tool_service import ToolService +from . tool_client import ToolClientSpec diff --git a/trustgraph-base/trustgraph/base/tool_client.py b/trustgraph-base/trustgraph/base/tool_client.py new file mode 100644 index 00000000..e8955758 --- /dev/null +++ b/trustgraph-base/trustgraph/base/tool_client.py @@ -0,0 +1,40 @@ + +import json + +from . request_response_spec import RequestResponse, RequestResponseSpec +from .. schema import ToolRequest, ToolResponse + +class ToolClient(RequestResponse): + + async def invoke(self, name, parameters={}, timeout=600): + + if parameters is None: + parameters = {} + + resp = await self.request( + ToolRequest( + name = name, + parameters = json.dumps(parameters), + ), + timeout=timeout + ) + + if resp.error: + raise RuntimeError(resp.error.message) + + if resp.text: return resp.text + + return json.loads(resp.object) + +class ToolClientSpec(RequestResponseSpec): + def __init__( + self, request_name, response_name, + ): + super(ToolClientSpec, self).__init__( + request_name = request_name, + request_schema = ToolRequest, + response_name = response_name, + response_schema = ToolResponse, + impl = ToolClient, + ) + diff --git a/trustgraph-flow/trustgraph/agent/react/agent_manager.py b/trustgraph-flow/trustgraph/agent/react/agent_manager.py index d20b86f7..7405d7e1 100644 --- a/trustgraph-flow/trustgraph/agent/react/agent_manager.py +++ b/trustgraph-flow/trustgraph/agent/react/agent_manager.py @@ -14,12 +14,19 @@ class AgentManager: async def reason(self, question, history, context): + print(f"calling reason: {question}", flush=True) + tools = self.tools + print(f"in reason", flush=True) + print(tools, flush=True) + tool_names = ",".join([ t for t in self.tools.keys() ]) + print("Tool names:", tool_names, flush=True) + variables = { "question": question, "tools": [ @@ -83,6 +90,9 @@ class AgentManager: async def react(self, question, history, think, observe, context): + logger.info(f"question: {question}") + print(f"question: {question}", flush=True) + act = await self.reason( question = question, history = history, @@ -104,13 +114,12 @@ class AgentManager: else: raise RuntimeError(f"No action for {act.name}!") - print("TOOL>>>", act) + print("TOOL>>>", act, flush=True) + resp = await action.implementation(context).invoke( **act.arguments ) - print("RSETUL", resp) - resp = resp.strip() logger.info(f"resp: {resp}") diff --git a/trustgraph-flow/trustgraph/agent/react/service.py b/trustgraph-flow/trustgraph/agent/react/service.py index beb17fd4..0a4cdd80 100755 --- a/trustgraph-flow/trustgraph/agent/react/service.py +++ b/trustgraph-flow/trustgraph/agent/react/service.py @@ -7,11 +7,11 @@ import re import sys from ... base import AgentService, TextCompletionClientSpec, PromptClientSpec -from ... base import GraphRagClientSpec +from ... base import GraphRagClientSpec, ToolClientSpec from ... schema import AgentRequest, AgentResponse, AgentStep, Error -from . tools import KnowledgeQueryImpl, TextCompletionImpl +from . tools import KnowledgeQueryImpl, TextCompletionImpl, McpToolImpl from . agent_manager import AgentManager from . types import Final, Action, Tool, Argument @@ -67,6 +67,13 @@ class Processor(AgentService): ) ) + self.register_specification( + ToolClientSpec( + request_name = "mcp-tool-request", + response_name = "mcp-tool-response", + ) + ) + async def on_tools_config(self, config, version): print("Loading configuration version", version) @@ -106,6 +113,8 @@ class Processor(AgentService): impl = KnowledgeQueryImpl elif impl_id == "text-completion": impl = TextCompletionImpl + elif impl_id == "mcp-tool": + impl = McpToolImpl else: raise RuntimeError( f"Tool-kind {impl_id} not known" @@ -181,6 +190,8 @@ class Processor(AgentService): await respond(r) + print("Call React", flush=True) + act = await self.agent.react( question = request.question, history = history, diff --git a/trustgraph-flow/trustgraph/agent/react/tools.py b/trustgraph-flow/trustgraph/agent/react/tools.py index 31568b25..a31953a4 100644 --- a/trustgraph-flow/trustgraph/agent/react/tools.py +++ b/trustgraph-flow/trustgraph/agent/react/tools.py @@ -23,3 +23,22 @@ class TextCompletionImpl: arguments.get("question") ) +# This tool implementation knows how to do MCP tool invocation. This uses +# the mcp-tool service. +class McpToolImpl: + def __init__(self, context): + self.context = context + async def invoke(self, **arguments): + client = self.context("mcp-tool-request") + print("MCP tool invocation...", flush=True) + output = await client.invoke( + name = "time", + parameters = {}, + ) + + print(output) + + print(type(output)) + + return output["result"] +