fix: improve typing

Philipp Burckhardt 2023-06-13 18:14:12 -04:00 zatwierdzone przez Travis Fischer
rodzic a84b7b191c
commit e722c5b9d7
4 zmienionych plików z 98 dodań i 46 usunięć

Wyświetl plik

@ -3,28 +3,16 @@ import editor from '@inquirer/editor'
import input from '@inquirer/input' import input from '@inquirer/input'
import select from '@inquirer/select' import select from '@inquirer/select'
import { Agentic } from '@/agentic'
import { import {
HumanFeedbackMechanism, HumanFeedbackMechanism,
HumanFeedbackOptions, HumanFeedbackType,
UserActionMessages, UserActionMessages,
UserActions UserActions
} from './feedback' } from './feedback'
export class HumanFeedbackMechanismCLI extends HumanFeedbackMechanism { export class HumanFeedbackMechanismCLI<
constructor({ T extends HumanFeedbackType
agentic, > extends HumanFeedbackMechanism<T> {
options
}: {
agentic: Agentic
options: HumanFeedbackOptions
}) {
super({ agentic, options })
this._agentic = agentic
this._options = options
}
/** /**
* Prompt the user to select one of a list of options. * Prompt the user to select one of a list of options.
*/ */

Wyświetl plik

@ -1,6 +1,5 @@
import { Agentic } from '@/agentic' import { Agentic } from '@/agentic'
import { BaseTask } from '@/task' import { BaseTask } from '@/task'
import { TaskResponseMetadata } from '@/types'
import { HumanFeedbackMechanismCLI } from './cli' import { HumanFeedbackMechanismCLI } from './cli'
@ -30,20 +29,18 @@ export const UserActionMessages: Record<UserActions, string> = {
*/ */
export type HumanFeedbackType = 'confirm' | 'selectOne' | 'selectN' export type HumanFeedbackType = 'confirm' | 'selectOne' | 'selectN'
type HumanFeedbackMechanismConstructor<T extends HumanFeedbackMechanism> = new ( type HumanFeedbackMechanismConstructor<T extends HumanFeedbackType> = new (
...args: any[] ...args: any[]
) => T ) => HumanFeedbackMechanism<T>
/** /**
* Options for human feedback. * Options for human feedback.
*/ */
export type HumanFeedbackOptions< export type HumanFeedbackOptions<T extends HumanFeedbackType> = {
T extends HumanFeedbackMechanism = HumanFeedbackMechanism
> = {
/** /**
* What type of feedback to request. * What type of feedback to request.
*/ */
type?: HumanFeedbackType type?: T
/** /**
* Whether the user can bail out of the feedback loop. * Whether the user can bail out of the feedback loop.
@ -66,17 +63,75 @@ export type HumanFeedbackOptions<
mechanism?: HumanFeedbackMechanismConstructor<T> mechanism?: HumanFeedbackMechanismConstructor<T>
} }
export abstract class HumanFeedbackMechanism { export interface BaseHumanFeedbackMetadata {
/**
* Edited output by the user (if applicable).
*/
editedOutput?: string
/**
* Annotation left by the user (if applicable).
*/
annotation?: string
}
export interface HumanFeedbackConfirmMetadata
extends BaseHumanFeedbackMetadata {
/**
* The type of feedback requested.
*/
type: 'confirm'
/**
* Whether the user accepted the output.
*/
accepted: boolean
}
export interface HumanFeedbackSelectOneMetadata
extends BaseHumanFeedbackMetadata {
/**
* The type of feedback requested.
*/
type: 'selectOne'
/**
* The selected output.
*/
chosen: any
}
export interface HumanFeedbackSelectNMetadata
extends BaseHumanFeedbackMetadata {
/**
* The type of feedback requested.
*/
type: 'selectN'
/**
* The selected outputs.
*/
selected: any[]
}
export type FeedbackTypeToMetadata<T extends HumanFeedbackType> =
T extends 'confirm'
? HumanFeedbackConfirmMetadata
: T extends 'selectOne'
? HumanFeedbackSelectOneMetadata
: HumanFeedbackSelectNMetadata
export abstract class HumanFeedbackMechanism<T extends HumanFeedbackType> {
protected _agentic: Agentic protected _agentic: Agentic
protected _options: HumanFeedbackOptions protected _options: Required<HumanFeedbackOptions<T>>
constructor({ constructor({
agentic, agentic,
options options
}: { }: {
agentic: Agentic agentic: Agentic
options: HumanFeedbackOptions options: Required<HumanFeedbackOptions<T>>
}) { }) {
this._agentic = agentic this._agentic = agentic
this._options = options this._options = options
@ -95,7 +150,7 @@ export abstract class HumanFeedbackMechanism {
choices: UserActions[] choices: UserActions[]
): Promise<UserActions> ): Promise<UserActions>
public async interact(response: any, metadata: TaskResponseMetadata) { public async interact(response: any): Promise<FeedbackTypeToMetadata<T>> {
const stringified = JSON.stringify(response, null, 2) const stringified = JSON.stringify(response, null, 2)
const msg = [ const msg = [
'The following output was generated:', 'The following output was generated:',
@ -125,33 +180,33 @@ export abstract class HumanFeedbackMechanism {
choices.push(UserActions.Exit) choices.push(UserActions.Exit)
} }
const feedback = const choice =
choices.length === 1 choices.length === 1
? UserActions.Select ? UserActions.Select
: await this.askUser(msg, choices) : await this.askUser(msg, choices)
metadata.feedback = {} const feedback: Record<string, any> = {}
switch (feedback) { switch (choice) {
case UserActions.Accept: case UserActions.Accept:
metadata.feedback.accepted = true feedback.accepted = true
break break
case UserActions.Edit: { case UserActions.Edit: {
const editedOutput = await this.edit(stringified) const editedOutput = await this.edit(stringified)
metadata.feedback.editedOutput = editedOutput feedback.editedOutput = editedOutput
break break
} }
case UserActions.Decline: case UserActions.Decline:
metadata.feedback.accepted = false feedback.accepted = false
break break
case UserActions.Select: case UserActions.Select:
if (this._options.type === 'selectN') { if (this._options.type === 'selectN') {
metadata.feedback.selected = await this.selectN(response) feedback.selected = await this.selectN(response)
} else if (this._options.type === 'selectOne') { } else if (this._options.type === 'selectOne') {
metadata.feedback.chosen = await this.selectOne(response) feedback.chosen = await this.selectOne(response)
} }
break break
@ -160,21 +215,23 @@ export abstract class HumanFeedbackMechanism {
throw new Error('Exiting...') throw new Error('Exiting...')
default: default:
throw new Error(`Unexpected feedback: ${feedback}`) 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) {
metadata.feedback.annotation = annotation feedback.annotation = annotation
} }
} }
return feedback as FeedbackTypeToMetadata<T>
} }
} }
export function withHumanFeedback<T, U>( export function withHumanFeedback<T, U, V extends HumanFeedbackType>(
task: BaseTask<T, U>, task: BaseTask<T, U>,
options: HumanFeedbackOptions = {} options: HumanFeedbackOptions<V> = {}
) { ) {
task = task.clone() task = task.clone()
@ -182,7 +239,7 @@ export function withHumanFeedback<T, U>(
const instanceDefaults = task.agentic.humanFeedbackDefaults const instanceDefaults = task.agentic.humanFeedbackDefaults
// Use Object.assign to merge the options, instance defaults, and hard-coded defaults // Use Object.assign to merge the options, instance defaults, and hard-coded defaults
const finalOptions: HumanFeedbackOptions = Object.assign( const finalOptions: HumanFeedbackOptions<V> = Object.assign(
{ {
type: 'confirm', type: 'confirm',
bail: false, bail: false,
@ -212,8 +269,9 @@ export function withHumanFeedback<T, U>(
task.callWithMetadata = async function (input?: T) { task.callWithMetadata = async function (input?: T) {
const response = await originalCall(input) const response = await originalCall(input)
// Process the response and add feedback to metadata const feedback = await feedbackMechanism.interact(response.result)
await feedbackMechanism.interact(response.result, response.metadata)
response.metadata = { ...response.metadata, feedback }
return response return response
} }

Wyświetl plik

@ -4,11 +4,14 @@ import { SlackClient } from '@/services/slack'
import { import {
HumanFeedbackMechanism, HumanFeedbackMechanism,
HumanFeedbackOptions, HumanFeedbackOptions,
HumanFeedbackType,
UserActionMessages, UserActionMessages,
UserActions UserActions
} from './feedback' } from './feedback'
export class HumanFeedbackMechanismSlack extends HumanFeedbackMechanism { export class HumanFeedbackMechanismSlack<
T extends HumanFeedbackType
> extends HumanFeedbackMechanism<T> {
private slackClient: SlackClient private slackClient: SlackClient
constructor({ constructor({
@ -16,7 +19,7 @@ export class HumanFeedbackMechanismSlack extends HumanFeedbackMechanism {
options options
}: { }: {
agentic: Agentic agentic: Agentic
options: HumanFeedbackOptions options: Required<HumanFeedbackOptions<T>>
}) { }) {
super({ agentic, options }) super({ agentic, options })
this.slackClient = new SlackClient() this.slackClient = new SlackClient()

Wyświetl plik

@ -4,11 +4,14 @@ import { TwilioConversationClient } from '@/services/twilio-conversation'
import { import {
HumanFeedbackMechanism, HumanFeedbackMechanism,
HumanFeedbackOptions, HumanFeedbackOptions,
HumanFeedbackType,
UserActionMessages, UserActionMessages,
UserActions UserActions
} from './feedback' } from './feedback'
export class HumanFeedbackMechanismTwilio extends HumanFeedbackMechanism { export class HumanFeedbackMechanismTwilio<
T extends HumanFeedbackType
> extends HumanFeedbackMechanism<T> {
private twilioClient: TwilioConversationClient private twilioClient: TwilioConversationClient
constructor({ constructor({
@ -16,7 +19,7 @@ export class HumanFeedbackMechanismTwilio extends HumanFeedbackMechanism {
options options
}: { }: {
agentic: Agentic agentic: Agentic
options: HumanFeedbackOptions options: Required<HumanFeedbackOptions<T>>
}) { }) {
super({ agentic, options }) super({ agentic, options })
this.twilioClient = new TwilioConversationClient() this.twilioClient = new TwilioConversationClient()