kopia lustrzana https://github.com/transitive-bullshit/chatgpt-api
refactor: separate out abstract class and sub-classes
rodzic
e4a65cb982
commit
25d285b290
|
@ -2,10 +2,8 @@ import defaultKy from 'ky'
|
|||
|
||||
import * as types from './types'
|
||||
import { DEFAULT_OPENAI_MODEL } from './constants'
|
||||
import {
|
||||
HumanFeedbackMechanismCLI,
|
||||
HumanFeedbackOptions
|
||||
} from './human-feedback'
|
||||
import { HumanFeedbackOptions } from './human-feedback'
|
||||
import { HumanFeedbackMechanismCLI } from './human-feedback/cli'
|
||||
import { OpenAIChatCompletion } from './llms/openai'
|
||||
import { defaultLogger } from './logger'
|
||||
import { defaultIDGeneratorFn } from './utils'
|
||||
|
|
|
@ -1,55 +0,0 @@
|
|||
import * as types from './types'
|
||||
import { Agentic } from './agentic'
|
||||
import { BaseTask } from './task'
|
||||
|
||||
export type HumanFeedbackType = 'confirm' | 'selectOne' | 'selectN'
|
||||
|
||||
export type HumanFeedbackOptions = {
|
||||
type: HumanFeedbackType
|
||||
|
||||
/**
|
||||
* Whether to allow exiting
|
||||
*/
|
||||
bail?: boolean
|
||||
|
||||
editing?: boolean
|
||||
|
||||
annotations?: boolean
|
||||
|
||||
feedbackMechanism?: HumanFeedbackMechanism
|
||||
}
|
||||
|
||||
export abstract class HumanFeedbackMechanism {
|
||||
protected _agentic: Agentic
|
||||
|
||||
constructor({ agentic }: { agentic: Agentic }) {
|
||||
this._agentic = agentic
|
||||
}
|
||||
// TODO
|
||||
}
|
||||
|
||||
export class HumanFeedbackMechanismCLI extends HumanFeedbackMechanism {
|
||||
// TODO
|
||||
constructor(opts: { agentic: Agentic }) {
|
||||
super(opts)
|
||||
}
|
||||
}
|
||||
|
||||
export function withHumanFeedback<
|
||||
TInput extends void | types.JsonObject = void,
|
||||
TOutput extends types.JsonValue = string
|
||||
>(
|
||||
task: BaseTask<TInput, TOutput>,
|
||||
options: HumanFeedbackOptions = {
|
||||
type: 'confirm',
|
||||
bail: false,
|
||||
editing: false,
|
||||
annotations: false
|
||||
}
|
||||
) {
|
||||
const { feedbackMechanism = task.agentic.defaultHumanFeedbackMechamism } =
|
||||
options
|
||||
|
||||
// TODO
|
||||
return task
|
||||
}
|
|
@ -0,0 +1,226 @@
|
|||
import checkbox from '@inquirer/checkbox'
|
||||
import editor from '@inquirer/editor'
|
||||
import input from '@inquirer/input'
|
||||
import select from '@inquirer/select'
|
||||
|
||||
import { Agentic } from '@/agentic'
|
||||
import { TaskResponseMetadata } from '@/types'
|
||||
|
||||
import { HumanFeedbackMechanism, HumanFeedbackOptions } from './index'
|
||||
|
||||
/**
|
||||
* Actions the user can take in the feedback selection prompt.
|
||||
*/
|
||||
export const UserActions = {
|
||||
Accept: 'accept',
|
||||
Edit: 'edit',
|
||||
Decline: 'decline',
|
||||
Select: 'select',
|
||||
Exit: 'exit'
|
||||
} as const
|
||||
|
||||
export type UserActions = (typeof UserActions)[keyof typeof UserActions]
|
||||
|
||||
const UserActionMessages: Record<UserActions, string> = {
|
||||
[UserActions.Accept]: 'Accept the output',
|
||||
[UserActions.Edit]: 'Edit the output (open in editor)',
|
||||
[UserActions.Decline]: 'Decline the output',
|
||||
[UserActions.Select]: 'Select outputs to keep',
|
||||
[UserActions.Exit]: 'Exit'
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 {
|
||||
constructor({
|
||||
agentic,
|
||||
options
|
||||
}: {
|
||||
agentic: Agentic
|
||||
options: HumanFeedbackOptions
|
||||
}) {
|
||||
super({ agentic, options })
|
||||
this._agentic = agentic
|
||||
this._options = options
|
||||
}
|
||||
|
||||
public async confirm(
|
||||
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}`)
|
||||
}
|
||||
|
||||
if (this._options.annotations) {
|
||||
const annotation = await input({
|
||||
message:
|
||||
'Please leave an annotation (leave blank to skip; press enter to submit):'
|
||||
})
|
||||
|
||||
if (annotation) {
|
||||
metadata.feedback.annotation = annotation
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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 = 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) => ({
|
||||
name: option,
|
||||
value: option
|
||||
}))
|
||||
const chosen = await select({ message: 'Pick one output:', choices })
|
||||
metadata.feedback.chosen = chosen
|
||||
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 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) => ({
|
||||
name: option,
|
||||
value: option
|
||||
}))
|
||||
const chosen = await checkbox({ message: 'Select outputs:', choices })
|
||||
metadata.feedback.selected = chosen
|
||||
break
|
||||
}
|
||||
|
||||
case UserActions.Exit:
|
||||
throw new Error('Exiting...')
|
||||
|
||||
default:
|
||||
throw new Error(`Unexpected feedback: ${feedback}`)
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,48 +1,8 @@
|
|||
import checkbox from '@inquirer/checkbox'
|
||||
import editor from '@inquirer/editor'
|
||||
import input from '@inquirer/input'
|
||||
import select from '@inquirer/select'
|
||||
|
||||
import { Agentic } from '@/agentic'
|
||||
import { BaseTask } from '@/task'
|
||||
import { TaskResponseMetadata } from '@/types'
|
||||
|
||||
/**
|
||||
* Actions the user can take in the feedback selection prompt.
|
||||
*/
|
||||
export const UserActions = {
|
||||
Accept: 'accept',
|
||||
Edit: 'edit',
|
||||
Decline: 'decline',
|
||||
Select: 'select',
|
||||
Exit: 'exit'
|
||||
} as const
|
||||
|
||||
export type UserActions = (typeof UserActions)[keyof typeof UserActions]
|
||||
|
||||
const UserActionMessages: Record<UserActions, string> = {
|
||||
[UserActions.Accept]: 'Accept the output',
|
||||
[UserActions.Edit]: 'Edit the output (open in editor)',
|
||||
[UserActions.Decline]: 'Decline the output',
|
||||
[UserActions.Select]: 'Select outputs to keep',
|
||||
[UserActions.Exit]: 'Exit'
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
}))
|
||||
})
|
||||
}
|
||||
import { HumanFeedbackMechanismCLI } from './cli'
|
||||
|
||||
/**
|
||||
* Available types of human feedback.
|
||||
|
@ -125,185 +85,6 @@ export abstract class HumanFeedbackMechanism {
|
|||
}
|
||||
}
|
||||
|
||||
export class HumanFeedbackMechanismCLI extends HumanFeedbackMechanism {
|
||||
constructor({
|
||||
agentic,
|
||||
options
|
||||
}: {
|
||||
agentic: Agentic
|
||||
options: HumanFeedbackOptions
|
||||
}) {
|
||||
super({ agentic, options })
|
||||
this._agentic = agentic
|
||||
this._options = options
|
||||
}
|
||||
|
||||
public async confirm(
|
||||
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}`)
|
||||
}
|
||||
|
||||
if (this._options.annotations) {
|
||||
const annotation = await input({
|
||||
message:
|
||||
'Please leave an annotation (leave blank to skip; press enter to submit):'
|
||||
})
|
||||
if (annotation) {
|
||||
metadata.feedback.annotation = annotation
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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 = 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) => ({
|
||||
name: option,
|
||||
value: option
|
||||
}))
|
||||
const chosen = await select({ message: 'Pick one output:', choices })
|
||||
metadata.feedback.chosen = chosen
|
||||
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 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) => ({
|
||||
name: option,
|
||||
value: option
|
||||
}))
|
||||
const chosen = await checkbox({ message: 'Select outputs:', choices })
|
||||
metadata.feedback.selected = chosen
|
||||
break
|
||||
}
|
||||
|
||||
case UserActions.Exit:
|
||||
throw new Error('Exiting...')
|
||||
|
||||
default:
|
||||
throw new Error(`Unexpected feedback: ${feedback}`)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export function withHumanFeedback<T, U>(
|
||||
task: BaseTask<T, U>,
|
||||
options: HumanFeedbackOptions = {}
|
||||
|
|
Ładowanie…
Reference in New Issue