kopia lustrzana https://github.com/transitive-bullshit/chatgpt-api
refactor: avoid code duplication
rodzic
768a727597
commit
9c41ea5607
|
@ -4,7 +4,6 @@ import input from '@inquirer/input'
|
||||||
import select from '@inquirer/select'
|
import select from '@inquirer/select'
|
||||||
|
|
||||||
import { Agentic } from '@/agentic'
|
import { Agentic } from '@/agentic'
|
||||||
import { TaskResponseMetadata } from '@/types'
|
|
||||||
|
|
||||||
import {
|
import {
|
||||||
HumanFeedbackMechanism,
|
HumanFeedbackMechanism,
|
||||||
|
@ -13,22 +12,6 @@ import {
|
||||||
UserActions
|
UserActions
|
||||||
} from './feedback'
|
} from './feedback'
|
||||||
|
|
||||||
/**
|
|
||||||
* Prompt the user to select one of a list of options.
|
|
||||||
*/
|
|
||||||
async function askUser(
|
|
||||||
message: string,
|
|
||||||
choices: UserActions[]
|
|
||||||
): Promise<UserActions> {
|
|
||||||
return select({
|
|
||||||
message,
|
|
||||||
choices: choices.map((choice) => ({
|
|
||||||
name: UserActionMessages[choice],
|
|
||||||
value: choice
|
|
||||||
}))
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
export class HumanFeedbackMechanismCLI extends HumanFeedbackMechanism {
|
export class HumanFeedbackMechanismCLI extends HumanFeedbackMechanism {
|
||||||
constructor({
|
constructor({
|
||||||
agentic,
|
agentic,
|
||||||
|
@ -42,172 +25,49 @@ export class HumanFeedbackMechanismCLI extends HumanFeedbackMechanism {
|
||||||
this._options = options
|
this._options = options
|
||||||
}
|
}
|
||||||
|
|
||||||
protected async annotate(
|
/**
|
||||||
response: any,
|
* Prompt the user to select one of a list of options.
|
||||||
metadata: TaskResponseMetadata
|
*/
|
||||||
): Promise<void> {
|
protected async askUser(
|
||||||
const annotation = await input({
|
message: string,
|
||||||
|
choices: UserActions[]
|
||||||
|
): Promise<UserActions> {
|
||||||
|
return select({
|
||||||
|
message,
|
||||||
|
choices: choices.map((choice) => ({
|
||||||
|
name: UserActionMessages[choice],
|
||||||
|
value: choice
|
||||||
|
}))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
protected async edit(output: string): Promise<string> {
|
||||||
|
return editor({
|
||||||
|
message: 'Edit the output:',
|
||||||
|
default: output
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
protected async annotate(): Promise<string> {
|
||||||
|
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):'
|
||||||
})
|
})
|
||||||
|
|
||||||
if (annotation) {
|
|
||||||
metadata.feedback.annotation = annotation
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
protected async confirm(
|
protected async selectOne(response: any[]): Promise<void> {
|
||||||
response: any,
|
|
||||||
metadata: TaskResponseMetadata
|
|
||||||
): Promise<void> {
|
|
||||||
const stringified = JSON.stringify(response, null, 2)
|
|
||||||
const msg = [
|
|
||||||
'The following output was generated:',
|
|
||||||
stringified,
|
|
||||||
'What would you like to do?'
|
|
||||||
].join('\n')
|
|
||||||
const choices: UserActions[] = [UserActions.Accept, UserActions.Decline]
|
|
||||||
|
|
||||||
if (this._options.editing) {
|
|
||||||
choices.push(UserActions.Edit)
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this._options.bail) {
|
|
||||||
choices.push(UserActions.Exit)
|
|
||||||
}
|
|
||||||
|
|
||||||
const feedback = await askUser(msg, choices)
|
|
||||||
|
|
||||||
metadata.feedback = {}
|
|
||||||
|
|
||||||
switch (feedback) {
|
|
||||||
case UserActions.Accept:
|
|
||||||
metadata.feedback.accepted = true
|
|
||||||
break
|
|
||||||
|
|
||||||
case UserActions.Edit: {
|
|
||||||
const editedOutput = await editor({
|
|
||||||
message: 'Edit the output:',
|
|
||||||
default: stringified
|
|
||||||
})
|
|
||||||
metadata.feedback.editedOutput = editedOutput
|
|
||||||
break
|
|
||||||
}
|
|
||||||
|
|
||||||
case UserActions.Decline:
|
|
||||||
metadata.feedback.accepted = false
|
|
||||||
break
|
|
||||||
|
|
||||||
case UserActions.Exit:
|
|
||||||
throw new Error('Exiting...')
|
|
||||||
|
|
||||||
default:
|
|
||||||
throw new Error(`Unexpected feedback: ${feedback}`)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
protected async selectOne(
|
|
||||||
response: any[],
|
|
||||||
metadata: TaskResponseMetadata
|
|
||||||
): Promise<void> {
|
|
||||||
const stringified = JSON.stringify(response, null, 2)
|
|
||||||
const msg = [
|
|
||||||
'The following output was generated:',
|
|
||||||
stringified,
|
|
||||||
'What would you like to do?'
|
|
||||||
].join('\n')
|
|
||||||
const choices: UserActions[] = [UserActions.Select]
|
|
||||||
|
|
||||||
if (this._options.editing) {
|
|
||||||
choices.push(UserActions.Edit)
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this._options.bail) {
|
|
||||||
choices.push(UserActions.Exit)
|
|
||||||
}
|
|
||||||
|
|
||||||
const feedback = await askUser(msg, choices)
|
|
||||||
|
|
||||||
metadata.feedback = {}
|
|
||||||
|
|
||||||
switch (feedback) {
|
|
||||||
case UserActions.Edit: {
|
|
||||||
const editedOutput = await editor({
|
|
||||||
message: 'Edit the output:',
|
|
||||||
default: stringified
|
|
||||||
})
|
|
||||||
metadata.feedback.editedOutput = editedOutput
|
|
||||||
break
|
|
||||||
}
|
|
||||||
|
|
||||||
case UserActions.Select: {
|
|
||||||
const choices = response.map((option) => ({
|
const choices = response.map((option) => ({
|
||||||
name: option,
|
name: option,
|
||||||
value: option
|
value: option
|
||||||
}))
|
}))
|
||||||
const chosen = await select({ message: 'Pick one output:', choices })
|
return select({ message: 'Pick one output:', choices })
|
||||||
metadata.feedback.chosen = chosen
|
|
||||||
break
|
|
||||||
}
|
}
|
||||||
|
|
||||||
case UserActions.Exit:
|
protected async selectN(response: any[]): Promise<any[]> {
|
||||||
throw new Error('Exiting...')
|
|
||||||
|
|
||||||
default:
|
|
||||||
throw new Error(`Unexpected feedback: ${feedback}`)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
protected async selectN(
|
|
||||||
response: any[],
|
|
||||||
metadata: TaskResponseMetadata
|
|
||||||
): Promise<void> {
|
|
||||||
const stringified = JSON.stringify(response, null, 2)
|
|
||||||
const msg = [
|
|
||||||
'The following output was generated:',
|
|
||||||
stringified,
|
|
||||||
'What would you like to do?'
|
|
||||||
].join('\n')
|
|
||||||
const choices: UserActions[] = [UserActions.Select]
|
|
||||||
|
|
||||||
if (this._options.editing) {
|
|
||||||
choices.push(UserActions.Edit)
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this._options.bail) {
|
|
||||||
choices.push(UserActions.Exit)
|
|
||||||
}
|
|
||||||
|
|
||||||
const feedback =
|
|
||||||
choices.length === 1 ? UserActions.Select : await askUser(msg, choices)
|
|
||||||
|
|
||||||
metadata.feedback = {}
|
|
||||||
|
|
||||||
switch (feedback) {
|
|
||||||
case UserActions.Edit: {
|
|
||||||
const editedOutput = await editor({
|
|
||||||
message: 'Edit the output:',
|
|
||||||
default: stringified
|
|
||||||
})
|
|
||||||
metadata.feedback.editedOutput = editedOutput
|
|
||||||
break
|
|
||||||
}
|
|
||||||
|
|
||||||
case UserActions.Select: {
|
|
||||||
const choices = response.map((option) => ({
|
const choices = response.map((option) => ({
|
||||||
name: option,
|
name: option,
|
||||||
value: option
|
value: option
|
||||||
}))
|
}))
|
||||||
const chosen = await checkbox({ message: 'Select outputs:', choices })
|
return checkbox({ message: 'Select outputs:', choices })
|
||||||
metadata.feedback.selected = chosen
|
|
||||||
break
|
|
||||||
}
|
|
||||||
|
|
||||||
case UserActions.Exit:
|
|
||||||
throw new Error('Exiting...')
|
|
||||||
|
|
||||||
default:
|
|
||||||
throw new Error(`Unexpected feedback: ${feedback}`)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -82,35 +82,92 @@ export abstract class HumanFeedbackMechanism {
|
||||||
this._options = options
|
this._options = options
|
||||||
}
|
}
|
||||||
|
|
||||||
protected abstract confirm(
|
protected abstract selectOne(response: any): Promise<any>
|
||||||
response: any,
|
|
||||||
metadata: TaskResponseMetadata
|
|
||||||
): Promise<void>
|
|
||||||
protected abstract selectOne(
|
|
||||||
response: any,
|
|
||||||
metadata: TaskResponseMetadata
|
|
||||||
): Promise<void>
|
|
||||||
protected abstract selectN(
|
|
||||||
response: any,
|
|
||||||
metadata: TaskResponseMetadata
|
|
||||||
): Promise<void>
|
|
||||||
|
|
||||||
protected abstract annotate(
|
protected abstract selectN(response: any): Promise<any>
|
||||||
response: any,
|
|
||||||
metadata: TaskResponseMetadata
|
protected abstract annotate(): Promise<string>
|
||||||
): Promise<void>
|
|
||||||
|
protected abstract edit(output: string): Promise<string>
|
||||||
|
|
||||||
|
protected abstract askUser(
|
||||||
|
message: string,
|
||||||
|
choices: UserActions[]
|
||||||
|
): Promise<UserActions>
|
||||||
|
|
||||||
public async interact(response: any, metadata: TaskResponseMetadata) {
|
public async interact(response: any, metadata: TaskResponseMetadata) {
|
||||||
|
const stringified = JSON.stringify(response, null, 2)
|
||||||
|
const msg = [
|
||||||
|
'The following output was generated:',
|
||||||
|
'```',
|
||||||
|
stringified,
|
||||||
|
'```',
|
||||||
|
'What would you like to do?'
|
||||||
|
].join('\n')
|
||||||
|
|
||||||
|
const choices: UserActions[] = []
|
||||||
|
if (
|
||||||
|
this._options.type === 'selectN' ||
|
||||||
|
this._options.type === 'selectOne'
|
||||||
|
) {
|
||||||
|
choices.push(UserActions.Select)
|
||||||
|
} else {
|
||||||
|
// Case: confirm
|
||||||
|
choices.push(UserActions.Accept)
|
||||||
|
choices.push(UserActions.Decline)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this._options.editing) {
|
||||||
|
choices.push(UserActions.Edit)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this._options.bail) {
|
||||||
|
choices.push(UserActions.Exit)
|
||||||
|
}
|
||||||
|
|
||||||
|
const feedback =
|
||||||
|
choices.length === 1
|
||||||
|
? UserActions.Select
|
||||||
|
: await this.askUser(msg, choices)
|
||||||
|
|
||||||
|
metadata.feedback = {}
|
||||||
|
|
||||||
|
switch (feedback) {
|
||||||
|
case UserActions.Accept:
|
||||||
|
metadata.feedback.accepted = true
|
||||||
|
break
|
||||||
|
|
||||||
|
case UserActions.Edit: {
|
||||||
|
const editedOutput = await this.edit(stringified)
|
||||||
|
metadata.feedback.editedOutput = editedOutput
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
case UserActions.Decline:
|
||||||
|
metadata.feedback.accepted = false
|
||||||
|
break
|
||||||
|
|
||||||
|
case UserActions.Select:
|
||||||
if (this._options.type === 'selectN') {
|
if (this._options.type === 'selectN') {
|
||||||
await this.selectN(response, metadata)
|
metadata.feedback.selected = await this.selectN(response)
|
||||||
} else if (this._options.type === 'confirm') {
|
|
||||||
await this.confirm(response, metadata)
|
|
||||||
} else if (this._options.type === 'selectOne') {
|
} else if (this._options.type === 'selectOne') {
|
||||||
await this.selectOne(response, metadata)
|
metadata.feedback.chosen = await this.selectOne(response)
|
||||||
|
}
|
||||||
|
|
||||||
|
break
|
||||||
|
|
||||||
|
case UserActions.Exit:
|
||||||
|
throw new Error('Exiting...')
|
||||||
|
|
||||||
|
default:
|
||||||
|
throw new Error(`Unexpected feedback: ${feedback}`)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this._options.annotations) {
|
if (this._options.annotations) {
|
||||||
await this.annotate(response, metadata)
|
const annotation = await this.annotate()
|
||||||
|
if (annotation) {
|
||||||
|
metadata.feedback.annotation = annotation
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
import { Agentic } from '@/agentic'
|
import { Agentic } from '@/agentic'
|
||||||
import { SlackClient } from '@/services/slack'
|
import { SlackClient } from '@/services/slack'
|
||||||
import { TaskResponseMetadata } from '@/types'
|
|
||||||
|
|
||||||
import {
|
import {
|
||||||
HumanFeedbackMechanism,
|
HumanFeedbackMechanism,
|
||||||
|
@ -23,24 +22,28 @@ export class HumanFeedbackMechanismSlack extends HumanFeedbackMechanism {
|
||||||
this.slackClient = new SlackClient()
|
this.slackClient = new SlackClient()
|
||||||
}
|
}
|
||||||
|
|
||||||
protected async annotate(
|
protected async annotate(): Promise<string> {
|
||||||
response: any,
|
|
||||||
metadata: TaskResponseMetadata
|
|
||||||
): Promise<void> {
|
|
||||||
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
|
||||||
if (annotation) {
|
|
||||||
metadata.feedback.annotation = annotation.text
|
|
||||||
}
|
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
// Deliberately swallow the error here as the user is not required to leave an annotation
|
// Deliberately swallow the error here as the user is not required to leave an annotation
|
||||||
|
return ''
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private async askUser(
|
protected async edit(): Promise<string> {
|
||||||
|
let { text: editedOutput } = await this.slackClient.sendAndWaitForReply({
|
||||||
|
text: 'Copy and edit the output:'
|
||||||
|
})
|
||||||
|
editedOutput = editedOutput.replace(/```$/g, '')
|
||||||
|
editedOutput = editedOutput.replace(/^```/g, '')
|
||||||
|
return editedOutput
|
||||||
|
}
|
||||||
|
|
||||||
|
protected async askUser(
|
||||||
message: string,
|
message: string,
|
||||||
choices: UserActions[]
|
choices: UserActions[]
|
||||||
): Promise<UserActions> {
|
): Promise<UserActions> {
|
||||||
|
@ -60,108 +63,9 @@ export class HumanFeedbackMechanismSlack extends HumanFeedbackMechanism {
|
||||||
return choices[parseInt(response.text)]
|
return choices[parseInt(response.text)]
|
||||||
}
|
}
|
||||||
|
|
||||||
public async confirm(
|
public async selectOne(response: any[]): Promise<any> {
|
||||||
response: any,
|
const { text: selectedOutput } = await this.slackClient.sendAndWaitForReply(
|
||||||
metadata: TaskResponseMetadata
|
|
||||||
): Promise<void> {
|
|
||||||
const stringified = JSON.stringify(response, null, 2)
|
|
||||||
const msg = [
|
|
||||||
'The following output was generated:',
|
|
||||||
'```',
|
|
||||||
stringified,
|
|
||||||
'```',
|
|
||||||
'What would you like to do?'
|
|
||||||
].join('\n')
|
|
||||||
|
|
||||||
const choices: UserActions[] = [UserActions.Accept, UserActions.Decline]
|
|
||||||
|
|
||||||
if (this._options.editing) {
|
|
||||||
choices.push(UserActions.Edit)
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this._options.bail) {
|
|
||||||
choices.push(UserActions.Exit)
|
|
||||||
}
|
|
||||||
|
|
||||||
const feedback = await this.askUser(msg, choices)
|
|
||||||
|
|
||||||
metadata.feedback = {}
|
|
||||||
|
|
||||||
switch (feedback) {
|
|
||||||
case UserActions.Accept:
|
|
||||||
metadata.feedback.accepted = true
|
|
||||||
break
|
|
||||||
|
|
||||||
case UserActions.Edit: {
|
|
||||||
let { text: editedOutput } = await this.slackClient.sendAndWaitForReply(
|
|
||||||
{
|
{
|
||||||
text: 'Copy and edit the output:'
|
|
||||||
}
|
|
||||||
)
|
|
||||||
editedOutput = editedOutput.replace(/```$/g, '')
|
|
||||||
editedOutput = editedOutput.replace(/^```/g, '')
|
|
||||||
metadata.feedback.editedOutput = editedOutput
|
|
||||||
break
|
|
||||||
}
|
|
||||||
|
|
||||||
case UserActions.Decline:
|
|
||||||
metadata.feedback.accepted = false
|
|
||||||
break
|
|
||||||
|
|
||||||
case UserActions.Exit:
|
|
||||||
throw new Error('Exiting...')
|
|
||||||
|
|
||||||
default:
|
|
||||||
throw new Error(`Unexpected feedback: ${feedback}`)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public async selectOne(
|
|
||||||
response: any[],
|
|
||||||
metadata: TaskResponseMetadata
|
|
||||||
): Promise<void> {
|
|
||||||
const stringified = JSON.stringify(response, null, 2)
|
|
||||||
const msg = [
|
|
||||||
'The following output was generated:',
|
|
||||||
'```',
|
|
||||||
stringified,
|
|
||||||
'```',
|
|
||||||
'What would you like to do?'
|
|
||||||
].join('\n')
|
|
||||||
|
|
||||||
const choices: UserActions[] = [UserActions.Select]
|
|
||||||
|
|
||||||
if (this._options.editing) {
|
|
||||||
choices.push(UserActions.Edit)
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this._options.bail) {
|
|
||||||
choices.push(UserActions.Exit)
|
|
||||||
}
|
|
||||||
|
|
||||||
const feedback =
|
|
||||||
choices.length === 1
|
|
||||||
? UserActions.Select
|
|
||||||
: await this.askUser(msg, choices)
|
|
||||||
|
|
||||||
metadata.feedback = {}
|
|
||||||
|
|
||||||
switch (feedback) {
|
|
||||||
case UserActions.Edit: {
|
|
||||||
let { text: editedOutput } = await this.slackClient.sendAndWaitForReply(
|
|
||||||
{
|
|
||||||
text: 'Copy and edit the output:'
|
|
||||||
}
|
|
||||||
)
|
|
||||||
editedOutput = editedOutput.replace(/```$/g, '')
|
|
||||||
editedOutput = editedOutput.replace(/^```/g, '')
|
|
||||||
metadata.feedback.editedOutput = editedOutput
|
|
||||||
break
|
|
||||||
}
|
|
||||||
|
|
||||||
case UserActions.Select: {
|
|
||||||
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('') +
|
||||||
|
@ -170,62 +74,14 @@ export class HumanFeedbackMechanismSlack extends HumanFeedbackMechanism {
|
||||||
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
|
||||||
}
|
}
|
||||||
})
|
|
||||||
metadata.feedback.chosen = response[parseInt(selectedOutput)]
|
|
||||||
break
|
|
||||||
}
|
|
||||||
|
|
||||||
case UserActions.Exit:
|
|
||||||
throw new Error('Exiting...')
|
|
||||||
|
|
||||||
default:
|
|
||||||
throw new Error(`Unexpected feedback: ${feedback}`)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public async selectN(
|
|
||||||
response: any[],
|
|
||||||
metadata: TaskResponseMetadata
|
|
||||||
): Promise<void> {
|
|
||||||
const stringified = JSON.stringify(response, null, 2)
|
|
||||||
const msg = [
|
|
||||||
'The following output was generated:',
|
|
||||||
stringified,
|
|
||||||
'What would you like to do?'
|
|
||||||
].join('\n')
|
|
||||||
const choices: UserActions[] = [UserActions.Select]
|
|
||||||
|
|
||||||
if (this._options.editing) {
|
|
||||||
choices.push(UserActions.Edit)
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this._options.bail) {
|
|
||||||
choices.push(UserActions.Exit)
|
|
||||||
}
|
|
||||||
|
|
||||||
const feedback =
|
|
||||||
choices.length === 1
|
|
||||||
? UserActions.Select
|
|
||||||
: await this.askUser(msg, choices)
|
|
||||||
|
|
||||||
metadata.feedback = {}
|
|
||||||
|
|
||||||
switch (feedback) {
|
|
||||||
case UserActions.Edit: {
|
|
||||||
let { text: editedOutput } = await this.slackClient.sendAndWaitForReply(
|
|
||||||
{
|
|
||||||
text: 'Copy and edit the output:'
|
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
editedOutput = editedOutput.replace(/```$/g, '')
|
return response[parseInt(selectedOutput)]
|
||||||
editedOutput = editedOutput.replace(/^```/g, '')
|
|
||||||
metadata.feedback.editedOutput = editedOutput
|
|
||||||
break
|
|
||||||
}
|
}
|
||||||
|
|
||||||
case UserActions.Select: {
|
public async selectN(response: any[]): Promise<any[]> {
|
||||||
const { text: selectedOutput } =
|
const { text: selectedOutput } = await this.slackClient.sendAndWaitForReply(
|
||||||
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('') +
|
||||||
|
@ -235,27 +91,17 @@ export class HumanFeedbackMechanismSlack extends HumanFeedbackMechanism {
|
||||||
return choices.every((choice) => {
|
return choices.every((choice) => {
|
||||||
const choiceInt = parseInt(choice)
|
const choiceInt = parseInt(choice)
|
||||||
return (
|
return (
|
||||||
!isNaN(choiceInt) &&
|
!isNaN(choiceInt) && choiceInt >= 0 && choiceInt < response.length
|
||||||
choiceInt >= 0 &&
|
|
||||||
choiceInt < response.length
|
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
})
|
}
|
||||||
|
)
|
||||||
const chosenOutputs = selectedOutput
|
const chosenOutputs = selectedOutput
|
||||||
.split(',')
|
.split(',')
|
||||||
.map((choice) => parseInt(choice))
|
.map((choice) => parseInt(choice))
|
||||||
metadata.feedback.selected = response.filter((_, idx) => {
|
return response.filter((_, idx) => {
|
||||||
return chosenOutputs.includes(idx)
|
return chosenOutputs.includes(idx)
|
||||||
})
|
})
|
||||||
break
|
|
||||||
}
|
|
||||||
|
|
||||||
case UserActions.Exit:
|
|
||||||
throw new Error('Exiting...')
|
|
||||||
|
|
||||||
default:
|
|
||||||
throw new Error(`Unexpected feedback: ${feedback}`)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
import { Agentic } from '@/agentic'
|
import { Agentic } from '@/agentic'
|
||||||
import { TwilioConversationClient } from '@/services/twilio-conversation'
|
import { TwilioConversationClient } from '@/services/twilio-conversation'
|
||||||
import { TaskResponseMetadata } from '@/types'
|
|
||||||
|
|
||||||
import {
|
import {
|
||||||
HumanFeedbackMechanism,
|
HumanFeedbackMechanism,
|
||||||
|
@ -23,25 +22,30 @@ export class HumanFeedbackMechanismTwilio extends HumanFeedbackMechanism {
|
||||||
this.twilioClient = new TwilioConversationClient()
|
this.twilioClient = new TwilioConversationClient()
|
||||||
}
|
}
|
||||||
|
|
||||||
protected async annotate(
|
protected async annotate(): Promise<string> {
|
||||||
response: any,
|
|
||||||
metadata: TaskResponseMetadata
|
|
||||||
): Promise<void> {
|
|
||||||
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):'
|
||||||
})
|
})
|
||||||
|
return annotation.body
|
||||||
if (annotation) {
|
|
||||||
metadata.feedback.annotation = annotation.body
|
|
||||||
}
|
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
// Deliberately swallow the error here as the user is not required to leave an annotation
|
// Deliberately swallow the error here as the user is not required to leave an annotation
|
||||||
|
return ''
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private async askUser(
|
protected async edit(): Promise<string> {
|
||||||
|
let { body: editedOutput } = await this.twilioClient.sendAndWaitForReply({
|
||||||
|
text: 'Copy and edit the output:',
|
||||||
|
name: 'human-feedback-edit'
|
||||||
|
})
|
||||||
|
editedOutput = editedOutput.replace(/```$/g, '')
|
||||||
|
editedOutput = editedOutput.replace(/^```/g, '')
|
||||||
|
return editedOutput
|
||||||
|
}
|
||||||
|
|
||||||
|
protected async askUser(
|
||||||
message: string,
|
message: string,
|
||||||
choices: UserActions[]
|
choices: UserActions[]
|
||||||
): Promise<UserActions> {
|
): Promise<UserActions> {
|
||||||
|
@ -62,106 +66,7 @@ export class HumanFeedbackMechanismTwilio extends HumanFeedbackMechanism {
|
||||||
return choices[parseInt(response.body)]
|
return choices[parseInt(response.body)]
|
||||||
}
|
}
|
||||||
|
|
||||||
public async confirm(
|
public async selectOne(response: any[]): Promise<any> {
|
||||||
response: any,
|
|
||||||
metadata: TaskResponseMetadata
|
|
||||||
): Promise<void> {
|
|
||||||
const stringified = JSON.stringify(response, null, 2)
|
|
||||||
const msg = [
|
|
||||||
'The following output was generated:',
|
|
||||||
'',
|
|
||||||
stringified,
|
|
||||||
'',
|
|
||||||
'What would you like to do?'
|
|
||||||
].join('\n')
|
|
||||||
|
|
||||||
const choices: UserActions[] = [UserActions.Accept, UserActions.Decline]
|
|
||||||
|
|
||||||
if (this._options.editing) {
|
|
||||||
choices.push(UserActions.Edit)
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this._options.bail) {
|
|
||||||
choices.push(UserActions.Exit)
|
|
||||||
}
|
|
||||||
|
|
||||||
const feedback = await this.askUser(msg, choices)
|
|
||||||
|
|
||||||
metadata.feedback = {}
|
|
||||||
|
|
||||||
switch (feedback) {
|
|
||||||
case UserActions.Accept:
|
|
||||||
metadata.feedback.accepted = true
|
|
||||||
break
|
|
||||||
|
|
||||||
case UserActions.Edit: {
|
|
||||||
let { body: editedOutput } =
|
|
||||||
await this.twilioClient.sendAndWaitForReply({
|
|
||||||
name: 'human-feedback-edit',
|
|
||||||
text: 'Copy and edit the output:'
|
|
||||||
})
|
|
||||||
editedOutput = editedOutput.replace(/```$/g, '')
|
|
||||||
editedOutput = editedOutput.replace(/^```/g, '')
|
|
||||||
metadata.feedback.editedOutput = editedOutput
|
|
||||||
break
|
|
||||||
}
|
|
||||||
|
|
||||||
case UserActions.Decline:
|
|
||||||
metadata.feedback.accepted = false
|
|
||||||
break
|
|
||||||
|
|
||||||
case UserActions.Exit:
|
|
||||||
throw new Error('Exiting...')
|
|
||||||
|
|
||||||
default:
|
|
||||||
throw new Error(`Unexpected feedback: ${feedback}`)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public async selectOne(
|
|
||||||
response: any[],
|
|
||||||
metadata: TaskResponseMetadata
|
|
||||||
): Promise<void> {
|
|
||||||
const stringified = JSON.stringify(response, null, 2)
|
|
||||||
const msg = [
|
|
||||||
'The following output was generated:',
|
|
||||||
'',
|
|
||||||
stringified,
|
|
||||||
'',
|
|
||||||
'What would you like to do?'
|
|
||||||
].join('\n')
|
|
||||||
|
|
||||||
const choices: UserActions[] = [UserActions.Select]
|
|
||||||
|
|
||||||
if (this._options.editing) {
|
|
||||||
choices.push(UserActions.Edit)
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this._options.bail) {
|
|
||||||
choices.push(UserActions.Exit)
|
|
||||||
}
|
|
||||||
|
|
||||||
const feedback =
|
|
||||||
choices.length === 1
|
|
||||||
? UserActions.Select
|
|
||||||
: await this.askUser(msg, choices)
|
|
||||||
|
|
||||||
metadata.feedback = {}
|
|
||||||
|
|
||||||
switch (feedback) {
|
|
||||||
case UserActions.Edit: {
|
|
||||||
let { body: editedOutput } =
|
|
||||||
await this.twilioClient.sendAndWaitForReply({
|
|
||||||
text: 'Copy and edit the output:',
|
|
||||||
name: 'human-feedback-edit'
|
|
||||||
})
|
|
||||||
editedOutput = editedOutput.replace(/```$/g, '')
|
|
||||||
editedOutput = editedOutput.replace(/^```/g, '')
|
|
||||||
metadata.feedback.editedOutput = editedOutput
|
|
||||||
break
|
|
||||||
}
|
|
||||||
|
|
||||||
case UserActions.Select: {
|
|
||||||
const { body: selectedOutput } =
|
const { body: selectedOutput } =
|
||||||
await this.twilioClient.sendAndWaitForReply({
|
await this.twilioClient.sendAndWaitForReply({
|
||||||
name: 'human-feedback-select',
|
name: 'human-feedback-select',
|
||||||
|
@ -174,61 +79,10 @@ export class HumanFeedbackMechanismTwilio extends HumanFeedbackMechanism {
|
||||||
return !isNaN(choice) && choice >= 0 && choice < response.length
|
return !isNaN(choice) && choice >= 0 && choice < response.length
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
metadata.feedback.chosen = response[parseInt(selectedOutput)]
|
return response[parseInt(selectedOutput)]
|
||||||
break
|
|
||||||
}
|
}
|
||||||
|
|
||||||
case UserActions.Exit:
|
public async selectN(response: any[]): Promise<any[]> {
|
||||||
throw new Error('Exiting...')
|
|
||||||
|
|
||||||
default:
|
|
||||||
throw new Error(`Unexpected feedback: ${feedback}`)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public async selectN(
|
|
||||||
response: any[],
|
|
||||||
metadata: TaskResponseMetadata
|
|
||||||
): Promise<void> {
|
|
||||||
const stringified = JSON.stringify(response, null, 2)
|
|
||||||
const msg = [
|
|
||||||
'The following output was generated:',
|
|
||||||
'',
|
|
||||||
stringified,
|
|
||||||
'',
|
|
||||||
'What would you like to do?'
|
|
||||||
].join('\n')
|
|
||||||
const choices: UserActions[] = [UserActions.Select]
|
|
||||||
|
|
||||||
if (this._options.editing) {
|
|
||||||
choices.push(UserActions.Edit)
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this._options.bail) {
|
|
||||||
choices.push(UserActions.Exit)
|
|
||||||
}
|
|
||||||
|
|
||||||
const feedback =
|
|
||||||
choices.length === 1
|
|
||||||
? UserActions.Select
|
|
||||||
: await this.askUser(msg, choices)
|
|
||||||
|
|
||||||
metadata.feedback = {}
|
|
||||||
|
|
||||||
switch (feedback) {
|
|
||||||
case UserActions.Edit: {
|
|
||||||
let { body: editedOutput } =
|
|
||||||
await this.twilioClient.sendAndWaitForReply({
|
|
||||||
text: 'Copy and edit the output:',
|
|
||||||
name: 'human-feedback-edit'
|
|
||||||
})
|
|
||||||
editedOutput = editedOutput.replace(/```$/g, '')
|
|
||||||
editedOutput = editedOutput.replace(/^```/g, '')
|
|
||||||
metadata.feedback.editedOutput = editedOutput
|
|
||||||
break
|
|
||||||
}
|
|
||||||
|
|
||||||
case UserActions.Select: {
|
|
||||||
const { body: selectedOutput } =
|
const { body: selectedOutput } =
|
||||||
await this.twilioClient.sendAndWaitForReply({
|
await this.twilioClient.sendAndWaitForReply({
|
||||||
name: 'human-feedback-select',
|
name: 'human-feedback-select',
|
||||||
|
@ -241,9 +95,7 @@ export class HumanFeedbackMechanismTwilio extends HumanFeedbackMechanism {
|
||||||
return choices.every((choice) => {
|
return choices.every((choice) => {
|
||||||
const choiceInt = parseInt(choice)
|
const choiceInt = parseInt(choice)
|
||||||
return (
|
return (
|
||||||
!isNaN(choiceInt) &&
|
!isNaN(choiceInt) && choiceInt >= 0 && choiceInt < response.length
|
||||||
choiceInt >= 0 &&
|
|
||||||
choiceInt < response.length
|
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -251,17 +103,8 @@ export class HumanFeedbackMechanismTwilio extends HumanFeedbackMechanism {
|
||||||
const chosenOutputs = selectedOutput
|
const chosenOutputs = selectedOutput
|
||||||
.split(',')
|
.split(',')
|
||||||
.map((choice) => parseInt(choice))
|
.map((choice) => parseInt(choice))
|
||||||
metadata.feedback.selected = response.filter((_, idx) => {
|
return response.filter((_, idx) => {
|
||||||
return chosenOutputs.includes(idx)
|
return chosenOutputs.includes(idx)
|
||||||
})
|
})
|
||||||
break
|
|
||||||
}
|
|
||||||
|
|
||||||
case UserActions.Exit:
|
|
||||||
throw new Error('Exiting...')
|
|
||||||
|
|
||||||
default:
|
|
||||||
throw new Error(`Unexpected feedback: ${feedback}`)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Ładowanie…
Reference in New Issue