Skip to content

Conversation

@psychedelicious
Copy link
Contributor

What type of PR is this? (check all applicable)

  • Refactor
  • Feature
  • Bug Fix
  • Optimization
  • Documentation Update
  • Community Node Submission

Have you updated all relevant documentation?

  • Yes
  • No

Description

  • Resolve issue where invocation import order can break graph validation
  • Remove unused GraphInvocation
  • Remove unused LibraryGraph

See commit messages for details.

Related Tickets & Documents

I feel like there must have been tickets for these issues but I can't find em.

QA Instructions, Screenshots, Recordings

Though next isn't fully functional, you can generate on the t2i tab and Workflow Editor (models don't work):

  • Do a t2i tab generation.
  • Create some workflows that don't use a model. Try combinations of iterate and collect nodes.

Merge Plan

This PR can be merged when approved by a code owner & after discussion confirming we are OK with the removal of the unused features.

Added/updated tests?

  • Yes
  • No : please replace this line with details on why tests
    have not been included

@github-actions github-actions bot added python PRs that change python files invocations PRs that change invocations services PRs that change app services frontend PRs that change frontend files PythonTests labels Feb 17, 2024
@psychedelicious psychedelicious force-pushed the psyche/fix/invocations-union branch from f97eaa0 to 9a1c5be Compare February 17, 2024 09:42
Copy link
Collaborator

@lstein lstein left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM.

We use pydantic to validate a union of valid invocations when instantiating a graph.

Previously, we constructed the union while creating the `Graph` class. This introduces a dependency on the order of imports.

For example, consider a setup where we have 3 invocations in the app:

- Python executes the module where `FirstInvocation` is defined, registering `FirstInvocation`.
- Python executes the module where `SecondInvocation` is defined, registering `SecondInvocation`.
- Python executes the module where `Graph` is defined. A union of invocations is created and used to define the `Graph.nodes` field. The union contains `FirstInvocation` and `SecondInvocation`.
- Python executes the module where `ThirdInvocation` is defined, registering `ThirdInvocation`.
- A graph is created that includes `ThirdInvocation`. Pydantic validates the graph using the union, which does not know about `ThirdInvocation`, raising a `ValidationError` about an unknown invocation type.

This scenario has been particularly problematic in tests, where we may create invocations dynamically. The test files have to be structured in such a way that the imports happen in the right order. It's a major pain.

This PR refactors the validation of graph nodes to resolve this issue:

- `BaseInvocation` gets a new method `get_typeadapter`. This builds a pydantic `TypeAdapter` for the union of all registered invocations, caching it after the first call.
- `Graph.nodes`'s type is widened to `dict[str, BaseInvocation]`. This actually is a nice bonus, because we get better type hints whenever we reference `some_graph.nodes`.
- A "plain" field validator takes over the validation logic for `Graph.nodes`. "Plain" validators totally override pydantic's own validation logic. The validator grabs the `TypeAdapter` from `BaseInvocation`, then validates each node with it. The validation is identical to the previous implementation - we get the same errors.

`BaseInvocationOutput` gets the same treatment.
The change to `Graph.nodes` and `GraphExecutionState.results` validation requires some fanagling to get the OpenAPI schema generation to work. See new comments for a details.
`GraphInvocation` is a node that can contain a whole graph. It is removed for a number of reasons:

1. This feature was unused (the UI doesn't support it) and there is no plan for it to be used.

The use-case it served is known in other node execution engines as "node groups" or "blocks" - a self-contained group of nodes, which has group inputs and outputs. This is a planned feature that will be handled client-side.

2. It adds substantial complexity to the graph processing logic. It's probably not enough to have a measurable performance impact but it does make it harder to work in the graph logic.

3. It allows for graphs to be recursive, and the improved invocations union handling does not play well with it. Actually, it works fine within `graph.py` but not in the tests for some reason. I do not understand why. There's probably a workaround, but I took this as encouragement to remove `GraphInvocation` from the app since we don't use it.
Thanks to the resolution of the import vs union issue, we can put tests anywhere.
The workflow library supersedes this unused feature.
Because we now customize the JSON Schema creation for GraphExecutionState, the model_config did nothing.
@psychedelicious psychedelicious force-pushed the psyche/fix/invocations-union branch from 5dd41de to 2c41746 Compare February 19, 2024 22:41
@psychedelicious psychedelicious enabled auto-merge (rebase) February 19, 2024 22:44
@psychedelicious psychedelicious merged commit 851e835 into next Feb 19, 2024
@psychedelicious psychedelicious deleted the psyche/fix/invocations-union branch February 19, 2024 22:48
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

frontend PRs that change frontend files invocations PRs that change invocations python PRs that change python files services PRs that change app services

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants