Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Update make_artifact to use value type for Union #342

Merged

Conversation

kamalesh0406
Copy link
Contributor

This PR fixes issue #235, where we display the json summary for the list instead of the actual contents of the list. This code will work well for Optional Types, but it's not perfect.

Suppose the user defines a type as typing.Union[typing.List[int], typing.Tuple[int]] or typing.Union[int, float] etc, we will not get the correct output. However, to fix this we would need to check the type of the value summary and choose the view based on it. I am hesitant to do this because checking types in Typescript for objects that were defined in Python seems weird to me at least.

The ideal solution would be to figure out the value type and return the correct type from the backend instead of the Union type. I would like to get your views on whether I should go for this backend fix instead. Thanks.

@neutralino1
Copy link
Member

You make a good point @kamalesh0406. Ideally, we would have access to the actual type of the object, instead of Union.

This is may require some messing around with how we create artifacts. Currently, we always use the function's type hint as artifact type. That's evidently suboptimal for Union types. One option would be to always take the actual value's type here:
https://github.com/sematic-ai/sematic/blob/main/sematic/db/models/factories.py#L145
although we have to make sure this has no unintended consequences.

Another option is to store the type's serialization as part of the JSON summary (I believe we do this for other types, such as List, and dataclass). If the first option works, I would prefer that.

Thoughts @augray, @tscurtu ?

@tscurtu
Copy link
Contributor

tscurtu commented Nov 21, 2022

Storing the Artifact with explicit type information sounds like the optimal solution to me.

One complication / thing we should keep in mind:

Currently we explicitly cast func inputs and outputs to their declared type hint types, but we plan to remove this behavior because it forces us to be rigorous when it comes to every single type, and provide a way to instantiate every single type, limiting support of external types. We intend to merely replace it with a check that the type can be casted.

The removal would lead to an increase in the number of effective types, due to polymorphism and duck typing. Taking this effective un-cast type as the artifact's type could therefore result in handling types which don't have supported UI representations. In this case we could / would need to fall back to the declared type hint again, but we gain hit the ambiguous Union type.

A premature solution proposal would be to have can_cast_type return the effective supported type instead of True, and use that as the Artifact type. For most types, this would be themselves. For Union, it would be one of the member types.

