kopia lustrzana https://github.com/transitive-bullshit/chatgpt-api
feat: apply suggestions from PR
rodzic
8ba59411e7
commit
0bfce67a7b
|
@ -17,7 +17,7 @@ export class HumanFeedbackMechanismCLI<
|
||||||
/**
|
/**
|
||||||
* Prompt the user to select one of a list of options.
|
* Prompt the user to select one of a list of options.
|
||||||
*/
|
*/
|
||||||
protected async askUser(
|
protected async _askUser(
|
||||||
message: string,
|
message: string,
|
||||||
choices: HumanFeedbackUserActions[]
|
choices: HumanFeedbackUserActions[]
|
||||||
): Promise<HumanFeedbackUserActions> {
|
): Promise<HumanFeedbackUserActions> {
|
||||||
|
@ -30,21 +30,21 @@ export class HumanFeedbackMechanismCLI<
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
protected async edit(output: string): Promise<string> {
|
protected async _edit(output: string): Promise<string> {
|
||||||
return editor({
|
return editor({
|
||||||
message: 'Edit the output:',
|
message: 'Edit the output:',
|
||||||
default: output
|
default: output
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
protected async annotate(): Promise<string> {
|
protected async _annotate(): Promise<string> {
|
||||||
return input({
|
return input({
|
||||||
message:
|
message:
|
||||||
'Please leave an annotation (leave blank to skip; press enter to submit):'
|
'Please leave an annotation (leave blank to skip; press enter to submit):'
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
protected async selectOne(
|
protected async _selectOne(
|
||||||
response: TOutput
|
response: TOutput
|
||||||
): Promise<TOutput extends (infer U)[] ? U : never> {
|
): Promise<TOutput extends (infer U)[] ? U : never> {
|
||||||
if (!Array.isArray(response)) {
|
if (!Array.isArray(response)) {
|
||||||
|
@ -58,7 +58,7 @@ export class HumanFeedbackMechanismCLI<
|
||||||
return select({ message: 'Pick one output:', choices })
|
return select({ message: 'Pick one output:', choices })
|
||||||
}
|
}
|
||||||
|
|
||||||
protected async selectN(
|
protected async _selectN(
|
||||||
response: TOutput
|
response: TOutput
|
||||||
): Promise<TOutput extends any[] ? TOutput : never> {
|
): Promise<TOutput extends any[] ? TOutput : never> {
|
||||||
if (!Array.isArray(response)) {
|
if (!Array.isArray(response)) {
|
||||||
|
|
|
@ -11,7 +11,7 @@ export const HumanFeedbackUserActions = {
|
||||||
Edit: 'edit',
|
Edit: 'edit',
|
||||||
Decline: 'decline',
|
Decline: 'decline',
|
||||||
Select: 'select',
|
Select: 'select',
|
||||||
Exit: 'exit'
|
Abort: 'abort'
|
||||||
} as const
|
} as const
|
||||||
|
|
||||||
export type HumanFeedbackUserActions =
|
export type HumanFeedbackUserActions =
|
||||||
|
@ -25,7 +25,7 @@ export const HumanFeedbackUserActionMessages: Record<
|
||||||
[HumanFeedbackUserActions.Edit]: 'Edit the output',
|
[HumanFeedbackUserActions.Edit]: 'Edit the output',
|
||||||
[HumanFeedbackUserActions.Decline]: 'Decline the output',
|
[HumanFeedbackUserActions.Decline]: 'Decline the output',
|
||||||
[HumanFeedbackUserActions.Select]: 'Select outputs to keep',
|
[HumanFeedbackUserActions.Select]: 'Select outputs to keep',
|
||||||
[HumanFeedbackUserActions.Exit]: 'Exit'
|
[HumanFeedbackUserActions.Abort]: 'Abort'
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -48,9 +48,9 @@ export type HumanFeedbackOptions<T extends HumanFeedbackType, TOutput> = {
|
||||||
type?: T
|
type?: T
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Whether the user can bail out of the feedback loop.
|
* Whether the user can abort the process.
|
||||||
*/
|
*/
|
||||||
bail?: boolean
|
abort?: boolean
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Whether the user can edit the output.
|
* Whether the user can edit the output.
|
||||||
|
@ -148,24 +148,24 @@ export abstract class HumanFeedbackMechanism<
|
||||||
this._options = options
|
this._options = options
|
||||||
}
|
}
|
||||||
|
|
||||||
protected abstract selectOne(
|
protected abstract _selectOne(
|
||||||
output: TOutput
|
output: TOutput
|
||||||
): Promise<TOutput extends any[] ? TOutput[0] : never>
|
): Promise<TOutput extends any[] ? TOutput[0] : never>
|
||||||
|
|
||||||
protected abstract selectN(
|
protected abstract _selectN(
|
||||||
response: TOutput
|
response: TOutput
|
||||||
): Promise<TOutput extends any[] ? TOutput : never>
|
): Promise<TOutput extends any[] ? TOutput : never>
|
||||||
|
|
||||||
protected abstract annotate(): Promise<string>
|
protected abstract _annotate(): Promise<string>
|
||||||
|
|
||||||
protected abstract edit(output: string): Promise<string>
|
protected abstract _edit(output: string): Promise<string>
|
||||||
|
|
||||||
protected abstract askUser(
|
protected abstract _askUser(
|
||||||
message: string,
|
message: string,
|
||||||
choices: HumanFeedbackUserActions[]
|
choices: HumanFeedbackUserActions[]
|
||||||
): Promise<HumanFeedbackUserActions>
|
): Promise<HumanFeedbackUserActions>
|
||||||
|
|
||||||
protected parseEditedOutput(editedOutput: string): any {
|
protected _parseEditedOutput(editedOutput: string): any {
|
||||||
const parsedOutput = JSON.parse(editedOutput)
|
const parsedOutput = JSON.parse(editedOutput)
|
||||||
return this._task.outputSchema.parse(parsedOutput)
|
return this._task.outputSchema.parse(parsedOutput)
|
||||||
}
|
}
|
||||||
|
@ -196,14 +196,14 @@ export abstract class HumanFeedbackMechanism<
|
||||||
choices.push(HumanFeedbackUserActions.Edit)
|
choices.push(HumanFeedbackUserActions.Edit)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this._options.bail) {
|
if (this._options.abort) {
|
||||||
choices.push(HumanFeedbackUserActions.Exit)
|
choices.push(HumanFeedbackUserActions.Abort)
|
||||||
}
|
}
|
||||||
|
|
||||||
const choice =
|
const choice =
|
||||||
choices.length === 1
|
choices.length === 1
|
||||||
? HumanFeedbackUserActions.Select
|
? HumanFeedbackUserActions.Select
|
||||||
: await this.askUser(msg, choices)
|
: await this._askUser(msg, choices)
|
||||||
|
|
||||||
const feedback: Record<string, any> = {}
|
const feedback: Record<string, any> = {}
|
||||||
|
|
||||||
|
@ -213,8 +213,8 @@ export abstract class HumanFeedbackMechanism<
|
||||||
break
|
break
|
||||||
|
|
||||||
case HumanFeedbackUserActions.Edit: {
|
case HumanFeedbackUserActions.Edit: {
|
||||||
const editedOutput = await this.edit(stringified)
|
const editedOutput = await this._edit(stringified)
|
||||||
feedback.editedOutput = await this.parseEditedOutput(editedOutput)
|
feedback.editedOutput = await this._parseEditedOutput(editedOutput)
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -228,26 +228,26 @@ export abstract class HumanFeedbackMechanism<
|
||||||
throw new Error('Expected output to be an array')
|
throw new Error('Expected output to be an array')
|
||||||
}
|
}
|
||||||
|
|
||||||
feedback.selected = await this.selectN(output)
|
feedback.selected = await this._selectN(output)
|
||||||
} else if (this._options.type === 'selectOne') {
|
} else if (this._options.type === 'selectOne') {
|
||||||
if (!Array.isArray(output)) {
|
if (!Array.isArray(output)) {
|
||||||
throw new Error('Expected output to be an array')
|
throw new Error('Expected output to be an array')
|
||||||
}
|
}
|
||||||
|
|
||||||
feedback.chosen = await this.selectOne(output)
|
feedback.chosen = await this._selectOne(output)
|
||||||
}
|
}
|
||||||
|
|
||||||
break
|
break
|
||||||
|
|
||||||
case HumanFeedbackUserActions.Exit:
|
case HumanFeedbackUserActions.Abort:
|
||||||
throw new Error('Exiting...')
|
throw new Error('Aborting...')
|
||||||
|
|
||||||
default:
|
default:
|
||||||
throw new Error(`Unexpected choice: ${choice}`)
|
throw new Error(`Unexpected choice: ${choice}`)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this._options.annotations) {
|
if (this._options.annotations) {
|
||||||
const annotation = await this.annotate()
|
const annotation = await this._annotate()
|
||||||
if (annotation) {
|
if (annotation) {
|
||||||
feedback.annotation = annotation
|
feedback.annotation = annotation
|
||||||
}
|
}
|
||||||
|
@ -270,7 +270,7 @@ export function withHumanFeedback<TInput, TOutput, V extends HumanFeedbackType>(
|
||||||
const finalOptions: HumanFeedbackOptions<V, TOutput> = Object.assign(
|
const finalOptions: HumanFeedbackOptions<V, TOutput> = Object.assign(
|
||||||
{
|
{
|
||||||
type: 'confirm',
|
type: 'confirm',
|
||||||
bail: false,
|
abort: false,
|
||||||
editing: false,
|
editing: false,
|
||||||
annotations: false,
|
annotations: false,
|
||||||
mechanism: HumanFeedbackMechanismCLI
|
mechanism: HumanFeedbackMechanismCLI
|
||||||
|
|
|
@ -13,22 +13,24 @@ export class HumanFeedbackMechanismSlack<
|
||||||
T extends HumanFeedbackType,
|
T extends HumanFeedbackType,
|
||||||
TOutput = any
|
TOutput = any
|
||||||
> extends HumanFeedbackMechanism<T, TOutput> {
|
> extends HumanFeedbackMechanism<T, TOutput> {
|
||||||
private slackClient: SlackClient
|
protected _slackClient: SlackClient
|
||||||
|
|
||||||
constructor({
|
constructor({
|
||||||
task,
|
task,
|
||||||
options
|
options,
|
||||||
|
slackClient = new SlackClient()
|
||||||
}: {
|
}: {
|
||||||
task: BaseTask
|
task: BaseTask
|
||||||
options: Required<HumanFeedbackOptions<T, TOutput>>
|
options: Required<HumanFeedbackOptions<T, TOutput>>
|
||||||
|
slackClient: SlackClient
|
||||||
}) {
|
}) {
|
||||||
super({ task, options })
|
super({ task, options })
|
||||||
this.slackClient = new SlackClient()
|
this._slackClient = slackClient
|
||||||
}
|
}
|
||||||
|
|
||||||
protected async annotate(): Promise<string> {
|
protected async _annotate(): Promise<string> {
|
||||||
try {
|
try {
|
||||||
const annotation = await this.slackClient.sendAndWaitForReply({
|
const annotation = await this._slackClient.sendAndWaitForReply({
|
||||||
text: 'Please leave an annotation (optional):'
|
text: 'Please leave an annotation (optional):'
|
||||||
})
|
})
|
||||||
return annotation.text
|
return annotation.text
|
||||||
|
@ -38,8 +40,8 @@ export class HumanFeedbackMechanismSlack<
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected async edit(): Promise<string> {
|
protected async _edit(): Promise<string> {
|
||||||
let { text: editedOutput } = await this.slackClient.sendAndWaitForReply({
|
let { text: editedOutput } = await this._slackClient.sendAndWaitForReply({
|
||||||
text: 'Copy and edit the output:'
|
text: 'Copy and edit the output:'
|
||||||
})
|
})
|
||||||
editedOutput = editedOutput.replace(/```$/g, '')
|
editedOutput = editedOutput.replace(/```$/g, '')
|
||||||
|
@ -47,7 +49,7 @@ export class HumanFeedbackMechanismSlack<
|
||||||
return editedOutput
|
return editedOutput
|
||||||
}
|
}
|
||||||
|
|
||||||
protected async askUser(
|
protected async _askUser(
|
||||||
message: string,
|
message: string,
|
||||||
choices: HumanFeedbackUserActions[]
|
choices: HumanFeedbackUserActions[]
|
||||||
): Promise<HumanFeedbackUserActions> {
|
): Promise<HumanFeedbackUserActions> {
|
||||||
|
@ -59,7 +61,7 @@ export class HumanFeedbackMechanismSlack<
|
||||||
.join('\n')
|
.join('\n')
|
||||||
message += '\n\n'
|
message += '\n\n'
|
||||||
message += 'Reply with the number of your choice.'
|
message += 'Reply with the number of your choice.'
|
||||||
const response = await this.slackClient.sendAndWaitForReply({
|
const response = await this._slackClient.sendAndWaitForReply({
|
||||||
text: message,
|
text: message,
|
||||||
validate: (slackMessage) => {
|
validate: (slackMessage) => {
|
||||||
const choice = parseInt(slackMessage.text)
|
const choice = parseInt(slackMessage.text)
|
||||||
|
@ -69,15 +71,15 @@ export class HumanFeedbackMechanismSlack<
|
||||||
return choices[parseInt(response.text)]
|
return choices[parseInt(response.text)]
|
||||||
}
|
}
|
||||||
|
|
||||||
protected async selectOne(
|
protected async _selectOne(
|
||||||
response: TOutput
|
response: TOutput
|
||||||
): Promise<TOutput extends (infer U)[] ? U : never> {
|
): Promise<TOutput extends (infer U)[] ? U : never> {
|
||||||
if (!Array.isArray(response)) {
|
if (!Array.isArray(response)) {
|
||||||
throw new Error('selectOne called on non-array response')
|
throw new Error('selectOne called on non-array response')
|
||||||
}
|
}
|
||||||
|
|
||||||
const { text: selectedOutput } = await this.slackClient.sendAndWaitForReply(
|
const { text: selectedOutput } =
|
||||||
{
|
await this._slackClient.sendAndWaitForReply({
|
||||||
text:
|
text:
|
||||||
'Pick one output:' +
|
'Pick one output:' +
|
||||||
response.map((r, idx) => `\n*${idx}* - ${r}`).join('') +
|
response.map((r, idx) => `\n*${idx}* - ${r}`).join('') +
|
||||||
|
@ -86,20 +88,19 @@ export class HumanFeedbackMechanismSlack<
|
||||||
const choice = parseInt(slackMessage.text)
|
const choice = parseInt(slackMessage.text)
|
||||||
return !isNaN(choice) && choice >= 0 && choice < response.length
|
return !isNaN(choice) && choice >= 0 && choice < response.length
|
||||||
}
|
}
|
||||||
}
|
})
|
||||||
)
|
|
||||||
return response[parseInt(selectedOutput)]
|
return response[parseInt(selectedOutput)]
|
||||||
}
|
}
|
||||||
|
|
||||||
protected async selectN(
|
protected async _selectN(
|
||||||
response: TOutput
|
response: TOutput
|
||||||
): Promise<TOutput extends any[] ? TOutput : never> {
|
): Promise<TOutput extends any[] ? TOutput : never> {
|
||||||
if (!Array.isArray(response)) {
|
if (!Array.isArray(response)) {
|
||||||
throw new Error('selectN called on non-array response')
|
throw new Error('selectN called on non-array response')
|
||||||
}
|
}
|
||||||
|
|
||||||
const { text: selectedOutput } = await this.slackClient.sendAndWaitForReply(
|
const { text: selectedOutput } =
|
||||||
{
|
await this._slackClient.sendAndWaitForReply({
|
||||||
text:
|
text:
|
||||||
'Select outputs:' +
|
'Select outputs:' +
|
||||||
response.map((r, idx) => `\n*${idx}* - ${r}`).join('') +
|
response.map((r, idx) => `\n*${idx}* - ${r}`).join('') +
|
||||||
|
@ -113,8 +114,7 @@ export class HumanFeedbackMechanismSlack<
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
})
|
||||||
)
|
|
||||||
const chosenOutputs = selectedOutput
|
const chosenOutputs = selectedOutput
|
||||||
.split(',')
|
.split(',')
|
||||||
.map((choice) => parseInt(choice))
|
.map((choice) => parseInt(choice))
|
||||||
|
|
|
@ -13,22 +13,24 @@ export class HumanFeedbackMechanismTwilio<
|
||||||
T extends HumanFeedbackType,
|
T extends HumanFeedbackType,
|
||||||
TOutput = any
|
TOutput = any
|
||||||
> extends HumanFeedbackMechanism<T, TOutput> {
|
> extends HumanFeedbackMechanism<T, TOutput> {
|
||||||
private twilioClient: TwilioConversationClient
|
protected _twilioClient: TwilioConversationClient
|
||||||
|
|
||||||
constructor({
|
constructor({
|
||||||
task,
|
task,
|
||||||
options
|
options,
|
||||||
|
twilioClient = new TwilioConversationClient()
|
||||||
}: {
|
}: {
|
||||||
task: BaseTask
|
task: BaseTask
|
||||||
options: Required<HumanFeedbackOptions<T, TOutput>>
|
options: Required<HumanFeedbackOptions<T, TOutput>>
|
||||||
|
twilioClient: TwilioConversationClient
|
||||||
}) {
|
}) {
|
||||||
super({ task, options })
|
super({ task, options })
|
||||||
this.twilioClient = new TwilioConversationClient()
|
this._twilioClient = twilioClient
|
||||||
}
|
}
|
||||||
|
|
||||||
protected async annotate(): Promise<string> {
|
protected async _annotate(): Promise<string> {
|
||||||
try {
|
try {
|
||||||
const annotation = await this.twilioClient.sendAndWaitForReply({
|
const annotation = await this._twilioClient.sendAndWaitForReply({
|
||||||
name: 'human-feedback-annotation',
|
name: 'human-feedback-annotation',
|
||||||
text: 'Please leave an annotation (optional):'
|
text: 'Please leave an annotation (optional):'
|
||||||
})
|
})
|
||||||
|
@ -39,8 +41,8 @@ export class HumanFeedbackMechanismTwilio<
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected async edit(): Promise<string> {
|
protected async _edit(): Promise<string> {
|
||||||
let { body: editedOutput } = await this.twilioClient.sendAndWaitForReply({
|
let { body: editedOutput } = await this._twilioClient.sendAndWaitForReply({
|
||||||
text: 'Copy and edit the output:',
|
text: 'Copy and edit the output:',
|
||||||
name: 'human-feedback-edit'
|
name: 'human-feedback-edit'
|
||||||
})
|
})
|
||||||
|
@ -49,7 +51,7 @@ export class HumanFeedbackMechanismTwilio<
|
||||||
return editedOutput
|
return editedOutput
|
||||||
}
|
}
|
||||||
|
|
||||||
protected async askUser(
|
protected async _askUser(
|
||||||
message: string,
|
message: string,
|
||||||
choices: HumanFeedbackUserActions[]
|
choices: HumanFeedbackUserActions[]
|
||||||
): Promise<HumanFeedbackUserActions> {
|
): Promise<HumanFeedbackUserActions> {
|
||||||
|
@ -61,7 +63,7 @@ export class HumanFeedbackMechanismTwilio<
|
||||||
.join('\n')
|
.join('\n')
|
||||||
message += '\n\n'
|
message += '\n\n'
|
||||||
message += 'Reply with the number of your choice.'
|
message += 'Reply with the number of your choice.'
|
||||||
const response = await this.twilioClient.sendAndWaitForReply({
|
const response = await this._twilioClient.sendAndWaitForReply({
|
||||||
name: 'human-feedback-ask',
|
name: 'human-feedback-ask',
|
||||||
text: message,
|
text: message,
|
||||||
validate: (message) => {
|
validate: (message) => {
|
||||||
|
@ -72,7 +74,7 @@ export class HumanFeedbackMechanismTwilio<
|
||||||
return choices[parseInt(response.body)]
|
return choices[parseInt(response.body)]
|
||||||
}
|
}
|
||||||
|
|
||||||
protected async selectOne(
|
protected async _selectOne(
|
||||||
response: TOutput
|
response: TOutput
|
||||||
): Promise<TOutput extends (infer U)[] ? U : never> {
|
): Promise<TOutput extends (infer U)[] ? U : never> {
|
||||||
if (!Array.isArray(response)) {
|
if (!Array.isArray(response)) {
|
||||||
|
@ -80,7 +82,7 @@ export class HumanFeedbackMechanismTwilio<
|
||||||
}
|
}
|
||||||
|
|
||||||
const { body: selectedOutput } =
|
const { body: selectedOutput } =
|
||||||
await this.twilioClient.sendAndWaitForReply({
|
await this._twilioClient.sendAndWaitForReply({
|
||||||
name: 'human-feedback-select',
|
name: 'human-feedback-select',
|
||||||
text:
|
text:
|
||||||
'Pick one output:' +
|
'Pick one output:' +
|
||||||
|
@ -94,7 +96,7 @@ export class HumanFeedbackMechanismTwilio<
|
||||||
return response[parseInt(selectedOutput)]
|
return response[parseInt(selectedOutput)]
|
||||||
}
|
}
|
||||||
|
|
||||||
protected async selectN(
|
protected async _selectN(
|
||||||
response: TOutput
|
response: TOutput
|
||||||
): Promise<TOutput extends any[] ? TOutput : never> {
|
): Promise<TOutput extends any[] ? TOutput : never> {
|
||||||
if (!Array.isArray(response)) {
|
if (!Array.isArray(response)) {
|
||||||
|
@ -102,7 +104,7 @@ export class HumanFeedbackMechanismTwilio<
|
||||||
}
|
}
|
||||||
|
|
||||||
const { body: selectedOutput } =
|
const { body: selectedOutput } =
|
||||||
await this.twilioClient.sendAndWaitForReply({
|
await this._twilioClient.sendAndWaitForReply({
|
||||||
name: 'human-feedback-select',
|
name: 'human-feedback-select',
|
||||||
text:
|
text:
|
||||||
'Select outputs:' +
|
'Select outputs:' +
|
||||||
|
|
Ładowanie…
Reference in New Issue