-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
19 changed files
with
579 additions
and
317 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
44 changes: 44 additions & 0 deletions
44
stack--current/5-incubator/active/view--chat/src/__fixtures/primitives--console.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,44 @@ | ||
import { type ChatPrimitives } from '../implementation/types.js' | ||
|
||
const CHAT_CONSOLE: ChatPrimitives<string> = { | ||
setup: async () => { | ||
console.log('[ChatPrimitives.setup()]') | ||
}, | ||
|
||
display_message: async ({msg, choices}) => { | ||
console.log('[ChatPrimitives.display_message()]') | ||
|
||
console.log(msg) | ||
if (choices) console.log('Choices:', choices) | ||
}, | ||
|
||
pretend_to_think: async ({duration_ms}) => { throw new Error('NO UI pretend_to_think') }, | ||
|
||
pretend_to_work: async({ | ||
msg_before, | ||
duration_ms, | ||
msg_after, | ||
}) => { | ||
console.log(msg_before) | ||
await new Promise(resolve => setTimeout(resolve, duration_ms)) | ||
console.log(msg_after) | ||
}, | ||
|
||
//read_answer: async () => { throw new Error('NO UI read_answer') }, | ||
|
||
display_task: ({ | ||
msg_before, | ||
progress_promise, | ||
msg_after, | ||
}) => { | ||
throw new Error(`NO UI display_task!`) | ||
}, | ||
|
||
spin_until_resolution: async ({promise}) => promise, | ||
|
||
teardown: async () => { | ||
console.log('[ChatPrimitives.teardown()]') | ||
}, | ||
} | ||
|
||
export default CHAT_CONSOLE |
1 change: 0 additions & 1 deletion
1
...5-incubator/active/view--chat/doc/demo.js → ...active/view--chat/src/__fixtures/todo.txt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
81 changes: 81 additions & 0 deletions
81
stack--current/5-incubator/active/view--chat/src/__fixtures/tour.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,81 @@ | ||
import { type Step, StepType } from '../types/types.js' | ||
import { type StepsGenerator } from '../loop/types.js' | ||
|
||
export default function* get_next_step(skip_to_index: number = 0) { | ||
const state = { | ||
mode: 'main', | ||
name: undefined, | ||
city: undefined, | ||
} | ||
|
||
const STEPS: Array<Step> = [ | ||
|
||
{ | ||
type: StepType.perceived_labor, | ||
|
||
msg_before: 'Waking up...', | ||
duration_ms: 2000, | ||
msg_after: 'Awoken!', | ||
}, | ||
|
||
{ | ||
type: StepType.progress, | ||
|
||
msg_before: 'Warming up...', | ||
task_promise: (new Promise((resolve, reject) => setTimeout(() => reject(new Error('Demo step 2 rejection!')), 2000))), | ||
msg_after: success => success ? '✔ Ready!' : '❌ Warm up unsuccessful.', | ||
|
||
callback: success => console.log(`[callback called: ${success}]`), | ||
}, | ||
|
||
/* | ||
{ | ||
type: 'simple_message', | ||
msg_main: 'Welcome. I’ll have a few questions…', | ||
}, | ||
{ | ||
type: 'ask_for_string', | ||
msg_main: 'What’s your name?', | ||
//validator: null, // TODO | ||
msgg_as_user: value => `My name is "${value}".`, | ||
msgg_acknowledge: name => `Thanks for the answer, ${name}!`, | ||
callback: value => { state.name = value }, | ||
}, | ||
{ | ||
type: 'ask_for_string', | ||
msg_main: 'What city do you live in?', | ||
msgg_as_user: value => `I live in "${value}".`, | ||
msgg_acknowledge: value => `${value}, a fine city indeed!`, | ||
callback: value => { state.city = value }, | ||
}, | ||
{ | ||
type: 'simple_message', | ||
msg_main: 'Please wait for a moment...', | ||
}, | ||
{ | ||
type: 'progress', | ||
duration_ms: 1000, | ||
msg_main: 'Calling server...', | ||
}, | ||
{ | ||
msg_main: 'Please choose between 1 and 2?', | ||
callback: value => { state.mode = value }, | ||
choices: [ | ||
{ | ||
msg_cta: 'Choice 1', | ||
value: 1, | ||
}, | ||
{ | ||
msg_cta: 'Choice 2', | ||
value: 2, | ||
}, | ||
], | ||
}, | ||
{ | ||
type: 'simple_message', | ||
msg_main: 'Thanks, good bye.', | ||
},*/ | ||
] | ||
|
||
yield* STEPS.slice(skip_to_index) | ||
} |
44 changes: 44 additions & 0 deletions
44
stack--current/5-incubator/active/view--chat/src/implementation/types.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,44 @@ | ||
import { Enum } from 'typescript-string-enums' | ||
import { PProgress as PromiseWithProgress } from 'p-progress' | ||
import { type Immutable } from '@offirmo-private/ts-types' | ||
|
||
///////////////////////////////////////////////// | ||
|
||
interface ChatPrimitives<ContentType> { | ||
setup(): Promise<void> | ||
|
||
// core primitives | ||
display_message(p: {msg: ContentType, choices?: ContentType[]}): Promise<void> | ||
|
||
// a staple of chat interfaces | ||
// to be used between steps | ||
pretend_to_think(p: {duration_ms: number}): Promise<void> | ||
|
||
pretend_to_work(p: { | ||
msg_before: ContentType, | ||
duration_ms: number, | ||
msg_after: ContentType, | ||
}): Promise<void> | ||
|
||
//read_answer(step) TODO clarify | ||
|
||
display_task(p: { | ||
msg_before: ContentType, | ||
progress_promise: PromiseWithProgress<any>, | ||
msg_after: ContentType, | ||
}): Promise<void> | ||
|
||
// while we wait for the next step. | ||
// wraps the promise, should return it | ||
// TODO clarify | ||
spin_until_resolution<T>(p: { promise: Promise<T> }): Promise<T> | ||
|
||
// if cleanup is needed | ||
teardown(): Promise<void> | ||
} | ||
|
||
///////////////////////////////////////////////// | ||
|
||
export { | ||
type ChatPrimitives, | ||
} |
158 changes: 2 additions & 156 deletions
158
stack--current/5-incubator/active/view--chat/src/index.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,160 +1,6 @@ | ||
import is_promise from 'is-promise' | ||
import { type Immutable } from '@offirmo-private/ts-types' | ||
|
||
import { LIB } from './consts.js' | ||
import { type Step } from './types.js' | ||
import { create_dummy_progress_promise } from './utils.js' | ||
|
||
///////////////////////////////////////////////// | ||
|
||
|
||
function is_step_input(step: Immutable<Step>): boolean { | ||
return step && step.type.startsWith('ask_') | ||
} | ||
|
||
function create({ | ||
DEBUG, | ||
gen_next_step, | ||
ui, | ||
inter_msg_delay_ms = 0, | ||
after_input_delay_ms = 0, | ||
to_prettified_str = x => x, // work with browser | ||
}) { | ||
if (DEBUG) console.log('↘ create()') | ||
|
||
|
||
async function ask_user(step) { | ||
if (DEBUG) console.log('↘ ask_user(\n', to_prettified_str(step, {outline: true}), '\n)') | ||
|
||
let answer = '' | ||
const ok = true // TODO used for confirmation | ||
do { | ||
await ui.display_message({msg: step.msg_main, choices: step.choices}) | ||
answer = await ui.read_answer(step) | ||
if (DEBUG) console.log(`↖ ask_user(…) answer = "${answer}"`) | ||
} while (!ok) | ||
await ui.pretend_to_think(after_input_delay_ms) | ||
|
||
let acknowledged = false | ||
if (step.choices.length) { | ||
const selected_choice = step.choices.find(choice => choice.value === answer) | ||
if (selected_choice.msgg_acknowledge) { | ||
await ui.display_message({msg: selected_choice.msgg_acknowledge(answer)}) | ||
acknowledged = true | ||
} | ||
} | ||
if (!acknowledged && step.msgg_acknowledge) { | ||
await ui.display_message({msg: step.msgg_acknowledge(answer)}) | ||
acknowledged = true | ||
} | ||
if (!acknowledged) { | ||
// Fine! It's optional. | ||
if (DEBUG) console.warn('You may want to add an acknowledge message to this step.') | ||
} | ||
|
||
return answer | ||
} | ||
|
||
async function execute_step(step) { | ||
if (DEBUG) console.log('↘ execute_step(\n', to_prettified_str(step, {outline: true}), '\n)') | ||
|
||
switch (step.type) { | ||
case 'simple_message': | ||
await ui.pretend_to_think(inter_msg_delay_ms) | ||
await ui.display_message({ msg: step.msg_main }) | ||
break | ||
|
||
case 'progress': | ||
await ui.display_progress({ | ||
progress_promise: step.progress_promise | ||
|| create_dummy_progress_promise({ DURATION_MS: step.duration_ms }), | ||
msg: step.msg_main, | ||
msgg_acknowledge: step.msgg_acknowledge, | ||
}) | ||
.then(() => true, () => false) | ||
.then(success => { | ||
if (step.callback) | ||
step.callback(success) | ||
}) | ||
break | ||
|
||
case 'ask_for_confirmation': | ||
case 'ask_for_string': | ||
case 'ask_for_choice': { | ||
await ui.pretend_to_think(inter_msg_delay_ms) | ||
const answer = await ask_user(step) | ||
|
||
let reported = false | ||
if (step.choices.length) { | ||
const selected_choice = step.choices.find(choice => choice.value === answer) | ||
if (selected_choice.callback) { | ||
await selected_choice.callback(answer) | ||
reported = true | ||
} | ||
} | ||
if (!reported && step.callback) { | ||
await step.callback(answer) | ||
reported = true | ||
} | ||
if (!reported) { | ||
const err = new Error('CNF reporting callback in ask for result!') | ||
err.step = step | ||
throw err | ||
} | ||
return answer | ||
} | ||
default: | ||
throw new Error(`Unsupported step type: "${step.type}"!`) | ||
} | ||
} | ||
|
||
async function start() { | ||
if (DEBUG) console.log('↘ start()') | ||
try { | ||
await ui.setup() | ||
let should_exit = false | ||
let last_step = undefined // just in case | ||
let last_answer = undefined // just in case | ||
do { | ||
const step_start_timestamp_ms = +new Date() | ||
const yielded_step = gen_next_step.next({last_step, last_answer}) | ||
|
||
// just in case the returned step is a promise. | ||
const {value: raw_step, done} = is_promise(yielded_step) | ||
? await ui.spin_until_resolution(yielded_step) | ||
: yielded_step | ||
|
||
if (done) { | ||
should_exit = true | ||
continue | ||
} | ||
|
||
const step = normalize_step(raw_step) | ||
const elapsed_time_ms = (+new Date()) - step_start_timestamp_ms | ||
if (is_step_input(last_step)) { | ||
// pretend to have processed the user answer | ||
await ui.pretend_to_think(Math.max(0, after_input_delay_ms - elapsed_time_ms)) | ||
} | ||
|
||
last_answer = await execute_step(step) | ||
last_step = step | ||
} while (!should_exit) | ||
await ui.teardown() | ||
} | ||
catch (e) { | ||
await ui.teardown() | ||
throw e | ||
} | ||
} | ||
|
||
return { | ||
start, | ||
} | ||
} | ||
|
||
///////////////////////////////////////////////// | ||
import { PProgress as PromiseWithProgress } from 'p-progress' | ||
|
||
export { | ||
// for convenience | ||
PromiseWithProgress, | ||
create, | ||
} |
12 changes: 12 additions & 0 deletions
12
stack--current/5-incubator/active/view--chat/src/loop/demo.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
import generator_func from '../__fixtures/tour.js' | ||
import primitives from '../__fixtures/primitives--console.js' | ||
|
||
import { create } from './index.js' | ||
|
||
const chat = create({ | ||
DEBUG: true, | ||
gen_next_step: generator_func() as any, | ||
primitives, | ||
}) | ||
|
||
await chat.start() |
Oops, something went wrong.