mirror of
https://github.com/trustgraph-ai/trustgraph.git
synced 2026-07-22 03:31:02 +02:00
Specify request/response queues
This commit is contained in:
parent
ade19c7d4b
commit
5f53cf1848
3 changed files with 46 additions and 49 deletions
|
|
@ -62,13 +62,14 @@ elif impl_id == "text-completion":
|
||||||
#### Tier 1: Tool Service Descriptor
|
#### Tier 1: Tool Service Descriptor
|
||||||
|
|
||||||
A tool service defines a Pulsar service interface. It declares:
|
A tool service defines a Pulsar service interface. It declares:
|
||||||
- The topic to call
|
- The Pulsar queues for request/response
|
||||||
- Configuration parameters it requires from tools that use it
|
- Configuration parameters it requires from tools that use it
|
||||||
|
|
||||||
```json
|
```json
|
||||||
{
|
{
|
||||||
"id": "custom-rag",
|
"id": "custom-rag",
|
||||||
"topic": "custom-rag-request",
|
"request-queue": "non-persistent://tg/request/custom-rag",
|
||||||
|
"response-queue": "non-persistent://tg/response/custom-rag",
|
||||||
"config-params": [
|
"config-params": [
|
||||||
{"name": "collection", "required": true}
|
{"name": "collection", "required": true}
|
||||||
]
|
]
|
||||||
|
|
@ -80,7 +81,8 @@ A tool service that needs no configuration parameters:
|
||||||
```json
|
```json
|
||||||
{
|
{
|
||||||
"id": "calculator",
|
"id": "calculator",
|
||||||
"topic": "calc-request",
|
"request-queue": "non-persistent://tg/request/calc",
|
||||||
|
"response-queue": "non-persistent://tg/response/calc",
|
||||||
"config-params": []
|
"config-params": []
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
@ -199,16 +201,18 @@ Requests and responses use untyped dicts. No schema validation at the agent leve
|
||||||
|
|
||||||
### Client Interface: Direct Pulsar Topics
|
### Client Interface: Direct Pulsar Topics
|
||||||
|
|
||||||
Tool services use direct Pulsar topics without requiring flow configuration:
|
Tool services use direct Pulsar topics without requiring flow configuration. The tool-service descriptor specifies the full queue names:
|
||||||
|
|
||||||
- **Request Topic**: `non-persistent://tg/request/{topic}`
|
```json
|
||||||
- **Response Topic**: `non-persistent://tg/response/{topic}`
|
{
|
||||||
|
"id": "joke-service",
|
||||||
|
"request-queue": "non-persistent://tg/request/joke",
|
||||||
|
"response-queue": "non-persistent://tg/response/joke",
|
||||||
|
"config-params": [...]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
Where `{topic}` comes from the tool-service descriptor. For example, a service with `"topic": "joke"` uses:
|
This allows services to be hosted in any namespace.
|
||||||
- `non-persistent://tg/request/joke`
|
|
||||||
- `non-persistent://tg/response/joke`
|
|
||||||
|
|
||||||
This avoids the complexity of flow blueprints and allows services to be deployed independently.
|
|
||||||
|
|
||||||
### Error Handling: Standard Error Convention
|
### Error Handling: Standard Error Convention
|
||||||
|
|
||||||
|
|
@ -295,8 +299,7 @@ class DynamicToolService(AsyncProcessor):
|
||||||
|
|
||||||
def __init__(self, **params):
|
def __init__(self, **params):
|
||||||
topic = params.get("topic", default_topic)
|
topic = params.get("topic", default_topic)
|
||||||
request_topic = f"non-persistent://tg/request/{topic}"
|
# Constructs topics: non-persistent://tg/request/{topic}, non-persistent://tg/response/{topic}
|
||||||
response_topic = f"non-persistent://tg/response/{topic}"
|
|
||||||
# Sets up Consumer and Producer
|
# Sets up Consumer and Producer
|
||||||
|
|
||||||
async def invoke(self, user, config, arguments):
|
async def invoke(self, user, config, arguments):
|
||||||
|
|
@ -310,8 +313,8 @@ Implementation in `trustgraph-flow/trustgraph/agent/react/tools.py`:
|
||||||
|
|
||||||
```python
|
```python
|
||||||
class ToolServiceImpl:
|
class ToolServiceImpl:
|
||||||
def __init__(self, context, service_topic, config_values, arguments, processor):
|
def __init__(self, context, request_queue, response_queue, config_values, arguments, processor):
|
||||||
# Constructs topic paths: non-persistent://tg/request/{topic}, non-persistent://tg/response/{topic}
|
# Uses the provided queue paths directly
|
||||||
# Creates ToolServiceClient on first use
|
# Creates ToolServiceClient on first use
|
||||||
|
|
||||||
async def invoke(self, **arguments):
|
async def invoke(self, **arguments):
|
||||||
|
|
@ -326,7 +329,7 @@ Two config sections:
|
||||||
|
|
||||||
```
|
```
|
||||||
tool-service/
|
tool-service/
|
||||||
joke-service: {"id": "joke-service", "topic": "joke", "config-params": [...]}
|
joke-service: {"id": "joke-service", "request-queue": "non-persistent://tg/request/joke", "response-queue": "non-persistent://tg/response/joke", "config-params": [...]}
|
||||||
|
|
||||||
tool/
|
tool/
|
||||||
tell-joke: {"type": "tool-service", "service": "joke-service", ...}
|
tell-joke: {"type": "tool-service", "service": "joke-service", ...}
|
||||||
|
|
@ -359,7 +362,8 @@ Tool service config:
|
||||||
```json
|
```json
|
||||||
{
|
{
|
||||||
"id": "joke-service",
|
"id": "joke-service",
|
||||||
"topic": "joke",
|
"request-queue": "non-persistent://tg/request/joke",
|
||||||
|
"response-queue": "non-persistent://tg/response/joke",
|
||||||
"config-params": [{"name": "style", "required": false}]
|
"config-params": [{"name": "style", "required": false}]
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
|
||||||
|
|
@ -203,10 +203,11 @@ class Processor(AgentService):
|
||||||
)
|
)
|
||||||
|
|
||||||
service_config = tool_services[service_ref]
|
service_config = tool_services[service_ref]
|
||||||
service_topic = service_config.get("topic")
|
request_queue = service_config.get("request-queue")
|
||||||
if not service_topic:
|
response_queue = service_config.get("response-queue")
|
||||||
|
if not request_queue or not response_queue:
|
||||||
raise RuntimeError(
|
raise RuntimeError(
|
||||||
f"Tool-service '{service_ref}' has no 'topic' defined"
|
f"Tool-service '{service_ref}' must define 'request-queue' and 'response-queue'"
|
||||||
)
|
)
|
||||||
|
|
||||||
# Build config values from tool config
|
# Build config values from tool config
|
||||||
|
|
@ -233,10 +234,11 @@ class Processor(AgentService):
|
||||||
for arg in config_args
|
for arg in config_args
|
||||||
]
|
]
|
||||||
|
|
||||||
# Store topic for the implementation
|
# Store queues for the implementation
|
||||||
impl = functools.partial(
|
impl = functools.partial(
|
||||||
ToolServiceImpl,
|
ToolServiceImpl,
|
||||||
service_topic=service_topic,
|
request_queue=request_queue,
|
||||||
|
response_queue=response_queue,
|
||||||
config_values=config_values,
|
config_values=config_values,
|
||||||
arguments=arguments,
|
arguments=arguments,
|
||||||
processor=self,
|
processor=self,
|
||||||
|
|
|
||||||
|
|
@ -210,23 +210,25 @@ class ToolServiceImpl:
|
||||||
Implementation for dynamically pluggable tool services.
|
Implementation for dynamically pluggable tool services.
|
||||||
|
|
||||||
Tool services are external Pulsar services that can be invoked as agent tools.
|
Tool services are external Pulsar services that can be invoked as agent tools.
|
||||||
The service is configured via a tool-service descriptor that defines the topic,
|
The service is configured via a tool-service descriptor that defines the queues,
|
||||||
and a tool descriptor that provides config values and argument definitions.
|
and a tool descriptor that provides config values and argument definitions.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self, context, service_topic, config_values=None, arguments=None, processor=None):
|
def __init__(self, context, request_queue, response_queue, config_values=None, arguments=None, processor=None):
|
||||||
"""
|
"""
|
||||||
Initialize a tool service implementation.
|
Initialize a tool service implementation.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
context: The context function (provides user info)
|
context: The context function (provides user info)
|
||||||
service_topic: The base topic name for the service
|
request_queue: Full Pulsar topic for requests
|
||||||
|
response_queue: Full Pulsar topic for responses
|
||||||
config_values: Dict of config values (e.g., {"collection": "customers"})
|
config_values: Dict of config values (e.g., {"collection": "customers"})
|
||||||
arguments: List of Argument objects defining the tool's parameters
|
arguments: List of Argument objects defining the tool's parameters
|
||||||
processor: The Processor instance (for pubsub access)
|
processor: The Processor instance (for pubsub access)
|
||||||
"""
|
"""
|
||||||
self.context = context
|
self.context = context
|
||||||
self.service_topic = service_topic
|
self.request_queue = request_queue
|
||||||
|
self.response_queue = response_queue
|
||||||
self.config_values = config_values or {}
|
self.config_values = config_values or {}
|
||||||
self.arguments = arguments or []
|
self.arguments = arguments or []
|
||||||
self.processor = processor
|
self.processor = processor
|
||||||
|
|
@ -240,9 +242,10 @@ class ToolServiceImpl:
|
||||||
if self._client is not None:
|
if self._client is not None:
|
||||||
return self._client
|
return self._client
|
||||||
|
|
||||||
# Check if processor already has a client for this topic
|
# Check if processor already has a client for this queue pair
|
||||||
if self.service_topic in self.processor.tool_service_clients:
|
client_key = f"{self.request_queue}|{self.response_queue}"
|
||||||
self._client = self.processor.tool_service_clients[self.service_topic]
|
if client_key in self.processor.tool_service_clients:
|
||||||
|
self._client = self.processor.tool_service_clients[client_key]
|
||||||
return self._client
|
return self._client
|
||||||
|
|
||||||
# Import here to avoid circular imports
|
# Import here to avoid circular imports
|
||||||
|
|
@ -251,40 +254,28 @@ class ToolServiceImpl:
|
||||||
from trustgraph.schema import ToolServiceRequest, ToolServiceResponse
|
from trustgraph.schema import ToolServiceRequest, ToolServiceResponse
|
||||||
import uuid
|
import uuid
|
||||||
|
|
||||||
# Construct full topic paths using non-persistent topics
|
|
||||||
# If topic already contains "://", assume it's a full Pulsar URI
|
|
||||||
# Otherwise, construct default path
|
|
||||||
if '://' in self.service_topic:
|
|
||||||
# Full Pulsar URI provided - use as base for request/response
|
|
||||||
request_topic = self.service_topic.replace("://tg/", "://tg/request/")
|
|
||||||
response_topic = self.service_topic.replace("://tg/", "://tg/response/")
|
|
||||||
else:
|
|
||||||
# Construct default path matching DynamicToolService pattern
|
|
||||||
request_topic = f"non-persistent://tg/request/{self.service_topic}"
|
|
||||||
response_topic = f"non-persistent://tg/response/{self.service_topic}"
|
|
||||||
|
|
||||||
request_metrics = ProducerMetrics(
|
request_metrics = ProducerMetrics(
|
||||||
processor=self.processor.id,
|
processor=self.processor.id,
|
||||||
flow="tool-service",
|
flow="tool-service",
|
||||||
name=request_topic
|
name=self.request_queue
|
||||||
)
|
)
|
||||||
response_metrics = SubscriberMetrics(
|
response_metrics = SubscriberMetrics(
|
||||||
processor=self.processor.id,
|
processor=self.processor.id,
|
||||||
flow="tool-service",
|
flow="tool-service",
|
||||||
name=response_topic
|
name=self.response_queue
|
||||||
)
|
)
|
||||||
|
|
||||||
# Create unique subscription for responses
|
# Create unique subscription for responses
|
||||||
subscription = f"{self.processor.id}--tool-service--{self.service_topic}--{uuid.uuid4()}"
|
subscription = f"{self.processor.id}--tool-service--{uuid.uuid4()}"
|
||||||
|
|
||||||
self._client = ToolServiceClient(
|
self._client = ToolServiceClient(
|
||||||
backend=self.processor.pubsub,
|
backend=self.processor.pubsub,
|
||||||
subscription=subscription,
|
subscription=subscription,
|
||||||
consumer_name=self.processor.id,
|
consumer_name=self.processor.id,
|
||||||
request_topic=request_topic,
|
request_topic=self.request_queue,
|
||||||
request_schema=ToolServiceRequest,
|
request_schema=ToolServiceRequest,
|
||||||
request_metrics=request_metrics,
|
request_metrics=request_metrics,
|
||||||
response_topic=response_topic,
|
response_topic=self.response_queue,
|
||||||
response_schema=ToolServiceResponse,
|
response_schema=ToolServiceResponse,
|
||||||
response_metrics=response_metrics,
|
response_metrics=response_metrics,
|
||||||
)
|
)
|
||||||
|
|
@ -293,13 +284,13 @@ class ToolServiceImpl:
|
||||||
await self._client.start()
|
await self._client.start()
|
||||||
|
|
||||||
# Register for cleanup
|
# Register for cleanup
|
||||||
self.processor.tool_service_clients[self.service_topic] = self._client
|
self.processor.tool_service_clients[client_key] = self._client
|
||||||
|
|
||||||
logger.debug(f"Created tool service client for {self.service_topic}")
|
logger.debug(f"Created tool service client for {self.request_queue}")
|
||||||
return self._client
|
return self._client
|
||||||
|
|
||||||
async def invoke(self, **arguments):
|
async def invoke(self, **arguments):
|
||||||
logger.debug(f"Tool service invocation: {self.service_topic}...")
|
logger.debug(f"Tool service invocation: {self.request_queue}...")
|
||||||
logger.debug(f"Config: {self.config_values}")
|
logger.debug(f"Config: {self.config_values}")
|
||||||
logger.debug(f"Arguments: {arguments}")
|
logger.debug(f"Arguments: {arguments}")
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue