fix: make cap unwrap text imply text output
This commit is contained in:
+3
-3
@@ -182,12 +182,12 @@ MCP content-block envelopes:
|
|||||||
wf cap call wf.std.constant --input '{"value": "hello"}' --format compact
|
wf cap call wf.std.constant --input '{"value": "hello"}' --format compact
|
||||||
```
|
```
|
||||||
|
|
||||||
Use `--format text --unwrap-text` to extract exactly one MCP text content block.
|
Use `--unwrap-text` to extract exactly one MCP text content block. This implies
|
||||||
This mode refuses images, resources, blobs, multiple content blocks, and
|
text output and refuses images, resources, blobs, multiple content blocks, and
|
||||||
non-MCP output:
|
non-MCP output:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
wf cap call everything.default.echo --input '{"message": "hello"}' --format text --unwrap-text
|
wf cap call everything.default.echo --input '{"message": "hello"}' --unwrap-text
|
||||||
```
|
```
|
||||||
|
|
||||||
Use `--max-output-chars N` to bound compact/text terminal output. JSON output
|
Use `--max-output-chars N` to bound compact/text terminal output. JSON output
|
||||||
|
|||||||
@@ -12,6 +12,18 @@ from wf_cli.formats import ListOutputFormat, emit_list_payload
|
|||||||
from wf_cli.io import CliInputError, emit_json, parse_json_input
|
from wf_cli.io import CliInputError, emit_json, parse_json_input
|
||||||
from wf_cli.remote_errors import run_cli_operation
|
from wf_cli.remote_errors import run_cli_operation
|
||||||
|
|
||||||
|
CAP_CALL_HELP = """Call one workflow capability once for authoring/runtime smoke tests.
|
||||||
|
|
||||||
|
Output modes:
|
||||||
|
|
||||||
|
- json: Full lossless payload. Default. Never truncated.
|
||||||
|
- compact: One bounded summary line.
|
||||||
|
- text: Requires --unwrap-text and prints exactly one MCP text content block.
|
||||||
|
|
||||||
|
Text unwrap refuses images, resources, blobs, multiple blocks, and non-MCP
|
||||||
|
output. Use --max-output-chars to bound compact/text terminal output.
|
||||||
|
"""
|
||||||
|
|
||||||
|
|
||||||
class CapCallOutputFormat(StrEnum):
|
class CapCallOutputFormat(StrEnum):
|
||||||
JSON = "json"
|
JSON = "json"
|
||||||
@@ -80,7 +92,7 @@ def inspect_capability(
|
|||||||
emit_json(payload)
|
emit_json(payload)
|
||||||
|
|
||||||
|
|
||||||
@app.command("call")
|
@app.command("call", help=CAP_CALL_HELP)
|
||||||
def call_capability(
|
def call_capability(
|
||||||
ctx: typer.Context,
|
ctx: typer.Context,
|
||||||
qualified_name: Annotated[str, typer.Argument(help="Workflow capability name.")],
|
qualified_name: Annotated[str, typer.Argument(help="Workflow capability name.")],
|
||||||
@@ -121,11 +133,10 @@ def call_capability(
|
|||||||
bool,
|
bool,
|
||||||
typer.Option(
|
typer.Option(
|
||||||
"--unwrap-text",
|
"--unwrap-text",
|
||||||
help="Only with --format text: unwrap one MCP text block.",
|
help="Unwrap one safe MCP text block; implies --format text.",
|
||||||
),
|
),
|
||||||
] = False,
|
] = False,
|
||||||
) -> None:
|
) -> None:
|
||||||
"""Call one workflow capability once for authoring/runtime smoke tests."""
|
|
||||||
try:
|
try:
|
||||||
payload = parse_json_input(input_json=input_json, input_file=input_file)
|
payload = parse_json_input(input_json=input_json, input_file=input_file)
|
||||||
except CliInputError as exc:
|
except CliInputError as exc:
|
||||||
@@ -143,7 +154,10 @@ def call_capability(
|
|||||||
try:
|
try:
|
||||||
rendered = render_cap_call_output(
|
rendered = render_cap_call_output(
|
||||||
result,
|
result,
|
||||||
output_format=output_format,
|
output_format=_resolve_cap_call_output_format(
|
||||||
|
output_format=output_format,
|
||||||
|
unwrap_text=unwrap_text,
|
||||||
|
),
|
||||||
unwrap_text=unwrap_text,
|
unwrap_text=unwrap_text,
|
||||||
max_output_chars=max_output_chars,
|
max_output_chars=max_output_chars,
|
||||||
)
|
)
|
||||||
@@ -173,6 +187,17 @@ def render_cap_call_output(
|
|||||||
return _truncate_text(summary, max_output_chars=max_output_chars)
|
return _truncate_text(summary, max_output_chars=max_output_chars)
|
||||||
|
|
||||||
|
|
||||||
|
def _resolve_cap_call_output_format(
|
||||||
|
*,
|
||||||
|
output_format: CapCallOutputFormat,
|
||||||
|
unwrap_text: bool,
|
||||||
|
) -> CapCallOutputFormat:
|
||||||
|
"""Treat --unwrap-text as the explicit request for safe text extraction."""
|
||||||
|
if unwrap_text and output_format is CapCallOutputFormat.JSON:
|
||||||
|
return CapCallOutputFormat.TEXT
|
||||||
|
return output_format
|
||||||
|
|
||||||
|
|
||||||
def _compact_cap_call_summary(result: dict[str, Any]) -> str:
|
def _compact_cap_call_summary(result: dict[str, Any]) -> str:
|
||||||
output = result.get("output")
|
output = result.get("output")
|
||||||
output_summary = _summarize_output(output)
|
output_summary = _summarize_output(output)
|
||||||
|
|||||||
@@ -157,8 +157,6 @@ def test_cap_call_cli_unwraps_single_mcp_text_block(monkeypatch) -> None:
|
|||||||
"everything.default.echo",
|
"everything.default.echo",
|
||||||
"--input",
|
"--input",
|
||||||
'{"message": "hello"}',
|
'{"message": "hello"}',
|
||||||
"--format",
|
|
||||||
"text",
|
|
||||||
"--unwrap-text",
|
"--unwrap-text",
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
@@ -191,6 +189,29 @@ def test_cap_call_cli_refuses_to_unwrap_blob_content(monkeypatch) -> None:
|
|||||||
assert "exactly one MCP text content block" in result.output
|
assert "exactly one MCP text content block" in result.output
|
||||||
|
|
||||||
|
|
||||||
|
def test_cap_call_cli_format_text_requires_unwrap_text(monkeypatch) -> None:
|
||||||
|
_patch_context(
|
||||||
|
monkeypatch,
|
||||||
|
_base_result({"content": [{"type": "text", "text": "hello text"}]}),
|
||||||
|
)
|
||||||
|
|
||||||
|
result = CliRunner().invoke(
|
||||||
|
app,
|
||||||
|
[
|
||||||
|
"cap",
|
||||||
|
"call",
|
||||||
|
"everything.default.echo",
|
||||||
|
"--input",
|
||||||
|
"{}",
|
||||||
|
"--format",
|
||||||
|
"text",
|
||||||
|
],
|
||||||
|
)
|
||||||
|
|
||||||
|
assert result.exit_code != 0
|
||||||
|
assert "--format text requires --unwrap-text" in result.output
|
||||||
|
|
||||||
|
|
||||||
def test_cap_call_cli_refuses_to_unwrap_multiple_text_blocks(monkeypatch) -> None:
|
def test_cap_call_cli_refuses_to_unwrap_multiple_text_blocks(monkeypatch) -> None:
|
||||||
_patch_context(
|
_patch_context(
|
||||||
monkeypatch,
|
monkeypatch,
|
||||||
|
|||||||
@@ -381,8 +381,11 @@ def test_wf_cap_commands_use_rpc_url_override(monkeypatch, tmp_path) -> None:
|
|||||||
|
|
||||||
help_result = runner.invoke(app, ["cap", "call", "--help"])
|
help_result = runner.invoke(app, ["cap", "call", "--help"])
|
||||||
assert help_result.exit_code == 0
|
assert help_result.exit_code == 0
|
||||||
|
help_text = " ".join(help_result.output.split())
|
||||||
assert "--unwrap-text" in help_result.output
|
assert "--unwrap-text" in help_result.output
|
||||||
assert "MCP text block" in help_result.output
|
assert "MCP text content block" in help_text
|
||||||
|
assert "multiple blocks" in help_text
|
||||||
|
assert "non-MCP" in help_text
|
||||||
|
|
||||||
|
|
||||||
def test_wf_source_commands_use_rpc_url_override(monkeypatch, tmp_path) -> None:
|
def test_wf_source_commands_use_rpc_url_override(monkeypatch, tmp_path) -> None:
|
||||||
|
|||||||
Reference in New Issue
Block a user