-
Notifications
You must be signed in to change notification settings - Fork 89
refactor(conversation): consolidate resolver generation logic #2957
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
refactor(conversation): consolidate resolver generation logic #2957
Conversation
| modelId: "anthropic.claude-3-haiku-20240307-v1:0", | ||
| systemPrompt: "You are a helpful chatbot. Answer questions to the best of your ability.", | ||
| inferenceConfiguration: {"temperature":0.5,"topP":0.9,"maxTokens":100}, | ||
| }; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Changes are from JSON.stringify(...) ('' --> "") and maintaining proper indentation.
| const dataTools = undefined; | ||
| const toolsConfiguration = { dataTools, clientTools }; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is expected if no tools are provided in the conversation directive. It was part of reworking the resolver function generation to stop constructed fragments of the JS resolver runtime code as strings within the transformer.
| /** | ||
| * Configuration for the Conversation Directive | ||
| */ | ||
| export type ConversationDirectiveConfiguration = { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Consolidating directive configuration relevant types here.
| import pluralize from 'pluralize'; | ||
| import { ConversationDirectiveConfiguration } from '../conversation-directive-configuration'; | ||
|
|
||
| export const CONVERSATION_MESSAGES_REFERENCE_FIELD_NAME = 'conversationId'; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Consolidating strings needed in various places and often multiple times.
| export type Tools = { | ||
| tools: Tool[]; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This type was unnecessary.
| validate = (): void => { | ||
| for (const directive of this.directives) { | ||
| if (directive.field.type.kind !== 'NamedType' || directive.field.type.name.value !== 'ConversationMessage') { | ||
| throw new InvalidDirectiveError('@conversation return type must be ConversationMessage'); | ||
| } | ||
| if (directive.handler && directive.functionName) { | ||
| throw new InvalidDirectiveError("'functionName' and 'handler' are mutually exclusive"); | ||
| } | ||
| if (directive.handler) { | ||
| const eventVersion = semver.coerce(directive.handler.eventVersion); | ||
| if (eventVersion?.major !== 1) { | ||
| throw new Error( | ||
| `Unsupported custom conversation handler. Expected eventVersion to match 1.x, received ${directive.handler.eventVersion}`, | ||
| ); | ||
| } | ||
| } | ||
| } | ||
| }; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This validation logic was moved to conversation-field-handler.ts where existing validation logic lives.
| */ | ||
| function data(): ResolverFunctionDefinition { | ||
| return createResolverFunctionDefinition({ | ||
| slotName: 'data', |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The name 'data' for this slot follows requirements set by TransformerResolver.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
These now live as ResolverFunctionDefinitions. For example, this one is the data() definition within assistant-response-pipeline-definition.ts.
packages/amplify-graphql-conversation-transformer/src/conversation-directive-configuration.ts
Show resolved
Hide resolved
| * @property {DirectiveNode} conversationAuthDirective - The auth directive for the conversation model. | ||
| * @property {DirectiveNode} conversationModelDirective - The model directive for the conversation model. | ||
| * @property {DirectiveNode} conversationDirective - The model directive for the conversation model. | ||
| * @property {DirectiveNode} conversationHasManyMessagesDirective - The has-many directive for the messages relationship. | ||
| * @property {FieldDefinitionNode} conversationMessagesField - The field definition for the messages relationship. | ||
| * @property {ObjectTypeDefinitionNode} conversationModel - The complete conversation model object type definition. | ||
| * @property {ObjectTypeDefinitionNode} conversation - The complete conversation model object type definition. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
- nit: doccomment names don't match field names.
- nit: When I first read this I assumed this was the TS representation of the model object itself, and wasn't until I dug into it further that I realized that shape was actually not represented at all. What is this structure actually representing? The
modelshould have the directives and fields on it -- is this just to hoist them into a dedicated property to make them easier to work with?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
- Thanks for catching that. Addressed in a3111dc
- The properties represent GraphQL document nodes (
DirectiveNode,FieldDefinitionNode,ObjectTypeDefinitionNode, etc) that are required input for APIs exposed by dependent transformers (model / relational / auth). For example, themodelDirectivewithinConversationModelis used when invokingmodelTransformer.object. The alternative is passing around only theObjectTypeDefinitionNodeand extracting the other nodes as needed, but that leads to some gnarly and difficult to maintain code.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No strong objections, but I'll note that if we need more transformers to operate on a the conversation model, you'll have to hoist more & more properties into this model structure. I suppose it's not all that different from writing utility methods to extract same, except that you're carrying the properties around everywhere rather than invoking a utility method when needed. I'll chalk this up to a stylistic preference & move on. :)
packages/amplify-graphql-conversation-transformer/src/graphql-types/message-model.ts
Show resolved
Hide resolved
...ify-graphql-conversation-transformer/src/resolvers/assistant-response-pipeline-definition.ts
Show resolved
Hide resolved
packages/amplify-graphql-conversation-transformer/src/resolvers/generate-resolver.ts
Show resolved
Hide resolved
packages/amplify-graphql-conversation-transformer/src/resolvers/index.ts
Outdated
Show resolved
Hide resolved
packages/amplify-graphql-conversation-transformer/src/resolvers/resolver-function-definition.ts
Outdated
Show resolved
Hide resolved
...s/amplify-graphql-conversation-transformer/src/resolvers/send-message-pipeline-definition.ts
Show resolved
Hide resolved
...amplify-graphql-conversation-transformer/src/transformer-steps/conversation-field-handler.ts
Outdated
Show resolved
Hide resolved
...amplify-graphql-conversation-transformer/src/transformer-steps/conversation-field-handler.ts
Show resolved
Hide resolved
| * @throws {InvalidDirectiveError} If the configuration is invalid. | ||
| */ | ||
| private validateHandler(config: ConversationDirectiveConfiguration): void { | ||
| if (config.handler && config.functionName) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
we could remove functionName at this point.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yea, I'll follow up with that. This PR is exclusively non-functional changes and considering its size, I'd like to keep it that way.
Description of changes
resolvers/*.template.jsfiles toresolvers/templates/*.template.jsResolverFunctionDefinitionandPipelineDefinitiontypes to allow function and pipeline definitions to live as configuration rather than runtime logic.invoke-lambdafunction template to remove the need for string defined JS resolver blocks in runtime code.CDK / CloudFormation Parameters Changed
N/A
Issue #, if available
N/A
Description of how you validated changes
CI PR workflow
E2E run
Checklist
yarn testpassesTests are changed or addedRelevant documentation is changed or added (and PR referenced)New AWS SDK calls or CloudFormation actions have been added to relevant test and service IAM policiesAny CDK or CloudFormation parameter changes are called out explicitlyBy submitting this pull request, I confirm that my contribution is made under the terms of the Apache 2.0 license.