fix: address source provider review notes
This commit is contained in:
@@ -73,12 +73,13 @@ Add a `kind: "python"` entry under `server.sources[]`:
|
||||
}
|
||||
```
|
||||
|
||||
`path` is resolved relative to the config file. It is important for console
|
||||
scripts: `uv run python` and installed entrypoints do not always have the same
|
||||
import path.
|
||||
`path` is resolved relative to the config file and added to `sys.path` before
|
||||
import. This makes the module discoverable whether you run through
|
||||
`uv run python` or an installed entrypoint.
|
||||
|
||||
The source id prefixes local names. A node named `echo` becomes
|
||||
`local.ops.echo`. A node named `authoring.echo` is also exposed as
|
||||
`local.ops.echo`. If a node uses the authoring namespace, such as
|
||||
`authoring.echo`, that authoring prefix is replaced by the source id, producing
|
||||
`local.ops.echo`.
|
||||
|
||||
## 3. Validate And Start
|
||||
|
||||
@@ -214,6 +214,8 @@ class WorkflowRunApi:
|
||||
allowed = ", ".join(item.value for item in StoredRunStatus)
|
||||
raise ValueError(f"status must be one of: {allowed}") from exc
|
||||
|
||||
# File-backed v1 stores keep run listing simple by filtering/sorting in
|
||||
# memory. Move this into store-level pagination if run counts grow large.
|
||||
records = self._run_store().list_runs()
|
||||
if status_filter is not None:
|
||||
records = [record for record in records if record.status == status_filter]
|
||||
|
||||
@@ -27,7 +27,7 @@ def list_runs(
|
||||
str | None,
|
||||
typer.Option(
|
||||
"--status",
|
||||
help="Filter by stopped status: completed, failed, or interrupted.",
|
||||
help="Filter by stopped status: completed, failed, interrupted, or blocked.",
|
||||
),
|
||||
] = None,
|
||||
cursor: Annotated[
|
||||
|
||||
@@ -14,11 +14,12 @@ from .models import (
|
||||
|
||||
|
||||
def load_workflow_config(path: str | Path) -> WorkflowConfigFile:
|
||||
"""Load neutral workflow config and resolve local filesystem paths.
|
||||
"""Load neutral workflow config and resolve local filesystem/source paths.
|
||||
|
||||
Relative filesystem store roots are config-file relative so `wf --config`
|
||||
behaves the same regardless of the caller's current working directory.
|
||||
Role-specific store overrides follow the same rule.
|
||||
Role-specific store overrides and Python source import paths follow the
|
||||
same rule.
|
||||
"""
|
||||
|
||||
config_path = Path(path)
|
||||
|
||||
@@ -8,6 +8,8 @@ from wf_core import ReducerSpec
|
||||
from wf_core.runtime.ops.merges import ReducerDefinition
|
||||
from wf_platform.refs import CapabilityRef
|
||||
|
||||
# Source kind is source-origin metadata, not a provider interface:
|
||||
# system=built-ins, connection=upstream/stateful providers, python=trusted local code.
|
||||
SourceKind = Literal["system", "connection", "python"]
|
||||
JsonObject = dict[str, Any]
|
||||
SOURCE_PREVIEW_LIMIT = 3
|
||||
|
||||
@@ -35,8 +35,12 @@ def collect_static_sources(
|
||||
collected: dict[str, CapabilitySource] = {}
|
||||
for provider in providers:
|
||||
for source_id, source in provider.load_sources().items():
|
||||
if source.id != source_id:
|
||||
raise ValueError(
|
||||
f"provider source key {source_id!r} does not match source id {source.id!r}"
|
||||
)
|
||||
if source_id in collected:
|
||||
raise ValueError(f"duplicate workflow source ids: {[source_id]}")
|
||||
raise ValueError(f"duplicate workflow source ids: {source_id}")
|
||||
collected[source_id] = source
|
||||
return collected
|
||||
|
||||
|
||||
@@ -103,9 +103,7 @@ async def test_call_openapi_operation_maps_unexpected_status() -> None:
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_call_openapi_operation_maps_invalid_request_to_validation_error() -> (
|
||||
None
|
||||
):
|
||||
async def test_call_openapi_operation_maps_invalid_request_to_validation_error() -> None:
|
||||
app = load_openapi_app(FIXTURE)
|
||||
operation = next(
|
||||
op for op in load_openapi_operations(FIXTURE) if op.name == "create_pet"
|
||||
|
||||
@@ -15,9 +15,9 @@ from .conftest import structured
|
||||
|
||||
|
||||
def test_proxy_admin_tools_mutate_config_file(tmp_path: Path) -> None:
|
||||
tmp_path = tmp_path / "proxy_admin_store"
|
||||
tmp_path.mkdir(parents=True, exist_ok=True)
|
||||
config_path = tmp_path / "wf_mcp.config.json"
|
||||
test_root = tmp_path / "proxy_admin_store"
|
||||
test_root.mkdir(parents=True, exist_ok=True)
|
||||
config_path = test_root / "wf_mcp.config.json"
|
||||
config_path.write_text(
|
||||
json.dumps(
|
||||
{
|
||||
@@ -91,9 +91,9 @@ def test_proxy_admin_tools_mutate_config_file(tmp_path: Path) -> None:
|
||||
|
||||
|
||||
def test_proxy_admin_reload_remounts_connections(tmp_path: Path) -> None:
|
||||
tmp_path = tmp_path / "proxy_reload_store"
|
||||
tmp_path.mkdir(parents=True, exist_ok=True)
|
||||
config_path = tmp_path / "wf_mcp.config.json"
|
||||
test_root = tmp_path / "proxy_reload_store"
|
||||
test_root.mkdir(parents=True, exist_ok=True)
|
||||
config_path = test_root / "wf_mcp.config.json"
|
||||
config_path.write_text(
|
||||
json.dumps(
|
||||
{
|
||||
@@ -132,9 +132,9 @@ def test_proxy_admin_reload_remounts_connections(tmp_path: Path) -> None:
|
||||
|
||||
|
||||
def test_proxy_admin_reload_sends_list_changed_notifications(tmp_path: Path) -> None:
|
||||
tmp_path = tmp_path / "proxy_reload_notification_store"
|
||||
tmp_path.mkdir(parents=True, exist_ok=True)
|
||||
config_path = tmp_path / "wf_mcp.config.json"
|
||||
test_root = tmp_path / "proxy_reload_notification_store"
|
||||
test_root.mkdir(parents=True, exist_ok=True)
|
||||
config_path = test_root / "wf_mcp.config.json"
|
||||
config_path.write_text(
|
||||
json.dumps(
|
||||
{
|
||||
@@ -166,9 +166,9 @@ def test_proxy_admin_reload_sends_list_changed_notifications(tmp_path: Path) ->
|
||||
|
||||
|
||||
def test_proxy_config_mutation_does_not_notify_before_reload(tmp_path: Path) -> None:
|
||||
tmp_path = tmp_path / "proxy_staged_notification_store"
|
||||
tmp_path.mkdir(parents=True, exist_ok=True)
|
||||
config_path = tmp_path / "wf_mcp.config.json"
|
||||
test_root = tmp_path / "proxy_staged_notification_store"
|
||||
test_root.mkdir(parents=True, exist_ok=True)
|
||||
config_path = test_root / "wf_mcp.config.json"
|
||||
config_path.write_text(
|
||||
json.dumps(
|
||||
{
|
||||
|
||||
@@ -73,6 +73,21 @@ def test_static_source_provider_rejects_duplicate_source_ids() -> None:
|
||||
collect_static_sources([provider, FakeSourceProvider()])
|
||||
|
||||
|
||||
def test_static_source_provider_rejects_source_key_mismatch() -> None:
|
||||
provider = StaticSourceProvider(
|
||||
{
|
||||
"fake.alias": CapabilitySource(
|
||||
id="fake.ops",
|
||||
kind="python",
|
||||
capabilities=CapabilityBuckets(),
|
||||
)
|
||||
}
|
||||
)
|
||||
|
||||
with pytest.raises(ValueError, match="does not match source id"):
|
||||
collect_static_sources([provider])
|
||||
|
||||
|
||||
def test_build_workflow_server_from_workflow_config_uses_local_static_for_no_mcp_sources(
|
||||
tmp_path: Path,
|
||||
) -> None:
|
||||
|
||||
Reference in New Issue
Block a user