read_resource to also read capability sources
This commit is contained in:
@@ -74,10 +74,7 @@ def _add_step(builder: WorkflowBuilder, step_id: str, step: DraftStep):
|
||||
).entry
|
||||
if isinstance(step, DraftChooseStep):
|
||||
return builder.choose(
|
||||
*[
|
||||
(clause.if_, clause.then)
|
||||
for clause in step.choose.clauses
|
||||
],
|
||||
*[(clause.if_, clause.then) for clause in step.choose.clauses],
|
||||
id=step_id,
|
||||
default=step.choose.default,
|
||||
).entry
|
||||
|
||||
@@ -101,7 +101,10 @@ def register_service_admin_tools(
|
||||
@server.tool(
|
||||
name=name("read_resource"),
|
||||
title="Read Resource",
|
||||
description="Read a broker-catalog resource by qualified name.",
|
||||
description=(
|
||||
"Read a local docs or broker-catalog resource by qualified name, "
|
||||
"for example wf.docs.workflow_capabilities."
|
||||
),
|
||||
)
|
||||
async def read_resource(qualified_name: str) -> dict[str, Any]:
|
||||
return await handlers.read_broker_resource(qualified_name)
|
||||
@@ -109,7 +112,10 @@ def register_service_admin_tools(
|
||||
@server.tool(
|
||||
name=name("render_prompt"),
|
||||
title="Render Prompt",
|
||||
description="Render a broker-catalog prompt by qualified name.",
|
||||
description=(
|
||||
"Render a local docs or broker-catalog prompt by qualified name, "
|
||||
"for example wf.docs.workflow_authoring_guide."
|
||||
),
|
||||
)
|
||||
async def render_prompt(
|
||||
qualified_name: str,
|
||||
|
||||
@@ -23,6 +23,8 @@ from wf_core import NodeUse, Workflow, execute_workflow_async
|
||||
from wf_platform import (
|
||||
CapabilityBuckets,
|
||||
CapabilitySource,
|
||||
DocumentationPrompt,
|
||||
DocumentationResource,
|
||||
SourcePermissions,
|
||||
SourceVisibility,
|
||||
page_items,
|
||||
@@ -363,6 +365,25 @@ class WfMcpService:
|
||||
return entry
|
||||
|
||||
async def read_resource(self, qualified_name: str) -> dict[str, Any]:
|
||||
local_resource = self._local_documentation_resource(qualified_name)
|
||||
if local_resource is not None:
|
||||
self._record_event(
|
||||
make_event(
|
||||
"resource_read_completed",
|
||||
capability_id=qualified_name,
|
||||
payload={"uri": local_resource.uri, "source": "local"},
|
||||
)
|
||||
)
|
||||
return {
|
||||
"contents": [
|
||||
{
|
||||
"uri": local_resource.uri,
|
||||
"mimeType": local_resource.mime_type,
|
||||
"text": local_resource.text,
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
resource = self.get_resource(qualified_name)
|
||||
connection = self.connections.get(resource.connection_id)
|
||||
adapter = require_adapter(connection, self.adapters)
|
||||
@@ -484,6 +505,31 @@ class WfMcpService:
|
||||
*,
|
||||
arguments: dict[str, str] | None = None,
|
||||
) -> dict[str, Any]:
|
||||
local_prompt = self._local_documentation_prompt(qualified_name)
|
||||
if local_prompt is not None:
|
||||
self._record_event(
|
||||
make_event(
|
||||
"prompt_get_completed",
|
||||
capability_id=qualified_name,
|
||||
payload={
|
||||
"argument_keys": sorted((arguments or {}).keys()),
|
||||
"source": "local",
|
||||
},
|
||||
)
|
||||
)
|
||||
return {
|
||||
"description": local_prompt.description,
|
||||
"messages": [
|
||||
{
|
||||
"role": "user",
|
||||
"content": {
|
||||
"type": "text",
|
||||
"text": local_prompt.text,
|
||||
},
|
||||
}
|
||||
],
|
||||
}
|
||||
|
||||
prompt = self.get_prompt(qualified_name)
|
||||
connection = self.connections.get(prompt.connection_id)
|
||||
adapter = require_adapter(connection, self.adapters)
|
||||
@@ -512,6 +558,28 @@ class WfMcpService:
|
||||
)
|
||||
return result
|
||||
|
||||
def _local_documentation_resource(
|
||||
self,
|
||||
qualified_name: str,
|
||||
) -> DocumentationResource | None:
|
||||
"""Return a local docs resource from capability sources by qualified name."""
|
||||
for source in self.capability_sources.values():
|
||||
resource = source.capabilities.resources.get(qualified_name)
|
||||
if isinstance(resource, DocumentationResource):
|
||||
return resource
|
||||
return None
|
||||
|
||||
def _local_documentation_prompt(
|
||||
self,
|
||||
qualified_name: str,
|
||||
) -> DocumentationPrompt | None:
|
||||
"""Return a local docs prompt from capability sources by qualified name."""
|
||||
for source in self.capability_sources.values():
|
||||
prompt = source.capabilities.prompts.get(qualified_name)
|
||||
if isinstance(prompt, DocumentationPrompt):
|
||||
return prompt
|
||||
return None
|
||||
|
||||
async def refresh_connection_catalog(
|
||||
self,
|
||||
connection_id: str,
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
from dataclasses import dataclass, field
|
||||
from typing import Any, Literal
|
||||
from typing import Any, Literal, dataclass_transform
|
||||
|
||||
from pydantic import BaseModel
|
||||
|
||||
|
||||
@@ -450,6 +450,43 @@ def test_server_exposes_platform_documentation_prompts() -> None:
|
||||
asyncio.run(run_proxy())
|
||||
|
||||
|
||||
def test_admin_tools_can_read_local_documentation_source() -> None:
|
||||
config = BrokerConfig(
|
||||
store_root=local_temp_root() / "unified_admin_docs_store",
|
||||
connections=[],
|
||||
)
|
||||
|
||||
async def run_proxy() -> None:
|
||||
client = create_server_client(
|
||||
config,
|
||||
resources_as_tools=True,
|
||||
prompts_as_tools=True,
|
||||
safe_tool_names=True,
|
||||
)
|
||||
async with client:
|
||||
resource = await client.call_tool(
|
||||
"wf_admin_read_resource",
|
||||
{"qualified_name": "wf.docs.workflow_capabilities"},
|
||||
)
|
||||
prompt = await client.call_tool(
|
||||
"wf_admin_render_prompt",
|
||||
{"qualified_name": "wf.docs.workflow_authoring_guide"},
|
||||
)
|
||||
|
||||
resource_payload = _structured(resource)
|
||||
prompt_payload = _structured(prompt)
|
||||
assert resource_payload["contents"][0]["uri"] == (
|
||||
"wf://docs/workflow-capabilities"
|
||||
)
|
||||
assert "# Workflow Capabilities" in resource_payload["contents"][0]["text"]
|
||||
assert (
|
||||
"wf://docs/workflow-capabilities"
|
||||
in (prompt_payload["messages"][0]["content"]["text"])
|
||||
)
|
||||
|
||||
asyncio.run(run_proxy())
|
||||
|
||||
|
||||
def test_server_reload_syncs_service_connection_source_enabled_state() -> None:
|
||||
tmp_path = local_temp_root() / "unified_reload_service_source_store"
|
||||
tmp_path.mkdir(parents=True, exist_ok=True)
|
||||
|
||||
Reference in New Issue
Block a user