add the thing formally
This commit is contained in:
@@ -43,10 +43,12 @@ from .nodes import (
|
|||||||
AsyncRegistryHandler,
|
AsyncRegistryHandler,
|
||||||
NodeReturn,
|
NodeReturn,
|
||||||
NodeSpec,
|
NodeSpec,
|
||||||
|
Nothing,
|
||||||
SyncRegistryHandler,
|
SyncRegistryHandler,
|
||||||
build_async_registry,
|
build_async_registry,
|
||||||
build_registry,
|
build_registry,
|
||||||
node,
|
node,
|
||||||
|
outcome,
|
||||||
)
|
)
|
||||||
from .schemas import StateFieldMetadata, state_field
|
from .schemas import StateFieldMetadata, state_field
|
||||||
from .subgraph import subgraph_node
|
from .subgraph import subgraph_node
|
||||||
@@ -64,6 +66,7 @@ __all__ = [
|
|||||||
"PickKeyInput",
|
"PickKeyInput",
|
||||||
"NodeReturn",
|
"NodeReturn",
|
||||||
"NodeSpec",
|
"NodeSpec",
|
||||||
|
"Nothing",
|
||||||
"AsyncRegistryHandler",
|
"AsyncRegistryHandler",
|
||||||
"SyncRegistryHandler",
|
"SyncRegistryHandler",
|
||||||
"SequenceInput",
|
"SequenceInput",
|
||||||
@@ -95,6 +98,7 @@ __all__ = [
|
|||||||
"length",
|
"length",
|
||||||
"pick_key",
|
"pick_key",
|
||||||
"node",
|
"node",
|
||||||
|
"outcome",
|
||||||
"state",
|
"state",
|
||||||
"state_field",
|
"state_field",
|
||||||
"state_path",
|
"state_path",
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ from .callables import (
|
|||||||
from .inference import accepts_context, infer_models, is_basemodel_subclass
|
from .inference import accepts_context, infer_models, is_basemodel_subclass
|
||||||
from .decorator import node
|
from .decorator import node
|
||||||
from .registry import build_async_registry, build_registry
|
from .registry import build_async_registry, build_registry
|
||||||
from .result import NodeReturn
|
from .result import NodeReturn, Nothing, outcome
|
||||||
from .schema import schema_ref_for
|
from .schema import schema_ref_for
|
||||||
from .spec import NodeSpec
|
from .spec import NodeSpec
|
||||||
|
|
||||||
@@ -27,6 +27,7 @@ __all__ = [
|
|||||||
"NodeCallable",
|
"NodeCallable",
|
||||||
"NodeReturn",
|
"NodeReturn",
|
||||||
"NodeSpec",
|
"NodeSpec",
|
||||||
|
"Nothing",
|
||||||
"OutputT",
|
"OutputT",
|
||||||
"PlainNodeCallable",
|
"PlainNodeCallable",
|
||||||
"SyncRegistryHandler",
|
"SyncRegistryHandler",
|
||||||
@@ -36,5 +37,6 @@ __all__ = [
|
|||||||
"infer_models",
|
"infer_models",
|
||||||
"is_basemodel_subclass",
|
"is_basemodel_subclass",
|
||||||
"node",
|
"node",
|
||||||
|
"outcome",
|
||||||
"schema_ref_for",
|
"schema_ref_for",
|
||||||
]
|
]
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
from dataclasses import dataclass
|
from dataclasses import dataclass
|
||||||
from typing import Generic, TypeVar
|
from typing import Generic, TypeVar, overload
|
||||||
|
|
||||||
from pydantic import BaseModel
|
from pydantic import BaseModel
|
||||||
|
|
||||||
@@ -14,3 +14,25 @@ class NodeReturn(Generic[OutputT_co]):
|
|||||||
|
|
||||||
outcome: str
|
outcome: str
|
||||||
output: OutputT_co
|
output: OutputT_co
|
||||||
|
|
||||||
|
|
||||||
|
class Nothing(BaseModel):
|
||||||
|
"""Empty output model for nodes that only choose an outcome."""
|
||||||
|
|
||||||
|
|
||||||
|
@overload
|
||||||
|
def outcome(name: str) -> NodeReturn[Nothing]: ...
|
||||||
|
|
||||||
|
|
||||||
|
@overload
|
||||||
|
def outcome(name: str, output: OutputT_co) -> NodeReturn[OutputT_co]: ...
|
||||||
|
|
||||||
|
|
||||||
|
def outcome(
|
||||||
|
name: str,
|
||||||
|
output: OutputT_co | None = None,
|
||||||
|
) -> NodeReturn[OutputT_co] | NodeReturn[Nothing]:
|
||||||
|
"""Create a node result with an optional output model."""
|
||||||
|
if output is None:
|
||||||
|
return NodeReturn(outcome=name, output=Nothing())
|
||||||
|
return NodeReturn(outcome=name, output=output)
|
||||||
|
|||||||
@@ -8,12 +8,14 @@ from .nodes import (
|
|||||||
NodeCallable,
|
NodeCallable,
|
||||||
NodeReturn,
|
NodeReturn,
|
||||||
NodeSpec,
|
NodeSpec,
|
||||||
|
Nothing,
|
||||||
OutputT,
|
OutputT,
|
||||||
PlainNodeCallable,
|
PlainNodeCallable,
|
||||||
SyncRegistryHandler,
|
SyncRegistryHandler,
|
||||||
build_async_registry,
|
build_async_registry,
|
||||||
build_registry,
|
build_registry,
|
||||||
node,
|
node,
|
||||||
|
outcome,
|
||||||
)
|
)
|
||||||
|
|
||||||
__all__ = [
|
__all__ = [
|
||||||
@@ -26,10 +28,12 @@ __all__ = [
|
|||||||
"NodeCallable",
|
"NodeCallable",
|
||||||
"NodeReturn",
|
"NodeReturn",
|
||||||
"NodeSpec",
|
"NodeSpec",
|
||||||
|
"Nothing",
|
||||||
"OutputT",
|
"OutputT",
|
||||||
"PlainNodeCallable",
|
"PlainNodeCallable",
|
||||||
"SyncRegistryHandler",
|
"SyncRegistryHandler",
|
||||||
"build_async_registry",
|
"build_async_registry",
|
||||||
"build_registry",
|
"build_registry",
|
||||||
"node",
|
"node",
|
||||||
|
"outcome",
|
||||||
]
|
]
|
||||||
|
|||||||
@@ -21,10 +21,9 @@ from pydantic import BaseModel, Field
|
|||||||
from wf_authoring import node
|
from wf_authoring import node
|
||||||
from wf_authoring import NodeReturn
|
from wf_authoring import NodeReturn
|
||||||
from wf_authoring.dsl.conditions import expr, state
|
from wf_authoring.dsl.conditions import expr, state
|
||||||
from wf_authoring.nodes.registry import build_registry
|
from wf_authoring.nodes.result import Nothing, outcome
|
||||||
from wf_authoring.schemas import state_field
|
from wf_authoring.schemas import state_field
|
||||||
from wf_core.run_state import RunStatus
|
from wf_core.run_state import RunStatus
|
||||||
from wf_core.runtime.engine import execute_workflow
|
|
||||||
from wf_core.tokens import END
|
from wf_core.tokens import END
|
||||||
|
|
||||||
# i copy things over, not the greatest design but im not doing deep fixes.
|
# i copy things over, not the greatest design but im not doing deep fixes.
|
||||||
@@ -64,7 +63,7 @@ class ContextInput(BaseModel):
|
|||||||
|
|
||||||
|
|
||||||
class how_do_i_explain_this(BaseModel):
|
class how_do_i_explain_this(BaseModel):
|
||||||
pity_120_available: bool = True
|
pity_120_available: bool = Field(default=True)
|
||||||
|
|
||||||
|
|
||||||
class Input(Counters, ContextInput, Countdown, how_do_i_explain_this):
|
class Input(Counters, ContextInput, Countdown, how_do_i_explain_this):
|
||||||
@@ -160,9 +159,6 @@ class State(
|
|||||||
# context: Context
|
# context: Context
|
||||||
|
|
||||||
|
|
||||||
class Nothing(BaseModel): ... # variance shit IDC
|
|
||||||
|
|
||||||
|
|
||||||
# to the functions
|
# to the functions
|
||||||
# @node(outcomes=("ok", "end")) # breaks because of input | nothing
|
# @node(outcomes=("ok", "end")) # breaks because of input | nothing
|
||||||
@node
|
@node
|
||||||
@@ -209,12 +205,8 @@ def rate_booster(c: Counters) -> NodeReturn[Nothing]:
|
|||||||
rate_guarantee, which is no op.
|
rate_guarantee, which is no op.
|
||||||
"""
|
"""
|
||||||
if c.counter["c_80"] >= 65:
|
if c.counter["c_80"] >= 65:
|
||||||
return NodeReturn("65", Nothing())
|
return outcome("65")
|
||||||
return NodeReturn("0", Nothing())
|
return outcome("0")
|
||||||
|
|
||||||
|
|
||||||
def s(o: str) -> NodeReturn[Nothing]:
|
|
||||||
return NodeReturn(o, Nothing())
|
|
||||||
|
|
||||||
|
|
||||||
def _popped(storage: list[Entity]) -> bool:
|
def _popped(storage: list[Entity]) -> bool:
|
||||||
@@ -223,7 +215,7 @@ def _popped(storage: list[Entity]) -> bool:
|
|||||||
|
|
||||||
@node
|
@node
|
||||||
def popped(s: Storage) -> how_do_i_explain_this:
|
def popped(s: Storage) -> how_do_i_explain_this:
|
||||||
return how_do_i_explain_this(pity_120_available=_popped(s.storage))
|
return how_do_i_explain_this(pity_120_available=not _popped(s.storage))
|
||||||
|
|
||||||
|
|
||||||
@node(outcomes=("240", "80", "10", "1"))
|
@node(outcomes=("240", "80", "10", "1"))
|
||||||
@@ -238,12 +230,12 @@ def pre_roll_router(c: CountersContextOutputInputAhhModelType) -> NodeReturn[Not
|
|||||||
if c.context["type"] == "banner" and (
|
if c.context["type"] == "banner" and (
|
||||||
(sc == 120 and c.pity_120_available) or (sc > 0 and sc % 240 == 0)
|
(sc == 120 and c.pity_120_available) or (sc > 0 and sc % 240 == 0)
|
||||||
):
|
):
|
||||||
return NodeReturn("240", Nothing())
|
return outcome("240")
|
||||||
if ct.get("c_80", 0) % 80 == 0:
|
if ct.get("c_80", 0) % 80 == 0:
|
||||||
return s("80")
|
return outcome("80")
|
||||||
if ct.get("c_10", 0) % 10 == 0:
|
if ct.get("c_10", 0) % 10 == 0:
|
||||||
return s("10")
|
return outcome("10")
|
||||||
return s("1")
|
return outcome("1")
|
||||||
|
|
||||||
|
|
||||||
# now to the weeds of it. a Class!
|
# now to the weeds of it. a Class!
|
||||||
@@ -398,7 +390,7 @@ def tick(state: Countdown) -> Countdown:
|
|||||||
|
|
||||||
@node(outcomes=("tick", END))
|
@node(outcomes=("tick", END))
|
||||||
def keep_rolling(state: Countdown) -> NodeReturn[Nothing]:
|
def keep_rolling(state: Countdown) -> NodeReturn[Nothing]:
|
||||||
return s("tick") if (state.countdown or 0) > 0 else s(END)
|
return outcome("tick") if (state.countdown or 0) > 0 else outcome(END)
|
||||||
|
|
||||||
|
|
||||||
gacha = WorkflowBuilder(
|
gacha = WorkflowBuilder(
|
||||||
@@ -549,34 +541,25 @@ def build_input(context: Context):
|
|||||||
return dec
|
return dec
|
||||||
|
|
||||||
|
|
||||||
def execute(graph: WorkflowBuilder, input: Input):
|
|
||||||
c = graph.compile()
|
|
||||||
r = build_registry(*(graph.node_specs.values()))
|
|
||||||
i = input.model_dump()
|
|
||||||
pprint(i)
|
|
||||||
pprint(c)
|
|
||||||
pprint(r)
|
|
||||||
return execute_workflow(c, i, r)
|
|
||||||
|
|
||||||
|
|
||||||
# twice in a row! it took 100+ and a miss tho
|
# twice in a row! it took 100+ and a miss tho
|
||||||
|
|
||||||
|
|
||||||
def test():
|
def test():
|
||||||
assert 240 - 135 + 20 >= 120, "my math!"
|
assert 240 - 135 + 20 >= 120, "my math!"
|
||||||
d = execute(
|
d = gacha.execute(
|
||||||
gacha,
|
|
||||||
build_input(context)(
|
build_input(context)(
|
||||||
20,
|
20, # lets be optimistic
|
||||||
rolled_previously=240 - 135,
|
rolled_previously=240 - 135,
|
||||||
until_5=5,
|
until_5=5,
|
||||||
until_6=73,
|
until_6=73,
|
||||||
good_stuff=False, # lets pretend
|
good_stuff=False, # lets pretend
|
||||||
),
|
).model_dump()
|
||||||
)
|
)
|
||||||
assert d.status == RunStatus.COMPLETED, "oops"
|
assert d.status == RunStatus.COMPLETED, "oops"
|
||||||
state = State.model_validate(d.state)
|
state = State.model_validate(d.state)
|
||||||
assert any(i["name"] in context["pool"]["n_240"] for i in state.storage)
|
assert any(
|
||||||
|
i["name"] in context["pool"]["n_240"] for i in state.storage
|
||||||
|
), "pity logic failed"
|
||||||
pprint(state.storage)
|
pprint(state.storage)
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ from __future__ import annotations
|
|||||||
|
|
||||||
from pydantic import BaseModel
|
from pydantic import BaseModel
|
||||||
|
|
||||||
from wf_authoring import build_registry, node
|
from wf_authoring import Nothing, build_registry, node, outcome
|
||||||
from wf_core import RuntimeContext
|
from wf_core import RuntimeContext
|
||||||
|
|
||||||
|
|
||||||
@@ -60,3 +60,19 @@ def test_node_can_alias_spec_with_direct_metadata_call() -> None:
|
|||||||
assert alias.fn is echo.fn
|
assert alias.fn is echo.fn
|
||||||
assert alias.input_model is AliasInput
|
assert alias.input_model is AliasInput
|
||||||
assert alias.output_model is AliasOutput
|
assert alias.output_model is AliasOutput
|
||||||
|
|
||||||
|
|
||||||
|
def test_outcome_returns_nothing_output_by_default() -> None:
|
||||||
|
result = outcome("skip")
|
||||||
|
|
||||||
|
assert result.outcome == "skip"
|
||||||
|
assert isinstance(result.output, Nothing)
|
||||||
|
|
||||||
|
|
||||||
|
def test_outcome_can_wrap_explicit_output() -> None:
|
||||||
|
output = AliasOutput(value="hello")
|
||||||
|
|
||||||
|
result = outcome("ok", output)
|
||||||
|
|
||||||
|
assert result.outcome == "ok"
|
||||||
|
assert result.output is output
|
||||||
|
|||||||
Reference in New Issue
Block a user