human-in-the-loop 0.1.0 → 2.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,137 +0,0 @@
1
- import React from 'react'
2
- import type { HumanFunction, HumanTaskRequest, EmailConfig } from '../../core/types'
3
-
4
- /**
5
- * Basic Email Template component
6
- */
7
- export function EmailTemplate({
8
- taskId,
9
- title,
10
- description,
11
- options,
12
- callbackUrl
13
- }: {
14
- taskId: string
15
- title: string
16
- description: string
17
- options?: string[] | Array<{ value: string; label: string }>
18
- callbackUrl?: string
19
- }) {
20
- return (
21
- <div>
22
- <h1>{title}</h1>
23
- <p>{description}</p>
24
-
25
- {options && options.length > 0 && (
26
- <div>
27
- <p>Please select one of the following options:</p>
28
- <ul>
29
- {options.map((option, index) => {
30
- const value = typeof option === 'string' ? option : option.value
31
- const label = typeof option === 'string' ? option : option.label
32
- const responseUrl = callbackUrl
33
- ? `${callbackUrl}?taskId=${taskId}&option=${encodeURIComponent(value)}`
34
- : '#'
35
-
36
- return (
37
- <li key={index}>
38
- <a href={responseUrl}>{label}</a>
39
- </li>
40
- )
41
- })}
42
- </ul>
43
- </div>
44
- )}
45
-
46
- <p>
47
- Or, you can reply to this email with your response.
48
- </p>
49
- </div>
50
- )
51
- }
52
-
53
- /**
54
- * Mock function to send an email
55
- */
56
- export async function sendEmail(
57
- config: EmailConfig & {
58
- title: string
59
- description: string
60
- options?: string[] | Array<{ value: string; label: string }>
61
- taskId: string
62
- }
63
- ): Promise<{ messageId: string }> {
64
- console.log(`Sending email to ${config.to}`)
65
- console.log(`Title: ${config.title}`)
66
- console.log(`Description: ${config.description}`)
67
-
68
-
69
- return { messageId: `email-${config.taskId}-${Date.now()}` }
70
- }
71
-
72
- /**
73
- * Get the response for an email task
74
- */
75
- export async function getEmailResponse<TOutput>(taskId: string): Promise<TOutput | null> {
76
- console.log(`Getting response for email task ${taskId}`)
77
-
78
- return null
79
- }
80
-
81
- /**
82
- * Implementation of HumanFunction for Email
83
- */
84
- export class EmailHumanFunction<TInput, TOutput> implements HumanFunction<TInput, TOutput> {
85
- private config: EmailConfig & {
86
- title: string
87
- description: string
88
- options?: string[] | Array<{ value: string; label: string }>
89
- }
90
-
91
- constructor(config: EmailConfig & {
92
- title: string
93
- description: string
94
- options?: string[] | Array<{ value: string; label: string }>
95
- }) {
96
- this.config = config
97
- }
98
-
99
- async request(input: TInput): Promise<HumanTaskRequest> {
100
- const taskId = `task-${Date.now()}`
101
-
102
- const { messageId } = await sendEmail({
103
- ...this.config,
104
- taskId
105
- })
106
-
107
- return {
108
- taskId,
109
- status: 'pending',
110
- messageId: {
111
- slack: '',
112
- teams: '',
113
- react: '',
114
- email: messageId
115
- }
116
- }
117
- }
118
-
119
- async getResponse(taskId: string): Promise<TOutput | null> {
120
- return getEmailResponse<TOutput>(taskId)
121
- }
122
-
123
- /**
124
- * Get a React component for this email template
125
- */
126
- getEmailComponent(taskId: string): React.ReactNode {
127
- return (
128
- <EmailTemplate
129
- taskId={taskId}
130
- title={this.config.title}
131
- description={this.config.description}
132
- options={this.config.options}
133
- callbackUrl={this.config.callbackUrl}
134
- />
135
- )
136
- }
137
- }
@@ -1,218 +0,0 @@
1
- import React, { useState } from 'react'
2
- import type { HumanFunction, HumanTaskRequest, ReactConfig } from '../../core/types'
3
-
4
- /**
5
- * Props for the HumanFeedback component
6
- */
7
- export interface HumanFeedbackProps<TInput, TOutput> {
8
- taskId: string
9
- title: string
10
- description: string
11
- input: TInput
12
- options?: string[] | Array<{ value: string; label: string }>
13
- freeText?: boolean
14
- onSubmit: (response: Partial<TOutput>) => void
15
- config?: ReactConfig
16
- }
17
-
18
- /**
19
- * Simple React component for human feedback
20
- */
21
- export function HumanFeedback<TInput, TOutput>({
22
- taskId,
23
- title,
24
- description,
25
- input,
26
- options,
27
- freeText,
28
- onSubmit,
29
- config
30
- }: HumanFeedbackProps<TInput, TOutput>) {
31
- const [response, setResponse] = useState<string>('')
32
- const [selectedOption, setSelectedOption] = useState<string>('')
33
-
34
- const handleSubmit = (e: React.FormEvent) => {
35
- e.preventDefault()
36
-
37
- const responseObj: Record<string, any> = {}
38
-
39
- if (selectedOption) {
40
- responseObj.selectedOption = selectedOption
41
- }
42
-
43
- if (response) {
44
- responseObj.freeText = response
45
- }
46
-
47
- onSubmit(responseObj as Partial<TOutput>)
48
- }
49
-
50
- const styles = {
51
- container: {
52
- maxWidth: '500px',
53
- margin: '0 auto',
54
- padding: '20px',
55
- borderRadius: '8px',
56
- boxShadow: '0 4px 6px rgba(0, 0, 0, 0.1)',
57
- backgroundColor: config?.theme === 'dark' ? '#1a1a1a' : '#fff',
58
- color: config?.theme === 'dark' ? '#fff' : '#333',
59
- ...config?.styles?.container
60
- },
61
- title: {
62
- fontSize: '20px',
63
- fontWeight: 'bold',
64
- marginBottom: '10px',
65
- ...config?.styles?.title
66
- },
67
- description: {
68
- marginBottom: '20px',
69
- ...config?.styles?.description
70
- },
71
- optionsContainer: {
72
- marginBottom: '20px',
73
- ...config?.styles?.optionsContainer
74
- },
75
- option: {
76
- display: 'block',
77
- margin: '8px 0',
78
- ...config?.styles?.option
79
- },
80
- textarea: {
81
- width: '100%',
82
- padding: '10px',
83
- borderRadius: '4px',
84
- border: '1px solid #ccc',
85
- minHeight: '100px',
86
- marginBottom: '20px',
87
- backgroundColor: config?.theme === 'dark' ? '#333' : '#fff',
88
- color: config?.theme === 'dark' ? '#fff' : '#333',
89
- ...config?.styles?.textarea
90
- },
91
- button: {
92
- padding: '10px 20px',
93
- backgroundColor: '#0070f3',
94
- color: 'white',
95
- border: 'none',
96
- borderRadius: '4px',
97
- cursor: 'pointer',
98
- ...config?.styles?.button
99
- }
100
- }
101
-
102
- return (
103
- <div style={styles.container}>
104
- <h2 style={styles.title}>{title}</h2>
105
- <p style={styles.description}>{description}</p>
106
-
107
- <form onSubmit={handleSubmit}>
108
- {options && options.length > 0 && (
109
- <div style={styles.optionsContainer}>
110
- {options.map((option, index) => {
111
- const value = typeof option === 'string' ? option : option.value
112
- const label = typeof option === 'string' ? option : option.label
113
-
114
- return (
115
- <label key={index} style={styles.option}>
116
- <input
117
- type="radio"
118
- name="option"
119
- value={value}
120
- checked={selectedOption === value}
121
- onChange={() => setSelectedOption(value)}
122
- />
123
- {' '}{label}
124
- </label>
125
- )
126
- })}
127
- </div>
128
- )}
129
-
130
- {freeText && (
131
- <textarea
132
- style={styles.textarea}
133
- value={response}
134
- onChange={(e) => setResponse(e.target.value)}
135
- placeholder="Enter your response..."
136
- />
137
- )}
138
-
139
- <button type="submit" style={styles.button}>Submit</button>
140
- </form>
141
- </div>
142
- )
143
- }
144
-
145
- /**
146
- * Storage for in-memory responses (in a real implementation, this would be a database)
147
- */
148
- const responses = new Map<string, any>()
149
- const tasks = new Map<string, { status: 'pending' | 'completed' | 'timeout' }>()
150
-
151
- /**
152
- * Implementation of HumanFunction for React
153
- */
154
- export class ReactHumanFunction<TInput, TOutput> implements HumanFunction<TInput, TOutput> {
155
- private config: ReactConfig & {
156
- title: string,
157
- description: string,
158
- options?: string[] | Array<{ value: string; label: string }>,
159
- freeText?: boolean
160
- }
161
-
162
- constructor(config: ReactConfig & {
163
- title: string,
164
- description: string,
165
- options?: string[] | Array<{ value: string; label: string }>,
166
- freeText?: boolean
167
- }) {
168
- this.config = config
169
- }
170
-
171
- async request(input: TInput): Promise<HumanTaskRequest> {
172
- const taskId = `task-${Date.now()}`
173
-
174
- tasks.set(taskId, { status: 'pending' })
175
-
176
- return {
177
- taskId,
178
- status: 'pending',
179
- messageId: {
180
- slack: '',
181
- teams: '',
182
- react: taskId,
183
- email: ''
184
- }
185
- }
186
- }
187
-
188
- async getResponse(taskId: string): Promise<TOutput | null> {
189
- const response = responses.get(taskId)
190
- return response || null
191
- }
192
-
193
- /**
194
- * Handle a response submission
195
- */
196
- handleResponse(taskId: string, response: TOutput): void {
197
- responses.set(taskId, response)
198
- tasks.set(taskId, { status: 'completed' })
199
- }
200
-
201
- /**
202
- * Get a React component for this human function
203
- */
204
- getComponent(taskId: string, input: TInput): React.ReactNode {
205
- return (
206
- <HumanFeedback<TInput, TOutput>
207
- taskId={taskId}
208
- title={this.config.title}
209
- description={this.config.description}
210
- input={input}
211
- options={this.config.options}
212
- freeText={this.config.freeText}
213
- config={this.config}
214
- onSubmit={(response) => this.handleResponse(taskId, response as TOutput)}
215
- />
216
- )
217
- }
218
- }
@@ -1,84 +0,0 @@
1
- import type { HumanFunction, HumanTaskRequest, SlackConfig } from '../../core/types'
2
-
3
- /**
4
- * Slack-specific response option
5
- */
6
- export interface SlackResponseOption {
7
- value: string
8
- label: string
9
- }
10
-
11
- /**
12
- * Create a Slack message with the given options
13
- */
14
- export async function createSlackMessage<TInput, TOutput>(
15
- taskId: string,
16
- input: TInput,
17
- config: SlackConfig & {
18
- title: string,
19
- description: string,
20
- options?: string[] | SlackResponseOption[],
21
- freeText?: boolean
22
- }
23
- ): Promise<{ messageId: string }> {
24
- console.log(`Creating Slack message for task ${taskId}`)
25
- console.log(`Title: ${config.title}`)
26
- console.log(`Description: ${config.description}`)
27
-
28
-
29
- return { messageId: `slack-${taskId}-${Date.now()}` }
30
- }
31
-
32
- /**
33
- * Get the response for a Slack task
34
- */
35
- export async function getSlackResponse<TOutput>(taskId: string): Promise<TOutput | null> {
36
- console.log(`Getting response for Slack task ${taskId}`)
37
-
38
- return null
39
- }
40
-
41
- /**
42
- * Implementation of HumanFunction for Slack
43
- */
44
- export class SlackHumanFunction<TInput, TOutput> implements HumanFunction<TInput, TOutput> {
45
- private config: SlackConfig & {
46
- title: string,
47
- description: string,
48
- options?: string[] | SlackResponseOption[],
49
- freeText?: boolean
50
- }
51
-
52
- constructor(config: SlackConfig & {
53
- title: string,
54
- description: string,
55
- options?: string[] | SlackResponseOption[],
56
- freeText?: boolean
57
- }) {
58
- this.config = config
59
- }
60
-
61
- async request(input: TInput): Promise<HumanTaskRequest> {
62
- const taskId = `task-${Date.now()}`
63
- const { messageId } = await createSlackMessage<TInput, TOutput>(
64
- taskId,
65
- input,
66
- this.config
67
- )
68
-
69
- return {
70
- taskId,
71
- status: 'pending',
72
- messageId: {
73
- slack: messageId,
74
- teams: '',
75
- react: '',
76
- email: ''
77
- }
78
- }
79
- }
80
-
81
- async getResponse(taskId: string): Promise<TOutput | null> {
82
- return getSlackResponse<TOutput>(taskId)
83
- }
84
- }
@@ -1,84 +0,0 @@
1
- import type { HumanFunction, HumanTaskRequest, TeamsConfig } from '../../core/types'
2
-
3
- /**
4
- * Teams-specific response option
5
- */
6
- export interface TeamsResponseOption {
7
- value: string
8
- label: string
9
- }
10
-
11
- /**
12
- * Create a Teams message with the given options
13
- */
14
- export async function createTeamsMessage<TInput, TOutput>(
15
- taskId: string,
16
- input: TInput,
17
- config: TeamsConfig & {
18
- title: string,
19
- description: string,
20
- options?: string[] | TeamsResponseOption[],
21
- freeText?: boolean
22
- }
23
- ): Promise<{ messageId: string }> {
24
- console.log(`Creating Teams message for task ${taskId}`)
25
- console.log(`Title: ${config.title}`)
26
- console.log(`Description: ${config.description}`)
27
-
28
-
29
- return { messageId: `teams-${taskId}-${Date.now()}` }
30
- }
31
-
32
- /**
33
- * Get the response for a Teams task
34
- */
35
- export async function getTeamsResponse<TOutput>(taskId: string): Promise<TOutput | null> {
36
- console.log(`Getting response for Teams task ${taskId}`)
37
-
38
- return null
39
- }
40
-
41
- /**
42
- * Implementation of HumanFunction for Microsoft Teams
43
- */
44
- export class TeamsHumanFunction<TInput, TOutput> implements HumanFunction<TInput, TOutput> {
45
- private config: TeamsConfig & {
46
- title: string,
47
- description: string,
48
- options?: string[] | TeamsResponseOption[],
49
- freeText?: boolean
50
- }
51
-
52
- constructor(config: TeamsConfig & {
53
- title: string,
54
- description: string,
55
- options?: string[] | TeamsResponseOption[],
56
- freeText?: boolean
57
- }) {
58
- this.config = config
59
- }
60
-
61
- async request(input: TInput): Promise<HumanTaskRequest> {
62
- const taskId = `task-${Date.now()}`
63
- const { messageId } = await createTeamsMessage<TInput, TOutput>(
64
- taskId,
65
- input,
66
- this.config
67
- )
68
-
69
- return {
70
- taskId,
71
- status: 'pending',
72
- messageId: {
73
- slack: '',
74
- teams: messageId,
75
- react: '',
76
- email: ''
77
- }
78
- }
79
- }
80
-
81
- async getResponse(taskId: string): Promise<TOutput | null> {
82
- return getTeamsResponse<TOutput>(taskId)
83
- }
84
- }
package/vitest.config.ts DELETED
@@ -1,15 +0,0 @@
1
- import { defineConfig } from 'vitest/config'
2
- import { resolve } from 'path'
3
-
4
- export default defineConfig({
5
- test: {
6
- environment: 'node',
7
- include: ['**/*.test.ts', '**/*.test.tsx'],
8
- globals: true
9
- },
10
- resolve: {
11
- alias: {
12
- '@': resolve(__dirname, './src')
13
- }
14
- }
15
- })