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

Use reshape to simplify deserialization #2180

Closed
wants to merge 2 commits into from

Conversation

joheredi
Copy link
Member

@joheredi joheredi commented Dec 26, 2023

Integrate the new reshape function from core-util to simplify serialization and deserialization.

reshape is WIP in Azure/azure-sdk-for-js#28095

@joheredi joheredi requested a review from xirzec December 26, 2023 23:27
@xirzec
Copy link
Member

xirzec commented Dec 27, 2023

Very nice! I love that we're removing more lines than we're adding in this PR.

I assume we could do a similar change for serializing input shapes from the customer?

Comment on lines -59 to +56
};
let deserializedResponse: unknown = result.body;
deserializedResponse = reshape(
deserializedResponse,
"choices[].delta.session_state",
"sessionState"
);
deserializedResponse = reshape(
deserializedResponse,
"choices[].session_state",
Copy link
Member

Choose a reason for hiding this comment

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

@xirzec If we could support jsonpath, I think we could somehow use $..session_state to be more simpler ?

})),
};
let deserializedResponse: unknown = result.body;
return deserializedResponse as AnalyzeTextResult;
Copy link
Member

Choose a reason for hiding this comment

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

why not return result.body directly ?

Copy link
Member Author

Choose a reason for hiding this comment

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

Yup I'm pushing that change next.

Comment on lines +199 to +204
let deserializedResponse: unknown = result.body;
deserializedResponse = reshape(
deserializedResponse,
"value[].event.data_base64",
"dataBase64"
);
Copy link
Member

Choose a reason for hiding this comment

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

I feel like this is incorrect ? because we still need to convert it into uint8Array ?

Copy link
Member Author

Choose a reason for hiding this comment

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

Yes, still need to handle the conversion. I let it out temporarily b/c I wanted to double check if we want to keep the current approach or need to update

Comment on lines +268 to +324
let deserializedResponse: unknown = result.body;
deserializedResponse = reshape(
deserializedResponse,
"created",
(value) => new Date(value as string)
);
deserializedResponse = reshape(
deserializedResponse,
"choices[].message.function_call",
"functionCall"
);
deserializedResponse = reshape(
deserializedResponse,
"choices[].finish_reason",
"finishReason"
);
deserializedResponse = reshape(
deserializedResponse,
"choices[].content_filter_results",
"contentFilterResults"
);
deserializedResponse = reshape(
deserializedResponse,
"choices[].content_filter_results.self_harm",
"selfHarm"
);
deserializedResponse = reshape(
deserializedResponse,
"prompt_annotations",
"promptFilterResults"
);
deserializedResponse = reshape(
deserializedResponse,
"prompt_annotations[].prompt_index",
"promptIndex"
);
deserializedResponse = reshape(
deserializedResponse,
"prompt_annotations[].content_filter_results",
"contentFilterResults"
);
deserializedResponse = reshape(
deserializedResponse,
"usage.completion_tokens",
"completionTokens"
);
deserializedResponse = reshape(
deserializedResponse,
"usage.prompt_tokens",
"promptTokens"
);
deserializedResponse = reshape(
deserializedResponse,
"usage.total_tokens",
"totalTokens"
);
return deserializedResponse as ChatCompletions;
Copy link
Member

@qiaozha qiaozha Dec 28, 2023

Choose a reason for hiding this comment

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

I wonder if we should just pass a function to reshape a specific type in a more general way ? feel like if every property has been renamed, we will still need to traverse the object in the operations.ts ? #1971 maybe just leverage reshape in this better modularization ?

/cc @dgetu

id: result.body["id"],
created: new Date(result.body["created"]),
choices: result.body["choices"].map((p) => ({
message: !p.message ? undefined : (p.message as any),
Copy link
Member

Choose a reason for hiding this comment

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

I am not sure if removing this would work, if I remember corrrectly, there's a recursive reference here. which has not yet been resolved. #1982

/cc @dgetu

Copy link
Member Author

Choose a reason for hiding this comment

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

If the recursive properties don't need to be reshaped it should be fine, as we'll be returning the same as we got from the service. True, this doesn't handle recursive properties that need to be transformed.

statements.push(`let deserializedResponse: unknown = result.body;`);

deserialize(responseProperties, statements, visitedTypes);
statements.push(`return deserializedResponse as ${resultType};`);
Copy link
Member

Choose a reason for hiding this comment

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

just a little curious, why we need as here ?

Copy link
Member Author

Choose a reason for hiding this comment

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

Because the reshape function returns Unknown I was thinking to suggest adding some type magic to the function so that it could actually returns the type we need, however as I started trying it out it became too complex and likely not worth investing in it and maintaining for the internal added type-safety

Copy link
Member

Choose a reason for hiding this comment

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

I'm fine changing reshape to just deal in any if that saves you effort :)

Comment on lines -755 to -765
} else if (
(property.restApiName === "message" ||
property.restApiName === "messages") &&
(property.type.name === "ChatMessage" ||
property.type.elementType?.name === "ChatMessage")
) {
definition = `"${property.restApiName}": ${
!property.optional
? `${propertyFullName} as any`
: `!${propertyFullName} ? undefined : ${propertyFullName} as any`
}`;
Copy link
Member

Choose a reason for hiding this comment

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

This is the workaround for recursive reference in codegen.

Copy link
Member Author

Choose a reason for hiding this comment

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

Yeah I wanted to try this out without the workaround, since the properties in message don't need to be reshaped I think we should be fine without it

switch (type.type) {
case "datetime":
return "(value) => new Date(value as string)";
default:
Copy link
Member

Choose a reason for hiding this comment

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

missing bytes ? and datetime serialization is another case

Copy link
Member Author

Choose a reason for hiding this comment

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

yes, I'll add both of them

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants