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 './openai'
|
||||||
export * from './anthropic'
|
export * from './anthropic'
|
||||||
export * from './tokenizer'
|
export * from './tokenizer'
|
||||||
export * from './feedback'
|
|
||||||
|
|
||||||
export * from './services/metaphor'
|
export * from './services/metaphor'
|
||||||
export * from './tools/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