refactor: group rpc transport client and methods

This commit is contained in:
lda
2026-06-08 21:44:41 +07:00 Verified
parent 7582c1d6fb
commit c39d361873
25 changed files with 104 additions and 114 deletions
+17 -2
View File
@@ -113,7 +113,7 @@ def load_cli_context(
if rpc_url is not None:
_validate_rpc_url(rpc_url)
client = RpcWorkflowApiClient(
client = rpc_client_from_target(
url=rpc_url,
timeout_seconds=_rpc_timeout_from_optional_config(
resolved_config_path,
@@ -161,7 +161,7 @@ def load_cli_context(
verbose=verbose,
)
if isinstance(target, RpcHttpTargetConfig):
client = RpcWorkflowApiClient(
client = rpc_client_from_target(
url=str(target.url),
timeout_seconds=(
rpc_timeout_seconds
@@ -181,6 +181,21 @@ def load_cli_context(
raise ValueError(f"unsupported workflow target {target!r}")
def rpc_client_from_target(
*,
url: str,
timeout_seconds: float,
) -> RpcWorkflowApiClient:
"""Build the remote workflow surface for a resolved RPC target.
CLI tests patch this project-owned seam instead of monkeypatching `httpx`
internals. The production path still keeps HTTP construction inside the
transport package.
"""
return RpcWorkflowApiClient(url=url, timeout_seconds=timeout_seconds)
def load_local_cli_context(
config_path: str | Path,
*,
+1 -4
View File
@@ -5,9 +5,6 @@ Canonical implementation lives in `wf_sources_mcp.tool_wrappers`.
from __future__ import annotations
from wf_sources_mcp.schema_models import model_from_schema
from wf_sources_mcp.tool_wrappers import wrap_discovered_tool
_model_from_schema = model_from_schema # TODO: remove when callers migrate
__all__ = ["_model_from_schema", "wrap_discovered_tool"]
__all__ = ["wrap_discovered_tool"]
+10 -8
View File
@@ -7,16 +7,18 @@ import fastapi_jsonrpc as jsonrpc
from wf_server import WorkflowServer
from .errors import WorkflowRpcError
from .methods_admin import register_methods as register_admin_methods
from .methods_artifacts import register_methods as register_artifact_methods
from .methods_capabilities import register_methods as register_capability_methods
from .methods_deployments import register_methods as register_deployment_methods
from .methods_drafts import register_methods as register_draft_methods
from .methods_runs import register_methods as register_run_methods
from .methods_source_registry import (
from .methods.admin import register_methods as register_admin_methods
from .methods.artifacts import register_methods as register_artifact_methods
from .methods.capabilities import (
register_methods as register_capability_methods,
)
from .methods.deployments import register_methods as register_deployment_methods
from .methods.drafts import register_methods as register_draft_methods
from .methods.runs import register_methods as register_run_methods
from .methods.source_registry import (
register_methods as register_source_registry_methods,
)
from .methods_sources import register_methods as register_source_methods
from .methods.sources import register_methods as register_source_methods
def create_rpc_app(server: WorkflowServer, *, rpc_path: str = "/rpc") -> jsonrpc.API:
@@ -2,17 +2,15 @@ from __future__ import annotations
from dataclasses import dataclass
import httpx # noqa: F401 # Backcompat for tests patching client.httpx.AsyncClient.
from .client_admin import RpcAdminClientMixin
from .client_artifacts import RpcArtifactClientMixin
from .client_base import RpcClientTransport
from .client_capabilities import RpcCapabilityClientMixin
from .client_deployments import RpcDeploymentClientMixin
from .client_drafts import RpcDraftClientMixin
from .client_runs import RpcRunClientMixin
from .client_source_registry import RpcSourceRegistryClientMixin
from .client_sources import RpcSourceAdminClientMixin
from .admin import RpcAdminClientMixin
from .artifacts import RpcArtifactClientMixin
from .base import RpcClientTransport
from .capabilities import RpcCapabilityClientMixin
from .deployments import RpcDeploymentClientMixin
from .drafts import RpcDraftClientMixin
from .runs import RpcRunClientMixin
from .source_registry import RpcSourceRegistryClientMixin
from .sources import RpcSourceAdminClientMixin
@dataclass(slots=True)
@@ -6,14 +6,14 @@ import fastapi_jsonrpc as jsonrpc
from wf_server import WorkflowServer
from .errors import WorkflowRpcError, raise_workflow_rpc_error
from .models import (
from ..errors import WorkflowRpcError, raise_workflow_rpc_error
from ..models import (
AdminEmptyParams,
DeleteAuthParams,
InspectAuthParams,
SaveAuthParams,
)
from .params import RpcParams
from ..params import RpcParams
def register_methods(
@@ -6,9 +6,9 @@ import fastapi_jsonrpc as jsonrpc
from wf_server import WorkflowServer
from .errors import WorkflowRpcError, raise_workflow_rpc_error
from .models import InspectArtifactParams, ListArtifactsParams, SaveArtifactParams
from .params import RpcParams
from ..errors import WorkflowRpcError, raise_workflow_rpc_error
from ..models import InspectArtifactParams, ListArtifactsParams, SaveArtifactParams
from ..params import RpcParams
def register_methods(
@@ -6,9 +6,9 @@ import fastapi_jsonrpc as jsonrpc
from wf_server import WorkflowServer
from .errors import WorkflowRpcError, raise_workflow_rpc_error
from .models import InspectCapabilityParams, ListCapabilitiesParams
from .params import RpcParams
from ..errors import WorkflowRpcError, raise_workflow_rpc_error
from ..models import InspectCapabilityParams, ListCapabilitiesParams
from ..params import RpcParams
def register_methods(
@@ -6,15 +6,15 @@ import fastapi_jsonrpc as jsonrpc
from wf_server import WorkflowServer
from .errors import WorkflowRpcError, raise_workflow_rpc_error
from .models import (
from ..errors import WorkflowRpcError, raise_workflow_rpc_error
from ..models import (
DeleteDeploymentParams,
InspectDeploymentParams,
ListDeploymentsParams,
SaveDeploymentParams,
ValidateDeploymentParams,
)
from .params import RpcParams
from ..params import RpcParams
def register_methods(
@@ -6,8 +6,8 @@ import fastapi_jsonrpc as jsonrpc
from wf_server import WorkflowServer
from .errors import WorkflowRpcError, raise_workflow_rpc_error
from .models import (
from ..errors import WorkflowRpcError, raise_workflow_rpc_error
from ..models import (
CreateArtifactFromWorkspaceParams,
CreateDraftFromCapabilityParams,
CreateWrapperFromWorkspaceParams,
@@ -18,7 +18,7 @@ from .models import (
ValidateDraftParams,
ValidateDraftWorkspaceParams,
)
from .params import RpcParams
from ..params import RpcParams
def register_methods(
@@ -6,14 +6,14 @@ import fastapi_jsonrpc as jsonrpc
from wf_server import WorkflowServer
from .errors import WorkflowRpcError, raise_workflow_rpc_error
from .models import (
from ..errors import WorkflowRpcError, raise_workflow_rpc_error
from ..models import (
InspectRunParams,
ReadRunTraceParams,
ResumeRunParams,
StartRunParams,
)
from .params import RpcParams
from ..params import RpcParams
def register_methods(
@@ -7,8 +7,8 @@ import fastapi_jsonrpc as jsonrpc
from wf_api import WorkflowSourceRegistrySurface
from wf_server import WorkflowServer
from .errors import WorkflowRpcError, raise_workflow_rpc_error
from .models import (
from ..errors import WorkflowRpcError, raise_workflow_rpc_error
from ..models import (
AddRegistryEntryParams,
ApplyRegistryChangesParams,
InspectRegistryEntryParams,
@@ -16,7 +16,7 @@ from .models import (
RegistryEntryIdParams,
UpdateRegistryEntryParams,
)
from .params import RpcParams
from ..params import RpcParams
def _require_source_registry_admin(
@@ -6,9 +6,9 @@ import fastapi_jsonrpc as jsonrpc
from wf_server import WorkflowServer
from .errors import WorkflowRpcError, raise_workflow_rpc_error
from .models import InspectSourceParams, ListSourcesParams
from .params import RpcParams
from ..errors import WorkflowRpcError, raise_workflow_rpc_error
from ..models import InspectSourceParams, ListSourcesParams
from ..params import RpcParams
def register_methods(
+29 -52
View File
@@ -8,12 +8,14 @@ from typing import Any, cast
import httpx
from typer.testing import CliRunner
import wf_cli.context as cli_context
from wf_api.models import RawWorkflowPlan
from wf_cli.app import app
from wf_cli.context import CliContext, load_cli_context, load_local_cli_context
from wf_core import END
from wf_server import build_local_static_workflow_server
from wf_transport_rpc_http import RpcWorkflowApiClient, create_rpc_app
from wf_transport_rpc_http.client.sources import RpcSourceAdminClientMixin
class BrokenSourceAdmin:
@@ -271,16 +273,29 @@ def _interrupt_plan() -> RawWorkflowPlan:
)
def _patch_rpc_client_to_server(monkeypatch, server) -> None:
"""Route CLI-created RPC clients to an in-process ASGI test server."""
def fake_rpc_client_from_target(
*,
url: str,
timeout_seconds: float,
) -> RpcWorkflowApiClient:
return RpcWorkflowApiClient(
url=url,
timeout_seconds=timeout_seconds,
http_client=httpx.AsyncClient(
transport=httpx.ASGITransport(app=create_rpc_app(server)),
base_url="http://test",
),
)
monkeypatch.setattr(cli_context, "rpc_client_from_target", fake_rpc_client_from_target)
def test_wf_cap_commands_use_rpc_url_override(monkeypatch, tmp_path) -> None:
server = build_local_static_workflow_server(tmp_path / "store")
original_client = httpx.AsyncClient
monkeypatch.setattr(
"wf_transport_rpc_http.client.httpx.AsyncClient",
lambda *args, **kwargs: original_client(
transport=httpx.ASGITransport(app=create_rpc_app(server)),
base_url="http://test",
),
)
_patch_rpc_client_to_server(monkeypatch, server)
config_path = tmp_path / "wf.json"
config_path.write_text('{"version": 1}', encoding="utf-8")
@@ -325,14 +340,7 @@ def test_wf_cap_commands_use_rpc_url_override(monkeypatch, tmp_path) -> None:
def test_wf_source_commands_use_rpc_url_override(monkeypatch, tmp_path) -> None:
server = build_local_static_workflow_server(tmp_path / "store")
original_client = httpx.AsyncClient
monkeypatch.setattr(
"wf_transport_rpc_http.client.httpx.AsyncClient",
lambda *args, **kwargs: original_client(
transport=httpx.ASGITransport(app=create_rpc_app(server)),
base_url="http://test",
),
)
_patch_rpc_client_to_server(monkeypatch, server)
config_path = tmp_path / "wf.json"
config_path.write_text('{"version": 1}', encoding="utf-8")
runner = CliRunner()
@@ -352,14 +360,7 @@ def test_wf_remote_source_inspect_formats_expected_rpc_error(
tmp_path,
) -> None:
server = build_local_static_workflow_server(tmp_path / "store")
original_client = httpx.AsyncClient
monkeypatch.setattr(
"wf_transport_rpc_http.client.httpx.AsyncClient",
lambda *args, **kwargs: original_client(
transport=httpx.ASGITransport(app=create_rpc_app(server)),
base_url="http://test",
),
)
_patch_rpc_client_to_server(monkeypatch, server)
config_path = tmp_path / "wf.json"
config_path.write_text('{"version": 1}', encoding="utf-8")
runner = CliRunner()
@@ -382,10 +383,7 @@ def test_wf_remote_source_list_formats_transport_error(monkeypatch, tmp_path) ->
request=httpx.Request("POST", "http://test/rpc"),
)
monkeypatch.setattr(
"wf_transport_rpc_http.client_sources.RpcSourceAdminClientMixin.list_sources",
connection_failed,
)
monkeypatch.setattr(RpcSourceAdminClientMixin, "list_sources", connection_failed)
config_path = tmp_path / "wf.json"
config_path.write_text('{"version": 1}', encoding="utf-8")
runner = CliRunner()
@@ -463,14 +461,7 @@ def test_wf_admin_commands_use_rpc_url_override(monkeypatch, tmp_path) -> None:
capability_id="workflow.demo.v1",
payload={"ok": True},
)
original_client = httpx.AsyncClient
monkeypatch.setattr(
"wf_transport_rpc_http.client.httpx.AsyncClient",
lambda *args, **kwargs: original_client(
transport=httpx.ASGITransport(app=create_rpc_app(server)),
base_url="http://test",
),
)
_patch_rpc_client_to_server(monkeypatch, server)
config_path = tmp_path / "wf.json"
config_path.write_text('{"version": 1}', encoding="utf-8")
runner = CliRunner()
@@ -490,14 +481,7 @@ def test_wf_admin_commands_use_rpc_url_override(monkeypatch, tmp_path) -> None:
def test_wf_remote_draft_artifact_deploy_lifecycle(monkeypatch, tmp_path) -> None:
server = build_local_static_workflow_server(tmp_path / "store")
original_client = httpx.AsyncClient
monkeypatch.setattr(
"wf_transport_rpc_http.client.httpx.AsyncClient",
lambda *args, **kwargs: original_client(
transport=httpx.ASGITransport(app=create_rpc_app(server)),
base_url="http://test",
),
)
_patch_rpc_client_to_server(monkeypatch, server)
config_path = tmp_path / "wf.json"
config_path.write_text('{"version": 1}', encoding="utf-8")
runner = CliRunner()
@@ -603,14 +587,7 @@ def test_wf_remote_run_resume_interrupted_deployment(monkeypatch, tmp_path) -> N
}
)
)
original_client = httpx.AsyncClient
monkeypatch.setattr(
"wf_transport_rpc_http.client.httpx.AsyncClient",
lambda *args, **kwargs: original_client(
transport=httpx.ASGITransport(app=create_rpc_app(server)),
base_url="http://test",
),
)
_patch_rpc_client_to_server(monkeypatch, server)
config_path = tmp_path / "wf.json"
config_path.write_text('{"version": 1}', encoding="utf-8")
runner = CliRunner()
@@ -8,7 +8,7 @@ import httpx
from wf_api import WorkflowSourceRegistryApi
from wf_server import build_local_static_workflow_server
from wf_transport_rpc_http import RpcWorkflowApiClient, create_rpc_app
from wf_transport_rpc_http.client_source_registry import RpcSourceRegistryClientMixin
from wf_transport_rpc_http.client.source_registry import RpcSourceRegistryClientMixin
@dataclass(frozen=True, slots=True)
+13 -12
View File
@@ -1,19 +1,21 @@
from __future__ import annotations
import importlib
from pathlib import Path
import inspect
from wf_transport_rpc_http.client import RpcWorkflowApiClient
def test_rpc_transport_has_domain_method_modules() -> None:
for module_name in (
"wf_transport_rpc_http.methods_admin",
"wf_transport_rpc_http.methods_capabilities",
"wf_transport_rpc_http.methods_drafts",
"wf_transport_rpc_http.methods_artifacts",
"wf_transport_rpc_http.methods_deployments",
"wf_transport_rpc_http.methods_runs",
"wf_transport_rpc_http.methods_sources",
"wf_transport_rpc_http.methods_source_registry",
"wf_transport_rpc_http.methods.admin",
"wf_transport_rpc_http.methods.capabilities",
"wf_transport_rpc_http.methods.drafts",
"wf_transport_rpc_http.methods.artifacts",
"wf_transport_rpc_http.methods.deployments",
"wf_transport_rpc_http.methods.runs",
"wf_transport_rpc_http.methods.sources",
"wf_transport_rpc_http.methods.source_registry",
):
module = importlib.import_module(module_name)
@@ -21,7 +23,6 @@ def test_rpc_transport_has_domain_method_modules() -> None:
def test_rpc_transport_client_stays_thin() -> None:
client_path = Path("src/wf_transport_rpc_http/client.py")
line_count = len(client_path.read_text(encoding="utf-8").splitlines())
line_count = len(inspect.getsource(RpcWorkflowApiClient).splitlines())
assert line_count < 140
assert line_count < 40