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
feat: implement response step handler (PL3-50) #551
base: master
Are you sure you want to change the base?
Conversation
e3c455a
to
cace4a4
Compare
9f0cf64
to
4940fac
Compare
if (!this.cachedCondition) { | ||
this.cachedCondition = buildCondition(this.rawVariant.condition, this.varContext); | ||
} | ||
return this.cachedCondition; |
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 caching might be unnecessary in hindsight? This will essentially just save one object constructor call per variant.
default: | ||
throw new VError('unexpected value for card layout'); | ||
} | ||
} |
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.
Probably could just turn this function into a map.
import { BaseLanguageGeneratorReturn } from './base.interface'; | ||
import { AIBillingEvents } from './billed.interface'; | ||
|
||
export class BilledGenerator< |
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 class is essentially a "Dynamic Decorator" pattern.
The design problem here is that I need to inject billing utilities into origGenerator
such that:
- The
origGenerator
is abstracted from the billing calls - If the tokens are exceeded, then the
.generate()
call is never called. There isn't a perfectly clean abstraction using events and callbacks that permits this behaviour. I don't think.generate()
should be aware that it has to not execute if the tokens run out. Therefore, it makes the most sense to put the billing in a wrapper method also called.generate()
- The billing calls are dependent on a dynamic value, the
runtime
, which may differ with each request.
A decorator like AuthGuard
provides the right abstraction, but this is a compile-time construct that doesn't really fit the constraints above.
Fixes or implements PL3-50
Brief description. What is this change?
Implements the v3 response step handler. Functionality that is included with this PR includes:
Implementation details. How do you make this change?
The
response.handler.ts
file is the best "entry point" for the code changes.The
response.handler.ts
calls "framework" functions from:selectDiscriminator.ts
evaluateVariant.ts
evaluateCarousel.ts
These "framework" functions define the high-level logical flow of evaluating a v3 Response step. It includes hooks where we can register handlers for specific functionality like different Variants, Attachments, and Conditions.
The translation of Variants to traces is handled by the
variant
module. In particular:text.variant.ts
- This handles translating content from a text variant into a V3 text tracejson.variant.ts
- This handles translating content from a JSON variant into a V3 JSON traceprompt.variant.ts
- This generates a response using knowledge base or LLMs based on the prompt variant's configuration and outputs a V3 text trace containing the generated content.The translation of Attachment CMS entities to traces is handled by the
variant/attachment
module. The individual handlers perform similar purposes as the variant handlers.No Condition handlers are implemented at the moment, but the code has been designed to account for that functionality in the future. Skeleton files that can house a future implementation have already been written.
This PR also includes additional utility functions to support the implementation:
variableContext
- A reimplementation of thevariables
map andreplaceVariables()
function in the old V2 runtime. The main change is that (1) variable substitution is done onMarkup
data rather than regexing strings and (2) we package the data (variables
) and the function (replaceVariables
) into a single classVariableContext
which is injected into specific handlers (e.g. Variant handlers). This was done to simplify code becausereplaceVariables()
's call signature already resembles a method call signature, i.e, why writereplaceVariables(variables, ...)
when you could just dovariables.replaceVariables(...)
?language-generator
- This module reimplements some of the AI functionality ingeneral-runtime
to cleanup some messy patterns, e.g, in the V2 code, we insert a prompt with Voiceflow variables into a chat history and then perform a regex replace on all messages in the chat history (this work is useless because all but the first and last messages would actually have variables). The V3language-generator
module reimplements this does not perform any variable substitution whatsoever. The caller must resolve the variables before callinglanguage-generator
.markupUtils
- This module serializesMarkup
into a plain string. There is a placeholder implementation that converts formatting like bolding into markup bolding (i.e**
). This implementation isn't actually useful, because with our current use-cases, if we serialize, then there isn't any formatting to convert. I've left this placeholder implementation in case we need to have an opinionated serialization of rich text.translateVariants
- This is a skeleton file for us to implement automatic translation of text in the future, as per Mike's designs.Setup information
N/A
Deployment Notes
Requires changes in
base-types
.Related PRs
Checklist