kopia lustrzana https://github.com/transitive-bullshit/chatgpt-api
feat: add human feedback tool
rodzic
bf4e52499b
commit
294011e410
|
@ -1,46 +0,0 @@
|
|||
import inquirer from 'inquirer'
|
||||
|
||||
class HumanFeedback {
|
||||
private completions: string[]
|
||||
private selectedCompletion: string
|
||||
|
||||
// Process the completions returned by the AI
|
||||
process(completions: string[]) {
|
||||
this.completions = completions
|
||||
return this
|
||||
}
|
||||
|
||||
// Request feedback from the user
|
||||
async requestFeedback() {
|
||||
const choices = this.completions.map((completion, index) => ({
|
||||
name: completion,
|
||||
value: index
|
||||
}))
|
||||
|
||||
const feedback = await inquirer.prompt([
|
||||
{
|
||||
type: 'list',
|
||||
name: 'userResponse',
|
||||
message: 'Pick the best completion:',
|
||||
choices: [...choices, new inquirer.Separator(), 'Retry', 'Decline']
|
||||
}
|
||||
])
|
||||
|
||||
switch (feedback.userResponse) {
|
||||
case 'Retry':
|
||||
return true
|
||||
case 'Decline':
|
||||
return process.exit(0)
|
||||
default:
|
||||
this.selectedCompletion = this.completions[feedback.userResponse]
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
// Return the selected completion
|
||||
getResult() {
|
||||
return this.selectedCompletion
|
||||
}
|
||||
}
|
||||
|
||||
export { HumanFeedback }
|
|
@ -4,7 +4,7 @@ export * from './llm'
|
|||
export * from './openai'
|
||||
export * from './anthropic'
|
||||
export * from './tokenizer'
|
||||
export * from './feedback'
|
||||
|
||||
export * from './services/metaphor'
|
||||
export * from './tools/metaphor'
|
||||
export * from './tools/feedback'
|
||||
|
|
|
@ -0,0 +1,210 @@
|
|||
import { checkbox, editor, select } from '@inquirer/prompts'
|
||||
import { ZodTypeAny, z } from 'zod'
|
||||
|
||||
import * as types from './../types'
|
||||
import { BaseTaskCallBuilder } from './../task'
|
||||
|
||||
enum UserActions {
|
||||
Accept = 'accept',
|
||||
Edit = 'edit',
|
||||
Decline = 'decline',
|
||||
Select = 'select'
|
||||
}
|
||||
|
||||
const UserActionMessages = {
|
||||
[UserActions.Accept]: 'Accept inputs',
|
||||
[UserActions.Edit]: 'Edit (open in editor)',
|
||||
[UserActions.Decline]: 'Decline',
|
||||
[UserActions.Select]: 'Select inputs to keep'
|
||||
}
|
||||
|
||||
export const FeedbackSingleInputSchema = <T extends ZodTypeAny>(choice: T) =>
|
||||
z.object({
|
||||
choice
|
||||
})
|
||||
|
||||
export const FeedbackSingleOutputSchema = <T extends ZodTypeAny>(result: T) =>
|
||||
z.object({
|
||||
result: result,
|
||||
accepted: z.boolean()
|
||||
})
|
||||
|
||||
export class HumanFeedbackSingle<
|
||||
T extends ZodTypeAny = ZodTypeAny
|
||||
> extends BaseTaskCallBuilder<ZodTypeAny, ZodTypeAny> {
|
||||
private choiceSchema: T
|
||||
|
||||
constructor(choiceSchema: T) {
|
||||
super()
|
||||
this.choiceSchema = choiceSchema
|
||||
}
|
||||
|
||||
public get inputSchema() {
|
||||
return FeedbackSingleInputSchema(this.choiceSchema)
|
||||
}
|
||||
|
||||
public get outputSchema() {
|
||||
return FeedbackSingleOutputSchema(this.choiceSchema)
|
||||
}
|
||||
|
||||
private async handleChoice(
|
||||
input: types.ParsedData<typeof this.inputSchema>
|
||||
): Promise<types.ParsedData<typeof this.outputSchema>> {
|
||||
const feedback = await select({
|
||||
message: [
|
||||
'The following input was generated:',
|
||||
JSON.stringify(input.choice, null, 2),
|
||||
'What would you like to do?'
|
||||
].join('\n'),
|
||||
choices: [
|
||||
{
|
||||
name: UserActionMessages[UserActions.Accept],
|
||||
value: UserActions.Accept
|
||||
},
|
||||
{
|
||||
name: UserActionMessages[UserActions.Edit],
|
||||
value: UserActions.Edit
|
||||
},
|
||||
{
|
||||
name: UserActionMessages[UserActions.Decline],
|
||||
value: UserActions.Decline
|
||||
}
|
||||
]
|
||||
})
|
||||
switch (feedback) {
|
||||
case UserActions.Edit: {
|
||||
// Open the completion in the user's default editor
|
||||
const editedInput = await editor({
|
||||
message: 'Edit the input:',
|
||||
default: JSON.stringify(input.choice)
|
||||
})
|
||||
return {
|
||||
result: this.choiceSchema.parse(JSON.parse(editedInput)),
|
||||
accepted: true
|
||||
}
|
||||
}
|
||||
case UserActions.Decline:
|
||||
return { result: null, accepted: false }
|
||||
case UserActions.Accept:
|
||||
return { result: input, accepted: true }
|
||||
default:
|
||||
throw new Error('Invalid feedback choice')
|
||||
}
|
||||
}
|
||||
|
||||
public async call(
|
||||
input: types.ParsedData<typeof this.inputSchema>
|
||||
): Promise<types.ParsedData<typeof this.outputSchema>> {
|
||||
try {
|
||||
input = this.inputSchema.parse(input)
|
||||
return this.handleChoice(input)
|
||||
} catch (err) {
|
||||
console.error('Error parsing input:', err)
|
||||
throw err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export const FeedbackSelectInputSchema = <T extends ZodTypeAny>(choice: T) =>
|
||||
z.object({
|
||||
choices: z.array(choice)
|
||||
})
|
||||
|
||||
export const FeedbackSelectOutputSchema = <T extends ZodTypeAny>(result: T) =>
|
||||
z.object({
|
||||
results: z.array(result),
|
||||
accepted: z.boolean()
|
||||
})
|
||||
|
||||
export class HumanFeedbackSelect<
|
||||
T extends ZodTypeAny = ZodTypeAny
|
||||
> extends BaseTaskCallBuilder<ZodTypeAny, ZodTypeAny> {
|
||||
private choiceSchema: T
|
||||
|
||||
constructor(choiceSchema: T) {
|
||||
super()
|
||||
this.choiceSchema = choiceSchema
|
||||
}
|
||||
|
||||
public get inputSchema() {
|
||||
return FeedbackSelectInputSchema(this.choiceSchema)
|
||||
}
|
||||
|
||||
public get outputSchema() {
|
||||
return FeedbackSelectOutputSchema(this.choiceSchema)
|
||||
}
|
||||
|
||||
private async handleChoices(
|
||||
input: types.ParsedData<typeof this.inputSchema>
|
||||
): Promise<types.ParsedData<typeof this.outputSchema>> {
|
||||
// Case: input is an array of strings
|
||||
const feedback = await select({
|
||||
message: [
|
||||
'The following inputs were generated:',
|
||||
...input.choices.map(
|
||||
(choice, index) => `${index + 1}. ${JSON.stringify(choice, null, 2)}`
|
||||
),
|
||||
'What would you like to do?'
|
||||
].join('\n'),
|
||||
choices: [
|
||||
{
|
||||
name: UserActionMessages[UserActions.Accept],
|
||||
value: UserActions.Accept
|
||||
},
|
||||
{
|
||||
name: UserActionMessages[UserActions.Edit],
|
||||
value: UserActions.Edit
|
||||
},
|
||||
{
|
||||
name: UserActionMessages[UserActions.Decline],
|
||||
value: UserActions.Decline
|
||||
},
|
||||
{
|
||||
name: UserActionMessages[UserActions.Select],
|
||||
value: UserActions.Select
|
||||
}
|
||||
]
|
||||
})
|
||||
switch (feedback) {
|
||||
case UserActions.Edit: {
|
||||
const edited = await editor({
|
||||
message: 'Edit the input:',
|
||||
default: JSON.stringify(input.choices, null, 2)
|
||||
})
|
||||
return { results: JSON.parse(edited), accepted: true }
|
||||
}
|
||||
case UserActions.Select: {
|
||||
const choices = input.choices.map((completion) => ({
|
||||
name: completion,
|
||||
value: completion
|
||||
}))
|
||||
const chosen = await checkbox({
|
||||
message: 'Pick items to keep:',
|
||||
choices: [...choices]
|
||||
})
|
||||
if (chosen.length === 0) {
|
||||
return { results: [], accepted: false }
|
||||
}
|
||||
return { results: chosen, accepted: true }
|
||||
}
|
||||
case UserActions.Decline:
|
||||
return { results: [], accepted: false }
|
||||
case UserActions.Accept:
|
||||
return { results: input.choices, accepted: true }
|
||||
default:
|
||||
throw new Error('Invalid feedback choice')
|
||||
}
|
||||
}
|
||||
|
||||
public async call(
|
||||
input: types.ParsedData<typeof this.inputSchema>
|
||||
): Promise<types.ParsedData<typeof this.outputSchema>> {
|
||||
try {
|
||||
input = this.inputSchema.parse(input)
|
||||
return this.handleChoices(input)
|
||||
} catch (err) {
|
||||
console.error('Error parsing input:', err)
|
||||
throw err
|
||||
}
|
||||
}
|
||||
}
|
Ładowanie…
Reference in New Issue