@tscurtu tscurtu added bug Something isn't working types Typing system and validation labels Nov 21, 2022
if (containsNoneType) {
const otherTypes: TypeParamRepr[] = typeRepr[2].args.filter((argsTypeParamRep) => argsTypeParamRep.type[1]!=="NoneType");

//Union Types in Python should not contain more than two types. Therefore, the otherTypes array should be of length 1.
Copy link
Contributor

@chance-an chance-an Nov 21, 2022

Choose a reason for hiding this comment

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

Union Types in Python should not contain more than two types The said is probably true for the Optional type.

But I am not sure it is always true for a generic Union type. It seems it is allowed to have more than two types.

Eg

Union[Union[int, str], float] == Union[int, str, float]

Copy link
Member

Choose a reason for hiding this comment

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

Agree with Chance--you can have Union of arbitrarily many types in python.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Right, I should have mentioned it as Optional Types in the comments. I will correct it in my next revision. I agree that for the actual Union type we can have multiple possible types as input.

function UnionValueView(props: ValueViewProps) {
const typeRepr = props.typeRepr as AliasTypeRepr;

if (!typeRepr[2].args) {
Copy link
Contributor

Choose a reason for hiding this comment

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

Since typeRepr[2].args appears several times, shall we assign it to a variable to give it a meaningful name?

e.g.

let [, , {args: typeArgs} ] = typeRepr

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yup this sounds good. I will fix it in my next revision.

return ValueView(props);
}

const noneTypeArgs: TypeParamRepr[] = typeRepr[2].args.filter((argsTypeParamRep) => argsTypeParamRep.type[1]==="NoneType");
Copy link
Contributor

Choose a reason for hiding this comment

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

or alternatively

.filter(({type:[, typeName]}) => typeName === "NoneType");

@chance-an
Copy link
Contributor

For Union, it would be one of the member types.

I like this idea. If some server side processing could reduce it to the exact member type. It is much simpler.

@chance-an
Copy link
Contributor

Storing the Artifact with explicit type information sounds like the optimal solution to me

If storage cost is of concern, we can store the artifacts with a type id. The FE could download the specific typing from the API using that id separately.

@augray
Copy link
Member

augray commented Nov 21, 2022

One option would be to always take the actual value's type here:
https://github.com/sematic-ai/sematic/blob/main/sematic/db/models/factories.py#L145
although we have to make sure this has no unintended consequences.

This is viable ONLY if we have a reliable way of identifying which type it is, using the declared type hint rather than just type(value). Why? Because the type hint contains information we will need. Consider this:

@sematic.func
def foo(x: Union[List[str], List[int]]) -> None:
    # ...
 
foo(["a", "b", "c"])  # Call A
foo([1, 2, 3])  # Call B

If we just stored type(x), we would wind up storing list for both "Call A" and "Call B", whereas what we should store is List[str] for "Call A" and List[int] for "Call B". I think we will likely want/need Union to be a "first class citizen" of our typing library, and have support for inspecting which one of the union-ed types the value actually corresponds to.

@augray
Copy link
Member

augray commented Nov 21, 2022

I think we will likely want/need Union to be a "first class citizen" of our typing library

Given this, it will be a non-trivial change. Perhaps we should discuss in Discord to align on a solution?

@kamalesh0406
Copy link
Contributor Author

Sure we can discuss on discord about this. But I agree we would need a non-trivial solution for this since this problem is confined to Union types. I feel we would need special helper functions to help us figure out the correct type for the value for Union types, which can be used only for updating the json_summary ?

@tscurtu tscurtu added the ui UI related work label Nov 22, 2022
@kamalesh0406 kamalesh0406 force-pushed the kamalesh0406/fix-optional-input-display branch from a997150 to 5d1e825 Compare November 27, 2022 04:21
@kamalesh0406 kamalesh0406 changed the title Add UnionValueView to fix issue displaying optional types Update make_artifact to use value type for Union Nov 27, 2022
@kamalesh0406
Copy link
Contributor Author

I have updated the PR such that we store the actual value type in the artifact instead of using the type hint. I will add a unit test once I get the initial comments on it.

def is_union(type_: Any) -> bool:
origin_type = get_origin_type(type_)

return origin_type==Union
Copy link
Contributor Author

Choose a reason for hiding this comment

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

I want to know if this equality check is fine with everyone. The other choice would be to use a can_cast here between the input and union type. But based on my understanding, the can_cast function checks if we can cast the type to any of the types specified inside Union and not the Union type itself. Do correct me if I am wrong here.

Copy link
Member

Choose a reason for hiding this comment

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

I think this works, also is works here.

Also, add spaces around the operator:

origin_type is Union

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Sure I will update it.

Copy link
Member

@neutralino1 neutralino1 left a comment

Choose a reason for hiding this comment

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

We're almost there, thank you.

def is_union(type_: Any) -> bool:
origin_type = get_origin_type(type_)

return origin_type==Union
Copy link
Member

Choose a reason for hiding this comment

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

I think this works, also is works here.

Also, add spaces around the operator:

origin_type is Union

sematic/db/models/factories.py Outdated Show resolved Hide resolved
sematic/db/models/factories.py Outdated Show resolved Hide resolved
@kamalesh0406 kamalesh0406 force-pushed the kamalesh0406/fix-optional-input-display branch from 5d1e825 to a037b70 Compare December 2, 2022 17:25
@kamalesh0406 kamalesh0406 requested review from neutralino1 and removed request for augray December 2, 2022 17:26
Copy link
Contributor

@chance-an chance-an left a comment

Choose a reason for hiding this comment

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

Great work! Thank you for making the change.

sematic/db/models/tests/test_factories.py Show resolved Hide resolved
@kamalesh0406
Copy link
Contributor Author

@chance-an @neutralino1 can you merge it as well, I do not have write access..

@tscurtu
Copy link
Contributor

tscurtu commented Jan 4, 2023

Thank you, @kamalesh0406!

@tscurtu tscurtu merged commit 58e4afb into sematic-ai:main Jan 4, 2023
jrcribb pushed a commit to jrcribb/sematic that referenced this pull request Apr 9, 2024
This PR fixes issue sematic-ai#235, where we display the json summary for the list
instead of the actual contents of the list. This code will work well for
Optional Types, but it's not perfect.

Suppose the user defines a type as `typing.Union[typing.List[int],
typing.Tuple[int]]` or `typing.Union[int, float]` etc, we will not get
the correct output. However, to fix this we would need to check the type
of the value summary and choose the view based on it. I am hesitant to
do this because checking types in Typescript for objects that were
defined in Python seems weird to me at least.

The ideal solution would be to figure out the value type and return the
correct type from the backend instead of the `Union` type. I would like
to get your views on whether I should go for this backend fix instead.
Thanks.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working types Typing system and validation ui UI related work
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants