migrate all everything to the new stuff

This commit is contained in:
lda
2026-05-21 06:16:58 +07:00 Verified
parent 0a079ec5f1
commit 274bf34edf
16 changed files with 532 additions and 341 deletions
+39 -28
View File
@@ -4,7 +4,18 @@ from typing import Any
from pydantic import BaseModel
from wf_authoring import NodeReturn, NodeSpec, WorkflowBuilder, node, outcome, state
from wf_authoring import (
NodeReturn,
NodeSpec,
WorkflowBuilder,
input_from,
input_path,
node,
outcome,
output_to,
state,
state_path,
)
from wf_core import END
@@ -169,8 +180,8 @@ def _message_use(
return graph.use(
spec,
id=id,
in_map={"state.message": "message"},
out_map={"message": "state.message"},
input=[input_from(state_path("message"), "message")],
output=[output_to("message", state_path("message"))],
)
@@ -184,8 +195,8 @@ def _status_use(
return graph.use(
spec,
id=id,
in_map={"state.status": "status"},
out_map={"message": "state.message"},
input=[input_from(state_path("status"), "status")],
output=[output_to("message", state_path("message"))],
)
@@ -199,11 +210,11 @@ def _metrics_use(
return graph.use(
spec,
id=id,
in_map={
"state.message": "message",
"state.length": "length",
},
out_map={"message": "state.message"},
input=[
input_from(state_path("message"), "message"),
input_from(state_path("length"), "length"),
],
output=[output_to("message", state_path("message"))],
)
@@ -213,8 +224,8 @@ def build_branch_workflow() -> WorkflowBuilder:
router = graph.use(
classify_message,
id="classify",
in_map={"input.text": "text"},
out_map={"message": "state.message"},
input=[input_from(input_path("text"), "text")],
output=[output_to("message", state_path("message"))],
)
graph.branch(
router,
@@ -237,8 +248,8 @@ def build_handle_workflow() -> WorkflowBuilder:
lookup = graph.use(
lookup_message,
id="lookup",
in_map={"input.text": "text"},
out_map={"message": "state.message"},
input=[input_from(input_path("text"), "text")],
output=[output_to("message", state_path("message"))],
)
deliver = _message_use(graph, deliver_message, id="deliver")
failed = _message_use(graph, fail_safely, id="failed")
@@ -256,8 +267,8 @@ def build_match_workflow() -> WorkflowBuilder:
classifier = graph.use(
classify_status,
id="classify_status",
in_map={"input.text": "text"},
out_map={"status": "state.status"},
input=[input_from(input_path("text"), "text")],
output=[output_to("status", state_path("status"))],
)
decision = graph.match(
state("status"),
@@ -282,11 +293,11 @@ def build_when_workflow() -> WorkflowBuilder:
measure = graph.use(
measure_text,
id="measure",
in_map={"input.text": "text"},
out_map={
"message": "state.message",
"length": "state.length",
},
input=[input_from(input_path("text"), "text")],
output=[
output_to("message", state_path("message")),
output_to("length", state_path("length")),
],
)
decision = graph.when(
state("length").ge(6),
@@ -307,11 +318,11 @@ def build_choose_workflow() -> WorkflowBuilder:
measure = graph.use(
measure_text,
id="measure",
in_map={"input.text": "text"},
out_map={
"message": "state.message",
"length": "state.length",
},
input=[input_from(input_path("text"), "text")],
output=[
output_to("message", state_path("message")),
output_to("length", state_path("length")),
],
)
decision = graph.choose(
(state("length").ge(20), _metrics_use(graph, long_message, id="long")),
@@ -333,8 +344,8 @@ def build_use_ref_workflow() -> WorkflowBuilder:
echo = graph.use_ref(
"demo.echo",
id="echo",
in_map={"input.text": "message"},
out_map={"echoed": "state.message"},
input=[input_from(input_path("text"), "message")],
output=[output_to("echoed", state_path("message"))],
)
graph.connect(echo, "ok", END)
graph.set_entry_point(echo)
+227 -180
View File
@@ -9,201 +9,248 @@ DemoHandler = Callable[[dict[str, object], RuntimeContext], dict[str, object]]
def build_demo_workflow() -> Workflow:
return Workflow.model_validate({
"name": "drive_summary_demo",
"input_schema": {
"type": "object",
"properties": {
"folder_id": {"type": "string"},
"should_email": {"type": "boolean"},
},
"required": ["folder_id", "should_email"],
},
"state_schema": {
"fields": {
"folder_id": {"type": "string"},
"should_email": {"type": "boolean"},
"documents": {"type": "array", "reducer": "wf.std.replace"},
"item_summaries": {"type": "array", "reducer": "wf.std.append"},
"summary": {"type": "string", "reducer": "wf.std.replace"},
"approved": {"type": "boolean", "reducer": "wf.std.replace"},
"approval_comment": {
"type": "string",
"reducer": "wf.std.replace",
return Workflow.model_validate(
{
"name": "drive_summary_demo",
"input_schema": {
"type": "object",
"properties": {
"folder_id": {"type": "string"},
"should_email": {"type": "boolean"},
},
"email_status": {"type": "string", "reducer": "wf.std.replace"},
}
},
"output_schema": {
"type": "object",
"properties": {
"summary": {"type": "string"},
"email_status": {"type": "string"},
"required": ["folder_id", "should_email"],
},
"required": ["summary", "email_status"],
},
"node_defs": [
{
"name": "drive_list_files",
"input_schema": {
"type": "object",
"properties": {"folder_id": {"type": "string"}},
"required": ["folder_id"],
"state_schema": {
"fields": {
"folder_id": {"type": "string"},
"should_email": {"type": "boolean"},
"documents": {"type": "array", "reducer": "wf.std.replace"},
"item_summaries": {"type": "array", "reducer": "wf.std.append"},
"summary": {"type": "string", "reducer": "wf.std.replace"},
"approved": {"type": "boolean", "reducer": "wf.std.replace"},
"approval_comment": {
"type": "string",
"reducer": "wf.std.replace",
},
"email_status": {"type": "string", "reducer": "wf.std.replace"},
}
},
"output_schema": {
"type": "object",
"properties": {
"summary": {"type": "string"},
"email_status": {"type": "string"},
},
"output_schema": {
"type": "object",
"properties": {"documents": {"type": "array"}},
"required": ["documents"],
"required": ["summary", "email_status"],
},
"node_defs": [
{
"name": "drive_list_files",
"input_schema": {
"type": "object",
"properties": {"folder_id": {"type": "string"}},
"required": ["folder_id"],
},
"output_schema": {
"type": "object",
"properties": {"documents": {"type": "array"}},
"required": ["documents"],
},
"outcomes": ["ok"],
},
"outcomes": ["ok"],
},
{
"name": "summarize_document",
"input_schema": {
"type": "object",
"properties": {"document": {"type": "string"}},
"required": ["document"],
{
"name": "summarize_document",
"input_schema": {
"type": "object",
"properties": {"document": {"type": "string"}},
"required": ["document"],
},
"output_schema": {
"type": "object",
"properties": {"item_summary": {"type": "string"}},
"required": ["item_summary"],
},
"outcomes": ["ok"],
},
"output_schema": {
"type": "object",
"properties": {"item_summary": {"type": "string"}},
"required": ["item_summary"],
{
"name": "combine_summaries",
"input_schema": {
"type": "object",
"properties": {"item_summaries": {"type": "array"}},
"required": ["item_summaries"],
},
"output_schema": {
"type": "object",
"properties": {"summary": {"type": "string"}},
"required": ["summary"],
},
"outcomes": ["ok"],
},
"outcomes": ["ok"],
},
{
"name": "combine_summaries",
"input_schema": {
"type": "object",
"properties": {"item_summaries": {"type": "array"}},
"required": ["item_summaries"],
{
"name": "send_email",
"input_schema": {
"type": "object",
"properties": {"summary": {"type": "string"}},
"required": ["summary"],
},
"output_schema": {
"type": "object",
"properties": {"email_status": {"type": "string"}},
"required": ["email_status"],
},
"outcomes": ["sent"],
},
"output_schema": {
"type": "object",
"properties": {"summary": {"type": "string"}},
"required": ["summary"],
{
"name": "mark_email_skipped",
"input_schema": {
"type": "object",
"properties": {},
},
"output_schema": {
"type": "object",
"properties": {"email_status": {"type": "string"}},
"required": ["email_status"],
},
"outcomes": ["ok"],
},
"outcomes": ["ok"],
},
{
"name": "send_email",
"input_schema": {
"type": "object",
"properties": {"summary": {"type": "string"}},
"required": ["summary"],
],
"start": "list_files",
"nodes": [
{
"id": "list_files",
"type": "node",
"node": "drive_list_files",
"desc": "List files from a Google Drive folder",
"input": [
{
"target": {"root": "local", "parts": ["folder_id"]},
"path": {"root": "input", "parts": ["folder_id"]},
}
],
"output": [
{
"source": {"root": "local", "parts": ["documents"]},
"target": {"root": "state", "parts": ["documents"]},
}
],
},
"output_schema": {
"type": "object",
"properties": {"email_status": {"type": "string"}},
"required": ["email_status"],
{
"id": "summarize_each",
"type": "foreach",
"over": "state.documents",
"as": "document",
"mode": "serial",
"on_item_error": "fail",
},
"outcomes": ["sent"],
},
{
"name": "mark_email_skipped",
"input_schema": {
"type": "object",
"properties": {},
{
"id": "summarize_one",
"type": "node",
"node": "summarize_document",
"desc": "Summarize one document",
"input": [
{
"target": {"root": "local", "parts": ["document"]},
"path": {"root": "context", "parts": ["document"]},
}
],
"output": [
{
"source": {"root": "local", "parts": ["item_summary"]},
"target": {"root": "state", "parts": ["item_summaries"]},
}
],
},
"output_schema": {
"type": "object",
"properties": {"email_status": {"type": "string"}},
"required": ["email_status"],
{
"id": "combine_summaries",
"type": "node",
"node": "combine_summaries",
"desc": "Combine item summaries into one final summary",
"input": [
{
"target": {"root": "local", "parts": ["item_summaries"]},
"path": {"root": "state", "parts": ["item_summaries"]},
}
],
"output": [
{
"source": {"root": "local", "parts": ["summary"]},
"target": {"root": "state", "parts": ["summary"]},
}
],
},
"outcomes": ["ok"],
},
],
"start": "list_files",
"nodes": [
{
"id": "list_files",
"type": "node",
"node": "drive_list_files",
"desc": "List files from a Google Drive folder",
"in_map": {"input.folder_id": "folder_id"},
"out_map": {"documents": "state.documents"},
},
{
"id": "summarize_each",
"type": "foreach",
"over": "state.documents",
"as": "document",
"mode": "serial",
"on_item_error": "fail",
},
{
"id": "summarize_one",
"type": "node",
"node": "summarize_document",
"desc": "Summarize one document",
"in_map": {"context.document": "document"},
"out_map": {"item_summary": "state.item_summaries"},
},
{
"id": "combine_summaries",
"type": "node",
"node": "combine_summaries",
"desc": "Combine item summaries into one final summary",
"in_map": {"state.item_summaries": "item_summaries"},
"out_map": {"summary": "state.summary"},
},
{
"id": "should_email",
"type": "condition",
"check": {
"op": "eq",
"left": {"path": "state.should_email"},
"right": {"value": True},
{
"id": "should_email",
"type": "condition",
"check": {
"op": "eq",
"left": {"path": "state.should_email"},
"right": {"value": True},
},
},
},
{
"id": "send_email",
"type": "node",
"node": "send_email",
"desc": "Send the summary by email",
"in_map": {"state.summary": "summary"},
"out_map": {"email_status": "state.email_status"},
},
{
"id": "approve_email",
"type": "interrupt",
"kind": "approval",
"request_map": {
"state.summary": "summary",
"input.folder_id": "folder_id",
{
"id": "send_email",
"type": "node",
"node": "send_email",
"desc": "Send the summary by email",
"input": [
{
"target": {"root": "local", "parts": ["summary"]},
"path": {"root": "state", "parts": ["summary"]},
}
],
"output": [
{
"source": {"root": "local", "parts": ["email_status"]},
"target": {"root": "state", "parts": ["email_status"]},
}
],
},
"out_map": {
"approved": "state.approved",
"comment": "state.approval_comment",
{
"id": "approve_email",
"type": "interrupt",
"kind": "approval",
"request_map": {
"state.summary": "summary",
"input.folder_id": "folder_id",
},
"out_map": {
"approved": "state.approved",
"comment": "state.approval_comment",
},
"outcomes": ["submitted", "cancelled"],
},
"outcomes": ["submitted", "cancelled"],
},
{
"id": "skip_email",
"type": "node",
"node": "mark_email_skipped",
"desc": "Record that email delivery was skipped",
"out_map": {"email_status": "state.email_status"},
},
],
"edges": [
{"from": "list_files", "outcome": "ok", "to": "summarize_each"},
{"from": "summarize_each", "outcome": "loop", "to": "summarize_one"},
{
"from": "summarize_each",
"outcome": "done",
"to": "combine_summaries",
},
{"from": "summarize_one", "outcome": "ok", "to": END},
{"from": "combine_summaries", "outcome": "ok", "to": "should_email"},
{"from": "should_email", "outcome": "true", "to": "approve_email"},
{"from": "should_email", "outcome": "false", "to": "skip_email"},
{"from": "approve_email", "outcome": "submitted", "to": "send_email"},
{"from": "approve_email", "outcome": "cancelled", "to": "skip_email"},
{"from": "send_email", "outcome": "sent", "to": END},
{"from": "skip_email", "outcome": "ok", "to": END},
],
})
{
"id": "skip_email",
"type": "node",
"node": "mark_email_skipped",
"desc": "Record that email delivery was skipped",
"output": [
{
"source": {"root": "local", "parts": ["email_status"]},
"target": {"root": "state", "parts": ["email_status"]},
}
],
},
],
"edges": [
{"from": "list_files", "outcome": "ok", "to": "summarize_each"},
{"from": "summarize_each", "outcome": "loop", "to": "summarize_one"},
{
"from": "summarize_each",
"outcome": "done",
"to": "combine_summaries",
},
{"from": "summarize_one", "outcome": "ok", "to": END},
{"from": "combine_summaries", "outcome": "ok", "to": "should_email"},
{"from": "should_email", "outcome": "true", "to": "approve_email"},
{"from": "should_email", "outcome": "false", "to": "skip_email"},
{"from": "approve_email", "outcome": "submitted", "to": "send_email"},
{"from": "approve_email", "outcome": "cancelled", "to": "skip_email"},
{"from": "send_email", "outcome": "sent", "to": END},
{"from": "skip_email", "outcome": "ok", "to": END},
],
}
)
def drive_list_files(
+54 -37
View File
@@ -34,45 +34,62 @@ async def run_example() -> dict[str, object]:
await service.refresh_connection_catalog("fixture.personal")
plan = RawWorkflowPlan.model_validate({
"name": "mcp_echo_workflow",
"input_schema": {
"type": "object",
"properties": {"text": {"type": "string"}},
"required": ["text"],
},
"state_schema": {
"type": "object",
"properties": {"echoed": {"type": "string"}},
},
"output_schema": {
"type": "object",
"properties": {"echoed": {"type": "string"}},
"required": ["echoed"],
},
"start": "echo",
"nodes": [
{
"id": "echo",
"type": "node",
"node": "fixture.personal.echo_tool",
"in_map": {"input.text": "text"},
"out_map": {"echoed": "state.echoed"},
plan = RawWorkflowPlan.model_validate(
{
"name": "mcp_echo_workflow",
"input_schema": {
"type": "object",
"properties": {"text": {"type": "string"}},
"required": ["text"],
},
{
"id": "raise_mcp_error",
"type": "node",
"node": "wf.std.runtime_error",
"in_map": {"input.text": "message"},
"out_map": {},
"state_schema": {
"type": "object",
"properties": {"echoed": {"type": "string"}},
},
],
"edges": [
{"from": "echo", "outcome": "ok", "to": END},
{"from": "echo", "outcome": "error", "to": "raise_mcp_error"},
{"from": "raise_mcp_error", "outcome": "ok", "to": END},
],
})
"output_schema": {
"type": "object",
"properties": {"echoed": {"type": "string"}},
"required": ["echoed"],
},
"start": "echo",
"nodes": [
{
"id": "echo",
"type": "node",
"node": "fixture.personal.echo_tool",
"input": [
{
"target": {"root": "local", "parts": ["text"]},
"path": {"root": "input", "parts": ["text"]},
}
],
"output": [
{
"source": {"root": "local", "parts": ["echoed"]},
"target": {"root": "state", "parts": ["echoed"]},
}
],
},
{
"id": "raise_mcp_error",
"type": "node",
"node": "wf.std.runtime_error",
"input": [
{
"target": {"root": "local", "parts": ["message"]},
"path": {"root": "input", "parts": ["text"]},
}
],
"output": [],
},
],
"edges": [
{"from": "echo", "outcome": "ok", "to": END},
{"from": "echo", "outcome": "error", "to": "raise_mcp_error"},
{"from": "raise_mcp_error", "outcome": "ok", "to": END},
],
}
)
run = await service.run_workflow_from_plan(plan, {"text": "hello from MCP"})
return {
+20 -11
View File
@@ -4,7 +4,16 @@ from typing import Literal
from pydantic import BaseModel
from wf_authoring import NodeReturn, WorkflowBuilder, node, outcome
from wf_authoring import (
NodeReturn,
WorkflowBuilder,
input_from,
input_path,
node,
outcome,
output_to,
state_path,
)
from wf_core import END
@@ -65,20 +74,20 @@ def build_normalized_wrapper() -> WorkflowBuilder:
raw = graph.use(
raw_status_tool,
id="raw_tool",
in_map={"input.text": "text"},
out_map={
"status": "state.status",
"message": "state.message",
},
input=[input_from(input_path("text"), "text")],
output=[
output_to("status", state_path("status")),
output_to("message", state_path("message")),
],
)
normalizer = graph.use(
normalize_status,
id="normalize",
in_map={
"state.status": "status",
"state.message": "message",
},
out_map={"message": "state.message"},
input=[
input_from(state_path("status"), "status"),
input_from(state_path("message"), "message"),
],
output=[output_to("message", state_path("message"))],
)
graph.connect(raw, "ok", normalizer)
graph.connect(normalizer, "done", END)